Skip to content
This repository was archived by the owner on Dec 12, 2022. It is now read-only.

Commit 1d73f65

Browse files
authored
Merge pull request #32 from netty/bytebuf-integration
Make the incubating buffers exposable as ByteBuf
2 parents f14a779 + 78f04ee commit 1d73f65

File tree

16 files changed

+2178
-18
lines changed

16 files changed

+2178
-18
lines changed

README.adoc

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
= Netty Incubator Buffer API
2+
3+
This repository is incubating a new buffer API proposed for Netty 5.
4+
5+
== Building and Testing
6+
7+
Short version: just run `make`.
8+
9+
The project currently relies on snapshot versions of the https://github.com/openjdk/panama-foreign[Panama Foreign] fork of OpenJDK.
10+
This allows us to test out the most recent version of the `jdk.incubator.foreign` APIs, but also make building, and local development more involved.
11+
To simplify things, we have a Docker based build, controlled via a Makefile with the following commands:
12+
13+
* `image` – build the docker image.This includes building a snapshot of OpenJDK, and download all relevant Maven dependencies.
14+
* `test` – run all tests in a docker container.This implies `image`.The container is automatically deleted afterwards.
15+
* `dbg` – drop into a shell in the build container, without running the build itself.The debugging container is not deleted afterwards.
16+
* `clean` – remove the leftover containers created by `dbg`, `test`, and `build`.
17+
* `build` – build binaries and run all tests in a container, and copy the `target` directory out of the container afterwards.This is the default build target.
18+
19+
== Example: Echo Client and Server
20+
21+
Making use of this new buffer API on the client side is quite easy.
22+
Even though Netty 5 does not have native support for these buffers, it is able to convert them to the old `ByteBuf` API as needed.
23+
This means we are able to send incubator buffers through a Netty pipeline, and have it work as if we were sending `ByteBuf` instances.
24+
25+
[source,java]
26+
----
27+
public final class Client {
28+
public static void main(String[] args) throws Exception {
29+
EventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory());
30+
try (BufferAllocator allocator = BufferAllocator.pooledDirect()) { // <1>
31+
Bootstrap b = new Bootstrap();
32+
b.group(group)
33+
.channel(NioSocketChannel.class)
34+
.option(ChannelOption.TCP_NODELAY, true)
35+
.handler(new ChannelInitializer<SocketChannel>() {
36+
@Override
37+
public void initChannel(SocketChannel ch) throws Exception {
38+
ch.pipeline().addLast(new ChannelHandlerAdapter() {
39+
@Override
40+
public void channelActive(ChannelHandlerContext ctx) {
41+
Buffer message = allocator.allocate(256); // <2>
42+
for (int i = 0; i < message.capacity(); i++) {
43+
message.writeByte((byte) i);
44+
}
45+
ctx.writeAndFlush(message); // <3>
46+
}
47+
});
48+
}
49+
});
50+
51+
// Start the client.
52+
ChannelFuture f = b.connect("127.0.0.1", 8007).sync();
53+
54+
// Wait until the connection is closed.
55+
f.channel().closeFuture().sync();
56+
} finally {
57+
// Shut down the event loop to terminate all threads.
58+
group.shutdownGracefully();
59+
}
60+
}
61+
}
62+
----
63+
<1> A life-cycled allocator is created to wrap the scope of our application.
64+
<2> Buffers are allocated with one of the `allocate` methods.
65+
<3> The buffer can then be sent down the pipeline, and will be written to the socket just like a `ByteBuf` would.
66+
67+
[NOTE]
68+
--
69+
The same is not the case for `BufferHolder`.
70+
It is not treated the same as a `ByteBufHolder`.
71+
--
72+
73+
On the server size, things are more complicated because Netty itself will be allocating the buffers, and the `ByteBufAllocator` API is only capable of returning `ByteBuf` instances.
74+
The `ByteBufAllocatorAdaptor` will allocate `ByteBuf` instances that are backed by the new buffers.
75+
The buffers can then we extracted from the `ByteBuf` instances with the `ByteBufAdaptor.extract` method.
76+
77+
We can tell a Netty server how to allocate buffers by setting the `ALLOCATOR` child-channel option:
78+
79+
[source,java]
80+
----
81+
ByteBufAllocatorAdaptor allocator = new ByteBufAllocatorAdaptor(); // <1>
82+
ServerBootstrap server = new ServerBootstrap();
83+
server.group(bossGroup, workerGroup)
84+
.channel(NioServerSocketChannel.class)
85+
.childOption(ChannelOption.ALLOCATOR, allocator) // <2>
86+
.handler(new EchoServerHandler());
87+
----
88+
<1> The `ByteBufAllocatorAdaptor` implements `ByteBufAllocator`, and directly allocates `ByteBuf` instances that are backed by buffers that use the new API.
89+
<2> To make Netty use a given allocator when allocating buffers for receiving data, we set the allocator as a child option.
90+
91+
With the above, we just changed how the buffers are allocated, but we haven't changed the API we use for interacting with the buffers.
92+
The buffers are still allocated at `ByteBuf` instances, and flow through the pipeline as such.
93+
If we want to use the new buffer API in our server handlers, we have to extract the buffers from the `ByteBuf` instances that are passed down:
94+
95+
[source,java]
96+
----
97+
import io.netty.buffer.ByteBuf;
98+
import io.netty.buffer.api.Buffer;
99+
import io.netty.buffer.api.adaptor.ByteBufAdaptor;
100+
101+
@Sharable
102+
public class EchoServerHandler implements ChannelHandler {
103+
@Override
104+
public void channelRead(ChannelHandlerContext ctx, Object msg) { // <1>
105+
if (msg instanceof ByteBuf) { // <2>
106+
// For this example, we only echo back buffers that are using the new buffer API.
107+
Buffer buf = ByteBufAdaptor.extract((ByteBuf) msg); // <3>
108+
ctx.write(buf); // <4>
109+
}
110+
}
111+
112+
@Override
113+
public void channelReadComplete(ChannelHandlerContext ctx) {
114+
ctx.flush();
115+
}
116+
}
117+
----
118+
<1> Netty pipelines are defined as transferring `Object` instances as messages.
119+
<2> When we receive data directly from a socket, these messages will be `ByteBuf` instances with the received data.
120+
<3> Since we set the allocator to create `ByteBuf` instances that are backed by buffers with the new API, we will be able to extract the backing `Buffer` instances.
121+
<4> We can then operate on the extracted `Buffer` instances directly.
122+
The `Buffer` and `ByteBuf` instances mirror each other exactly.
123+
In this case, we just write them back to the client that sent the data to us.
124+
125+
The files in `src/test/java/io/netty/buffer/api/examples/echo` for the full source code to this example.

