netty-incubator-buffer-api/src/main/java/io/netty/buffer/api/BufferHolder.java
Chris Vest 1b65bf9a23 Make the incubating buffers exposable as ByteBuf
Motivation:
This makes it possible to use the new buffer API in Netty as is.

Modification:
Make the MemSegBuffer implementation class implement AsByteBuf and ReferenceCounted.
The produced ByteBuf instance delegates all calls to the underlying Buffer instance as faithfully as possible.
One area where the two deviates, is that it's not possible to create non-retained duplicates and slices with the new buffer API.

Result:
It is now possible to use the new buffer API on both client and server side.
The Echo* examples demonstrate this, and the EchoIT proves it with a test.
The API is used more directly on the client side, since the server-side allocator in Netty does not know how to allocate buffers with the incubating API.
2021-03-01 10:49:09 +01:00

199 lines
7.1 KiB
Java

/*
* 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:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/
package io.netty.buffer.api;
import java.lang.invoke.VarHandle;
import java.util.Objects;
import static io.netty.buffer.api.Statics.findVarHandle;
import static java.lang.invoke.MethodHandles.lookup;
/**
* The {@link BufferHolder} is an abstract class that simplifies the implementation of objects that themselves contain
* a {@link Buffer} instance.
* <p>
* The {@link BufferHolder} can only hold on to a single buffer, so objects and classes that need to hold on to multiple
* buffers will have to do their implementation from scratch, though they can use the code of the {@link BufferHolder}
* as inspiration.
* <p>
* If you just want an object that is a reference to a buffer, then the {@link BufferRef} can be used for that purpose.
* If you have an advanced use case where you wish to implement {@link Rc}, and tightly control lifetimes, then
* {@link RcSupport} can be of help.
*
* @param <T> The concrete {@link BufferHolder} type.
*/
public abstract class BufferHolder<T extends BufferHolder<T>> implements Rc<T> {
private static final VarHandle BUF = findVarHandle(lookup(), BufferHolder.class, "buf", Buffer.class);
private Buffer buf;
/**
* Create a new {@link BufferHolder} to hold the given {@linkplain Buffer buffer}.
* <p>
* <strong>Note:</strong> this increases the reference count of the given buffer.
*
* @param buf The {@linkplain Buffer buffer} to be held by this holder.
*/
protected BufferHolder(Buffer buf) {
this.buf = Objects.requireNonNull(buf, "The buffer cannot be null.").acquire();
}
/**
* Create a new {@link BufferHolder} to hold the {@linkplain Buffer buffer} received from the given {@link Send}.
* <p>
* The {@link BufferHolder} will then be holding exclusive ownership of the buffer.
*
* @param send The {@linkplain Buffer buffer} to be held by this holder.
*/
protected BufferHolder(Send<Buffer> send) {
buf = Objects.requireNonNull(send, "The send cannot be null.").receive();
}
@SuppressWarnings("unchecked")
@Override
public T acquire() {
buf.acquire();
return (T) this;
}
@Override
public void close() {
buf.close();
}
@Override
public boolean isOwned() {
return buf.isOwned();
}
@Override
public int countBorrows() {
return buf.countBorrows();
}
@SuppressWarnings("unchecked")
@Override
public Send<T> send() {
return buf.send().map((Class<T>) getClass(), this::receive);
}
/**
* Called when a {@linkplain #send() sent} {@link BufferHolder} is received by the recipient.
* The {@link BufferHolder} should return a new concrete instance, that wraps the given {@link Buffer} object.
*
* @param buf The {@link Buffer} that is {@linkplain Send#receive() received} by the recipient,
* and needs to be wrapped in a new {@link BufferHolder} instance.
* @return A new {@linkplain T buffer holder} instance, containing the given {@linkplain Buffer buffer}.
*/
protected abstract T receive(Buffer buf);
/**
* Replace the underlying referenced buffer with the given buffer.
* <p>
* This method is protected to permit advanced use cases of {@link BufferHolder} sub-class implementations.
* <p>
* <strong>Note:</strong> this method decreases the reference count of the current buffer,
* and increases the reference count of the new buffer.
* <p>
* The buffer assignment is performed using a plain store.
*
* @param newBuf The new {@link Buffer} instance that is replacing the currently held buffer.
*/
protected final void replaceBuf(Buffer newBuf) {
try (var ignore = buf) {
buf = newBuf.acquire();
}
}
/**
* Replace the underlying referenced buffer with the given buffer.
* <p>
* This method is protected to permit advanced use cases of {@link BufferHolder} sub-class implementations.
* <p>
* <strong>Note:</strong> this method decreases the reference count of the current buffer,
* and takes exclusive ownership of the sent buffer.
* <p>
* The buffer assignment is performed using a plain store.
*
* @param send The new {@link Buffer} instance that is replacing the currently held buffer.
*/
protected final void replaceBuf(Send<Buffer> send) {
try (var ignore = buf) {
buf = send.receive();
}
}
/**
* Replace the underlying referenced buffer with the given buffer.
* <p>
* This method is protected to permit advanced use cases of {@link BufferHolder} sub-class implementations.
* <p>
* <strong>Note:</strong> this method decreases the reference count of the current buffer,
* and increases the reference count of the new buffer.
* <p>
* The buffer assignment is performed using a volatile store.
*
* @param newBuf The new {@link Buffer} instance that is replacing the currently held buffer.
*/
protected final void replaceBufVolatile(Buffer newBuf) {
var prev = (Buffer) BUF.getAndSet(this, newBuf.acquire());
prev.close();
}
/**
* Replace the underlying referenced buffer with the given buffer.
* <p>
* This method is protected to permit advanced use cases of {@link BufferHolder} sub-class implementations.
* <p>
* <strong>Note:</strong> this method decreases the reference count of the current buffer,
* and takes exclusive ownership of the sent buffer.
* <p>
* The buffer assignment is performed using a volatile store.
*
* @param send The {@link Send} with the new {@link Buffer} instance that is replacing the currently held buffer.
*/
protected final void replaceBufVolatile(Send<Buffer> send) {
var prev = (Buffer) BUF.getAndSet(this, send.receive());
prev.close();
}
/**
* Access the held {@link Buffer} instance.
* <p>
* The access is performed using a plain load.
*
* @return The {@link Buffer} instance being held by this {@linkplain T buffer holder}.
*/
protected final Buffer getBuf() {
return buf;
}
/**
* Access the held {@link Buffer} instance.
* <p>
* The access is performed using a volatile load.
*
* @return The {@link Buffer} instance being held by this {@linkplain T buffer holder}.
*/
protected final Buffer getBufVolatile() {
return (Buffer) BUF.getVolatile(this);
}
@Override
public boolean isAccessible() {
return buf.isAccessible();
}
}