2020-08-28 14:02:51 +02:00
|
|
|
/*
|
|
|
|
* Copyright 2020 The Netty Project
|
|
|
|
*
|
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
|
|
|
*
|
2020-10-28 14:38:14 +01:00
|
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
2020-08-28 14:02:51 +02:00
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*/
|
2021-05-26 17:13:29 +02:00
|
|
|
package io.netty.buffer.api.internal;
|
|
|
|
|
|
|
|
import io.netty.buffer.api.Drop;
|
|
|
|
import io.netty.buffer.api.Owned;
|
|
|
|
import io.netty.buffer.api.Resource;
|
|
|
|
import io.netty.buffer.api.Send;
|
2020-08-24 16:34:32 +02:00
|
|
|
|
2020-12-08 19:25:53 +01:00
|
|
|
import java.util.Objects;
|
|
|
|
|
2021-05-26 17:13:29 +02:00
|
|
|
/**
|
|
|
|
* Internal support class for resources.
|
|
|
|
*
|
|
|
|
* @param <I> The public interface for the resource.
|
|
|
|
* @param <T> The concrete implementation of the resource.
|
|
|
|
*/
|
|
|
|
public abstract class ResourceSupport<I extends Resource<I>, T extends ResourceSupport<I, T>> implements Resource<I> {
|
2020-08-24 16:34:32 +02:00
|
|
|
private int acquires; // Closed if negative.
|
2020-12-08 19:25:53 +01:00
|
|
|
private Drop<T> drop;
|
2021-03-05 16:32:10 +01:00
|
|
|
private final LifecycleTracer tracer;
|
2020-08-24 16:34:32 +02:00
|
|
|
|
2021-05-26 17:13:29 +02:00
|
|
|
protected ResourceSupport(Drop<T> drop) {
|
2020-08-24 16:34:32 +02:00
|
|
|
this.drop = drop;
|
2021-03-05 16:32:10 +01:00
|
|
|
tracer = LifecycleTracer.get();
|
2020-08-24 16:34:32 +02:00
|
|
|
}
|
|
|
|
|
2021-05-28 16:23:35 +02:00
|
|
|
/**
|
|
|
|
* Encapsulation bypass for calling {@link #acquire()} on the given object.
|
|
|
|
* <p>
|
|
|
|
* Note: this {@code acquire} method does not check the type of the return value from acquire at compile time.
|
|
|
|
* The type is instead checked at runtime, and will cause a {@link ClassCastException} to be thrown if done
|
|
|
|
* incorrectly.
|
|
|
|
*
|
|
|
|
* @param obj The object we wish to acquire (increment reference count) on.
|
|
|
|
* @param <T> The type of the acquired object, given by target-typing.
|
|
|
|
* @return The acquired object.
|
|
|
|
*/
|
2021-05-27 17:34:40 +02:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
static <T> T acquire(ResourceSupport<?, ?> obj) {
|
|
|
|
return (T) obj.acquire();
|
|
|
|
}
|
|
|
|
|
2020-08-24 16:34:32 +02:00
|
|
|
/**
|
|
|
|
* Increment the reference count.
|
|
|
|
* <p>
|
2021-05-26 17:13:29 +02:00
|
|
|
* Note, this method is not thread-safe because Resources are meant to thread-confined.
|
2020-08-24 16:34:32 +02:00
|
|
|
*
|
2021-05-26 17:13:29 +02:00
|
|
|
* @return This {@link Resource} instance.
|
2020-08-24 16:34:32 +02:00
|
|
|
*/
|
2021-05-27 17:34:40 +02:00
|
|
|
protected final I acquire() {
|
2020-08-24 16:34:32 +02:00
|
|
|
if (acquires < 0) {
|
2021-05-28 15:13:16 +02:00
|
|
|
throw attachTrace(createResourceClosedException());
|
2020-08-24 16:34:32 +02:00
|
|
|
}
|
2020-10-15 16:20:26 +02:00
|
|
|
if (acquires == Integer.MAX_VALUE) {
|
|
|
|
throw new IllegalStateException("Cannot acquire more references; counter would overflow.");
|
|
|
|
}
|
2020-08-24 16:34:32 +02:00
|
|
|
acquires++;
|
2021-03-05 16:32:10 +01:00
|
|
|
tracer.acquire(acquires);
|
2020-08-24 16:34:32 +02:00
|
|
|
return self();
|
|
|
|
}
|
|
|
|
|
2021-05-28 15:13:16 +02:00
|
|
|
protected abstract RuntimeException createResourceClosedException();
|
|
|
|
|
2020-08-24 16:34:32 +02:00
|
|
|
/**
|
|
|
|
* Decrement the reference count, and despose of the resource if the last reference is closed.
|
|
|
|
* <p>
|
2021-05-26 17:13:29 +02:00
|
|
|
* Note, this method is not thread-safe because Resources are meant to be thread-confined.
|
2020-08-24 16:34:32 +02:00
|
|
|
*
|
2021-05-26 17:13:29 +02:00
|
|
|
* @throws IllegalStateException If this Resource has already been closed.
|
2020-08-24 16:34:32 +02:00
|
|
|
*/
|
|
|
|
@Override
|
Update with support for shared segments
Motivation:
We wish to make as little use of Unsafe as possible, yet have a flexible buffer API.
With the proposed support for shared segments, we're able to clean up our buffer API and simplify the implementation.
Modification:
With shared segments, we are able to implement TransferSend using supported APIs.
This allows us to remove RendezvousSend, and also our hacks in Statics.
TransferSend now works by first creating a shared segment and using that for the handover.
On the receiving end, if the segment was originally thread-confined, it will once again become
confined, but this time to the receiver thread.
Shared segments are just passed directly to their new owners.
Pooled allocators always create shared memory segments for their buffers, so they can be
shared easily via the pool.
Result:
We now have buffer ownership transfer with nice, convenient, APIs, and we have buffer pooling,
all using supported APIs.
2020-09-07 16:51:43 +02:00
|
|
|
public final void close() {
|
2020-08-24 16:34:32 +02:00
|
|
|
if (acquires == -1) {
|
2021-03-05 16:32:10 +01:00
|
|
|
throw attachTrace(new IllegalStateException("Double-free: Resource already closed and dropped."));
|
2020-08-24 16:34:32 +02:00
|
|
|
}
|
|
|
|
if (acquires == 0) {
|
2021-03-05 16:32:10 +01:00
|
|
|
tracer.drop(acquires);
|
2020-08-28 12:17:41 +02:00
|
|
|
drop.drop(impl());
|
2020-08-24 16:34:32 +02:00
|
|
|
}
|
|
|
|
acquires--;
|
2021-03-05 16:32:10 +01:00
|
|
|
tracer.close(acquires);
|
2020-08-24 16:34:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-05-26 17:13:29 +02:00
|
|
|
* Send this Resource instance to another Thread, transferring the ownership to the recipient.
|
|
|
|
* This method can be used when the receiving thread is not known up front.
|
2020-08-24 16:34:32 +02:00
|
|
|
* <p>
|
2021-05-26 17:13:29 +02:00
|
|
|
* This instance immediately becomes inaccessible, and all attempts at accessing this resource will throw.
|
|
|
|
* Calling {@link #close()} will have no effect, so this method is safe to call within a try-with-resources
|
|
|
|
* statement.
|
2020-08-24 16:34:32 +02:00
|
|
|
*
|
2020-10-15 16:20:26 +02:00
|
|
|
* @throws IllegalStateException if this object has any outstanding acquires; that is, if this object has been
|
|
|
|
* {@link #acquire() acquired} more times than it has been {@link #close() closed}.
|
2020-08-24 16:34:32 +02:00
|
|
|
*/
|
|
|
|
@Override
|
Update with support for shared segments
Motivation:
We wish to make as little use of Unsafe as possible, yet have a flexible buffer API.
With the proposed support for shared segments, we're able to clean up our buffer API and simplify the implementation.
Modification:
With shared segments, we are able to implement TransferSend using supported APIs.
This allows us to remove RendezvousSend, and also our hacks in Statics.
TransferSend now works by first creating a shared segment and using that for the handover.
On the receiving end, if the segment was originally thread-confined, it will once again become
confined, but this time to the receiver thread.
Shared segments are just passed directly to their new owners.
Pooled allocators always create shared memory segments for their buffers, so they can be
shared easily via the pool.
Result:
We now have buffer ownership transfer with nice, convenient, APIs, and we have buffer pooling,
all using supported APIs.
2020-09-07 16:51:43 +02:00
|
|
|
public final Send<I> send() {
|
2021-06-01 10:28:42 +02:00
|
|
|
if (acquires < 0) {
|
|
|
|
throw attachTrace(createResourceClosedException());
|
|
|
|
}
|
2020-11-16 18:00:32 +01:00
|
|
|
if (!isOwned()) {
|
2020-10-30 14:21:20 +01:00
|
|
|
throw notSendableException();
|
2020-10-15 16:20:26 +02:00
|
|
|
}
|
2021-03-05 16:32:10 +01:00
|
|
|
var owned = tracer.send(prepareSend(), acquires);
|
2020-11-10 14:36:39 +01:00
|
|
|
acquires = -2; // Close without dropping. This also ignore future double-free attempts.
|
Introduce Deref abstraction
Motivation:
Sometimes, we wish to operate on both buffers and anything that can produce a buffer.
For instance, when making a composite buffer, we could compose either buffers or sends.
Modification:
Introduce a Deref interface, which is extended by both Rc and Send.
A Deref can be used to acquire an Rc instance, and in doing so will also acquire a reference to the Rc.
That is, dereferencing increases the reference count.
For Rc itself, this just delegates to Rc.acquire, while for Send it delegates to Send.receive, and can only be called once.
The Allocator.compose method has been changed to take Derefs.
This allows us to compose either Bufs or Sends of bufs.
Or a mix.
Extra care and caution has been added to the code, to make sure the reference counts are managed correctly when composing buffers, now that it's a more complicated operation.
A handful of convenience methods for working with Sends have also been added to the Send interface.
Result:
We can now build a composite buffer out of sends of buffers.
2021-02-11 14:08:22 +01:00
|
|
|
return new TransferSend<I, T>(owned, drop, getClass());
|
2020-08-24 16:34:32 +02:00
|
|
|
}
|
|
|
|
|
2021-05-28 16:23:35 +02:00
|
|
|
/**
|
|
|
|
* Attach a trace of the life-cycle of this object as suppressed exceptions to the given throwable.
|
|
|
|
*
|
|
|
|
* @param throwable The throwable to attach a life-cycle trace to.
|
|
|
|
* @param <E> The concrete exception type.
|
|
|
|
* @return The given exception, which can then be thrown.
|
|
|
|
*/
|
2021-03-05 16:32:10 +01:00
|
|
|
protected <E extends Throwable> E attachTrace(E throwable) {
|
|
|
|
return tracer.attachTrace(throwable);
|
|
|
|
}
|
|
|
|
|
2020-12-11 12:09:32 +01:00
|
|
|
/**
|
2021-05-26 17:13:29 +02:00
|
|
|
* Create an {@link IllegalStateException} with a custom message, tailored to this particular
|
|
|
|
* {@link Resource} instance, for when the object cannot be sent for some reason.
|
2020-12-11 12:09:32 +01:00
|
|
|
* @return An {@link IllegalStateException} to be thrown when this object cannot be sent.
|
|
|
|
*/
|
2020-10-30 14:21:20 +01:00
|
|
|
protected IllegalStateException notSendableException() {
|
|
|
|
return new IllegalStateException(
|
2021-03-16 12:11:29 +01:00
|
|
|
"Cannot send() a reference counted object with " + countBorrows() + " borrows: " + this + '.');
|
2020-10-30 14:21:20 +01:00
|
|
|
}
|
|
|
|
|
2021-05-28 16:23:35 +02:00
|
|
|
/**
|
|
|
|
* Encapsulation bypass to call {@link #isOwned()} on the given object.
|
|
|
|
*
|
|
|
|
* @param obj The object to query the ownership state on.
|
|
|
|
* @return {@code true} if the given object is owned, otherwise {@code false}.
|
|
|
|
*/
|
2021-05-27 17:34:40 +02:00
|
|
|
static boolean isOwned(ResourceSupport<?, ?> obj) {
|
|
|
|
return obj.isOwned();
|
|
|
|
}
|
|
|
|
|
2021-05-28 16:23:35 +02:00
|
|
|
/**
|
|
|
|
* Query if this object is in an "owned" state, which means no other references have been
|
|
|
|
* {@linkplain #acquire() acquired} to it.
|
|
|
|
*
|
|
|
|
* This would usually be the case, since there are no public methods for acquiring references to these objects.
|
|
|
|
*
|
|
|
|
* @return {@code true} if this object is in an owned state, otherwise {@code false}.
|
|
|
|
*/
|
2021-05-27 17:34:40 +02:00
|
|
|
protected boolean isOwned() {
|
2020-10-30 14:21:20 +01:00
|
|
|
return acquires == 0;
|
|
|
|
}
|
|
|
|
|
2021-05-28 16:23:35 +02:00
|
|
|
/**
|
|
|
|
* Encapsulation bypass to call {@link #countBorrows()} on the given object.
|
|
|
|
*
|
|
|
|
* @param obj The object to count borrows on.
|
|
|
|
* @return The number of borrows, or outstanding {@linkplain #acquire() acquires}, if any, of the given object.
|
|
|
|
*/
|
2021-05-27 17:34:40 +02:00
|
|
|
static int countBorrows(ResourceSupport<?, ?> obj) {
|
|
|
|
return obj.countBorrows();
|
|
|
|
}
|
|
|
|
|
2021-05-07 10:54:04 +02:00
|
|
|
/**
|
|
|
|
* Count the number of borrows of this object.
|
|
|
|
* Note that even if the number of borrows is {@code 0}, this object might not be {@linkplain #isOwned() owned}
|
|
|
|
* because there could be other restrictions involved in ownership.
|
|
|
|
*
|
|
|
|
* @return The number of borrows, if any, of this object.
|
|
|
|
*/
|
2021-05-27 17:34:40 +02:00
|
|
|
protected int countBorrows() {
|
2020-11-16 18:00:32 +01:00
|
|
|
return Math.max(acquires, 0);
|
2020-11-12 14:55:42 +01:00
|
|
|
}
|
|
|
|
|
2021-02-17 13:54:11 +01:00
|
|
|
@Override
|
|
|
|
public boolean isAccessible() {
|
|
|
|
return acquires >= 0;
|
|
|
|
}
|
|
|
|
|
2020-08-24 16:34:32 +02:00
|
|
|
/**
|
|
|
|
* Prepare this instance for ownsership transfer. This method is called from {@link #send()} in the sending thread.
|
2021-05-26 17:13:29 +02:00
|
|
|
* This method should put this resource in a deactivated state where it is no longer accessible from the currently
|
|
|
|
* owning thread.
|
|
|
|
* In this state, the resource instance should only allow a call to {@link Owned#transferOwnership(Drop)} in the
|
|
|
|
* recipient thread.
|
2020-08-24 16:34:32 +02:00
|
|
|
*
|
2021-05-26 17:13:29 +02:00
|
|
|
* @return This resource instance in a deactivated state.
|
2020-08-24 16:34:32 +02:00
|
|
|
*/
|
Update with support for shared segments
Motivation:
We wish to make as little use of Unsafe as possible, yet have a flexible buffer API.
With the proposed support for shared segments, we're able to clean up our buffer API and simplify the implementation.
Modification:
With shared segments, we are able to implement TransferSend using supported APIs.
This allows us to remove RendezvousSend, and also our hacks in Statics.
TransferSend now works by first creating a shared segment and using that for the handover.
On the receiving end, if the segment was originally thread-confined, it will once again become
confined, but this time to the receiver thread.
Shared segments are just passed directly to their new owners.
Pooled allocators always create shared memory segments for their buffers, so they can be
shared easily via the pool.
Result:
We now have buffer ownership transfer with nice, convenient, APIs, and we have buffer pooling,
all using supported APIs.
2020-09-07 16:51:43 +02:00
|
|
|
protected abstract Owned<T> prepareSend();
|
2020-08-28 12:17:41 +02:00
|
|
|
|
2020-11-16 18:00:32 +01:00
|
|
|
/**
|
|
|
|
* Get access to the underlying {@link Drop} object.
|
|
|
|
* This method is unsafe because it open the possibility of bypassing and overriding resource lifetimes.
|
2020-12-11 12:09:32 +01:00
|
|
|
*
|
2020-11-16 18:00:32 +01:00
|
|
|
* @return The {@link Drop} object used by this reference counted object.
|
|
|
|
*/
|
|
|
|
protected Drop<T> unsafeGetDrop() {
|
|
|
|
return drop;
|
|
|
|
}
|
|
|
|
|
2020-12-11 12:09:32 +01:00
|
|
|
/**
|
|
|
|
* Replace the current underlying {@link Drop} object with the given one.
|
|
|
|
* This method is unsafe because it open the possibility of bypassing and overring resource lifetimes.
|
|
|
|
*
|
|
|
|
* @param replacement The new {@link Drop} object to use instead of the current one.
|
|
|
|
*/
|
|
|
|
protected void unsafeSetDrop(Drop<T> replacement) {
|
2020-12-10 12:51:18 +01:00
|
|
|
drop = Objects.requireNonNull(replacement, "Replacement drop cannot be null.");
|
2020-12-08 19:25:53 +01:00
|
|
|
}
|
|
|
|
|
2020-08-28 12:17:41 +02:00
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
private I self() {
|
|
|
|
return (I) this;
|
2020-08-24 16:34:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
2020-08-28 12:17:41 +02:00
|
|
|
private T impl() {
|
2020-08-24 16:34:32 +02:00
|
|
|
return (T) this;
|
|
|
|
}
|
|
|
|
}
|