README.md

Lines changed: 0 additions & 17 deletions
This file was deleted.

pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,12 @@
394394
<version>${netty.build.version}</version>
395395
<scope>test</scope>
396396
</dependency>
397+
<dependency>
398+
<groupId>io.netty</groupId>
399+
<artifactId>netty-handler</artifactId>
400+
<version>${netty.version}</version>
401+
<scope>test</scope>
402+
</dependency>
397403
<dependency>
398404
<groupId>org.openjdk.jmh</groupId>
399405
<artifactId>jmh-core</artifactId>

src/main/java/io/netty/buffer/api/BufferHolder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,4 +190,9 @@ protected final Buffer getBuf() {
190190
protected final Buffer getBufVolatile() {
191191
return (Buffer) BUF.getVolatile(this);
192192
}
193+
194+
@Override
195+
public boolean isAccessible() {
196+
return buf.isAccessible();
197+
}
193198
}

src/main/java/io/netty/buffer/api/Rc.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,13 @@ default boolean isInstanceOf(Class<?> cls) {
8787
* @return The number of borrows, if any, of this object.
8888
*/
8989
int countBorrows();
90+
91+
/**
92+
* Check if this object is accessible.
93+
*
94+
* @return {@code true} if this object is still valid and can be accessed,
95+
* otherwise {@code false} if, for instance, this object has been dropped/deallocated,
96+
* or been {@linkplain #send() sent} elsewhere.
97+
*/
98+
boolean isAccessible();
9099
}

src/main/java/io/netty/buffer/api/RcSupport.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ public int countBorrows() {
102102
return Math.max(acquires, 0);
103103
}
104104

105+
@Override
106+
public boolean isAccessible() {
107+
return acquires >= 0;
108+
}
109+
105110
/**
106111
* Prepare this instance for ownsership transfer. This method is called from {@link #send()} in the sending thread.
107112
* This method should put this Rc in a deactivated state where it is no longer accessible from the currently owning
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2021 The Netty Project
3+
*
4+
* The Netty Project licenses this file to you under the Apache License,
5+
* version 2.0 (the "License"); you may not use this file except in compliance
6+
* with the License. You may obtain a copy of the License at:
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
package io.netty.buffer.api.adaptor;
17+
18+
import io.netty.buffer.ByteBufConvertible;
19+
import io.netty.util.ReferenceCounted;
20+
21+
/**
22+
* Interfaces that are required for an object to stand-in for a {@link io.netty.buffer.ByteBuf} in Netty.
23+
*/
24+
public interface BufferIntegratable extends ByteBufConvertible, ReferenceCounted {
25+
}

0 commit comments

Comments
 (0)