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.
|
|
|
|
*/
|
2020-11-17 15:38:11 +01:00
|
|
|
package io.netty.buffer.api;
|
2020-07-24 19:38:48 +02:00
|
|
|
|
|
|
|
import java.lang.invoke.VarHandle;
|
|
|
|
|
2021-03-18 15:18:22 +01:00
|
|
|
import static io.netty.buffer.api.internal.Statics.findVarHandle;
|
2020-10-28 14:38:14 +01:00
|
|
|
import static java.lang.invoke.MethodHandles.lookup;
|
2020-07-24 19:38:48 +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
|
|
|
class TransferSend<I extends Rc<I>, T extends Rc<I>> implements Send<I> {
|
2020-07-24 19:38:48 +02:00
|
|
|
private static final VarHandle RECEIVED = findVarHandle(lookup(), TransferSend.class, "received", boolean.class);
|
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
|
|
|
private final Owned<T> outgoing;
|
2020-07-24 19:38:48 +02:00
|
|
|
private final Drop<T> drop;
|
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
|
|
|
private final Class<?> concreteType;
|
2020-07-24 19:38:48 +02:00
|
|
|
@SuppressWarnings("unused")
|
|
|
|
private volatile boolean received; // Accessed via VarHandle
|
|
|
|
|
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
|
|
|
TransferSend(Owned<T> outgoing, Drop<T> drop, Class<?> concreteType) {
|
2020-07-24 19:38:48 +02:00
|
|
|
this.outgoing = outgoing;
|
|
|
|
this.drop = drop;
|
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
|
|
|
this.concreteType = concreteType;
|
2020-07-24 19:38:48 +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
|
|
|
@SuppressWarnings("unchecked")
|
2020-07-24 19:38:48 +02:00
|
|
|
@Override
|
2020-08-28 12:17:41 +02:00
|
|
|
public I receive() {
|
2020-11-16 18:00:32 +01:00
|
|
|
gateReception();
|
2020-10-28 14:38:14 +01:00
|
|
|
var copy = outgoing.transferOwnership(drop);
|
2020-12-10 12:51:18 +01:00
|
|
|
drop.attach(copy);
|
2020-08-28 12:17:41 +02:00
|
|
|
return (I) copy;
|
2020-07-24 19:38:48 +02:00
|
|
|
}
|
2020-11-16 18:00:32 +01:00
|
|
|
|
|
|
|
Owned<T> unsafeUnwrapOwned() {
|
|
|
|
gateReception();
|
|
|
|
return outgoing;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void gateReception() {
|
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
|
|
|
if ((boolean) RECEIVED.getAndSet(this, true)) {
|
2020-11-16 18:00:32 +01:00
|
|
|
throw new IllegalStateException("This object has already been received.");
|
|
|
|
}
|
|
|
|
}
|
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
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isInstanceOf(Class<?> cls) {
|
|
|
|
return cls.isAssignableFrom(concreteType);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void discard() {
|
|
|
|
if (!(boolean) RECEIVED.getAndSet(this, true)) {
|
|
|
|
var copy = outgoing.transferOwnership(drop);
|
|
|
|
drop.attach(copy);
|
|
|
|
copy.close();
|
|
|
|
}
|
|
|
|
}
|
2020-07-24 19:38:48 +02:00
|
|
|
}
|