From 6b941e9bdbc1b1a9090c280bc6c44903ff7c7b67 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 5 Feb 2015 10:40:41 +0100 Subject: [PATCH] Allow to create Epoll*Channel from FileDescriptor Motivation: Sometimes it's useful to be able to create a Epoll*Channel from an existing file descriptor. This is especially helpful if you integrade some c/jni code. Modifications: - Add extra constructor to Epoll*Channel implementations that take a FileDescriptor as an argument - Make Rename EpollFileDescriptor to NativeFileDescriptor and make it public - Also ensure we obtain the correct remote/local address when create a Channel from a FileDescriptor Result: It's now possible to create a FileDescriptor and instance a Epoll*Channel via it. --- .../src/main/c/io_netty_channel_epoll_Native.c | 8 ++++++++ .../src/main/c/io_netty_channel_epoll_Native.h | 1 + .../netty/channel/epoll/AbstractEpollChannel.java | 11 +++++++++-- .../channel/epoll/AbstractEpollServerChannel.java | 5 +++++ .../channel/epoll/AbstractEpollStreamChannel.java | 5 +++++ .../netty/channel/epoll/EpollDatagramChannel.java | 12 ++++++++++++ .../channel/epoll/EpollDomainSocketChannel.java | 11 +++++++++-- .../epoll/EpollDomainSocketChannelConfig.java | 2 +- .../epoll/EpollServerDomainSocketChannel.java | 8 ++++++++ .../channel/epoll/EpollServerSocketChannel.java | 13 +++++++++++++ .../io/netty/channel/epoll/EpollSocketChannel.java | 14 ++++++++++++++ .../main/java/io/netty/channel/epoll/Native.java | 1 + ...leDescriptor.java => NativeFileDescriptor.java} | 12 ++++++++---- .../channel/epoll/EpollDomainSocketFdTest.java | 8 ++++---- 14 files changed, 98 insertions(+), 13 deletions(-) rename transport-native-epoll/src/main/java/io/netty/channel/epoll/{EpollFileDescriptor.java => NativeFileDescriptor.java} (80%) diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c index 27b0086e99..89875b5d0b 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.c @@ -1222,6 +1222,14 @@ JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv* en } } +JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getSoError(JNIEnv* env, jclass clazz, jint fd) { + int optval = 0; + if (getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)) == -1) { + return optval; + } + return 0; +} + JNIEXPORT jint JNICALL Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd) { int optval; if (getOption(env, fd, IPPROTO_IP, IP_TOS, &optval, sizeof(optval)) == -1) { diff --git a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h index 09f3865e2f..bae39db303 100644 --- a/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h +++ b/transport-native-epoll/src/main/c/io_netty_channel_epoll_Native.h @@ -99,6 +99,7 @@ jint Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, j jint Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd); jint Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd); +jint Java_io_netty_channel_epoll_Native_getSoError(JNIEnv* env, jclass clazz, jint fd); jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz); jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java index f7a2a95a47..342f9bc4c4 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollChannel.java @@ -44,11 +44,18 @@ abstract class AbstractEpollChannel extends AbstractChannel { } AbstractEpollChannel(Channel parent, int fd, int flag, boolean active) { + this(parent, new NativeFileDescriptor(fd), flag, active); + } + + AbstractEpollChannel(Channel parent, FileDescriptor fd, int flag, boolean active) { super(parent); + if (fd == null) { + throw new NullPointerException("fd"); + } readFlag = flag; flags |= flag; this.active = active; - fileDescriptor = new EpollFileDescriptor(fd); + fileDescriptor = fd; } void setFlag(int flag) { @@ -113,7 +120,7 @@ abstract class AbstractEpollChannel extends AbstractChannel { @Override public boolean isOpen() { - return fileDescriptor != EpollFileDescriptor.INVALID; + return fileDescriptor != FileDescriptor.INVALID; } @Override diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java index adcb963470..743aed0c2e 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollServerChannel.java @@ -20,6 +20,7 @@ import io.netty.channel.ChannelOutboundBuffer; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoop; +import io.netty.channel.FileDescriptor; import io.netty.channel.ServerChannel; import java.net.InetSocketAddress; @@ -32,6 +33,10 @@ public abstract class AbstractEpollServerChannel extends AbstractEpollChannel im super(fd, Native.EPOLLIN); } + protected AbstractEpollServerChannel(FileDescriptor fd) { + super(null, fd, Native.EPOLLIN, Native.getSoError(fd.intValue()) == 0); + } + @Override protected boolean isCompatible(EventLoop loop) { return loop instanceof EpollEventLoop; diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java index 36d26c2ad6..a08525acc3 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java @@ -29,6 +29,7 @@ import io.netty.channel.ChannelPromise; import io.netty.channel.ConnectTimeoutException; import io.netty.channel.DefaultFileRegion; import io.netty.channel.EventLoop; +import io.netty.channel.FileDescriptor; import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.util.internal.PlatformDependent; @@ -61,6 +62,10 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel { flags |= Native.EPOLLRDHUP; } + protected AbstractEpollStreamChannel(FileDescriptor fd) { + super(null, fd, Native.EPOLLIN, Native.getSoError(fd.intValue()) == 0); + } + @Override protected AbstractEpollUnsafe newUnsafe() { return new EpollStreamUnsafe(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java index 5dd920cbee..1eac8abedb 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDatagramChannel.java @@ -67,6 +67,18 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements config = new EpollDatagramChannelConfig(this); } + /** + * Create a new {@link EpollDatagramChannel} from the given {@link FileDescriptor}. + */ + public EpollDatagramChannel(FileDescriptor fd) { + super(null, fd, Native.EPOLLIN, true); + config = new EpollDatagramChannelConfig(this); + + // As we create an EpollDatagramChannel from a FileDescriptor we should try to obtain the remote and local + // address from it. This is needed as the FileDescriptor may be bound already. + local = Native.localAddress(fd.intValue()); + } + @Override public InetSocketAddress remoteAddress() { return (InetSocketAddress) super.remoteAddress(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java index 5a507b13df..b4643f5b24 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannel.java @@ -37,6 +37,13 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel { super(parent, fd.intValue()); } + /** + * Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor} + */ + public EpollDomainSocketChannel(FileDescriptor fd) { + super(fd); + } + EpollDomainSocketChannel(Channel parent, int fd) { super(parent, fd); } @@ -100,7 +107,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel { @Override protected Object filterOutboundMessage(Object msg) { - if (msg instanceof EpollFileDescriptor) { + if (msg instanceof NativeFileDescriptor) { return msg; } return super.filterOutboundMessage(msg); @@ -143,7 +150,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel { readPending = false; try { - pipeline.fireChannelRead(new EpollFileDescriptor(socketFd)); + pipeline.fireChannelRead(new NativeFileDescriptor(socketFd)); } catch (Throwable t) { // keep on reading as we use epoll ET and need to consume everything from the socket pipeline.fireChannelReadComplete(); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannelConfig.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannelConfig.java index fca597af74..ee7ab05712 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannelConfig.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollDomainSocketChannelConfig.java @@ -123,7 +123,7 @@ public final class EpollDomainSocketChannelConfig extends EpollChannelConfig { * {@link DomainSocketReadMode#BYTES} which means bytes will be read from the * {@link Channel} and passed through the pipeline. If * {@link DomainSocketReadMode#FILE_DESCRIPTORS} is used - * {@link EpollFileDescriptor}s will be passed through the {@link ChannelPipeline}. + * {@link NativeFileDescriptor}s will be passed through the {@link ChannelPipeline}. * * This setting can be modified on the fly if needed. */ diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerDomainSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerDomainSocketChannel.java index 516d4e8fa7..55b3e7d44a 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerDomainSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerDomainSocketChannel.java @@ -16,6 +16,7 @@ package io.netty.channel.epoll; import io.netty.channel.Channel; +import io.netty.channel.FileDescriptor; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -34,6 +35,13 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha super(Native.socketDomainFd()); } + /** + * Creates a new {@link EpollServerDomainSocketChannel} from an existing {@link FileDescriptor}. + */ + public EpollServerDomainSocketChannel(FileDescriptor fd) { + super(fd); + } + @Override protected Channel newChildChannel(int fd) throws Exception { return new EpollDomainSocketChannel(this, fd); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java index 47128ee495..a1a19c9dc0 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollServerSocketChannel.java @@ -17,6 +17,7 @@ package io.netty.channel.epoll; import io.netty.channel.Channel; import io.netty.channel.EventLoop; +import io.netty.channel.FileDescriptor; import io.netty.channel.socket.ServerSocketChannel; import java.net.InetSocketAddress; @@ -36,6 +37,18 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i config = new EpollServerSocketChannelConfig(this); } + /** + * Creates a new {@link EpollServerSocketChannel} from an existing {@link FileDescriptor}. + */ + public EpollServerSocketChannel(FileDescriptor fd) { + super(fd); + config = new EpollServerSocketChannelConfig(this); + + // As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local + // address from it. This is needed as the FileDescriptor may be bound already. + local = Native.localAddress(fd.intValue()); + } + @Override protected boolean isCompatible(EventLoop loop) { return loop instanceof EpollEventLoop; diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java index 769fdf9f73..d19d625acd 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollSocketChannel.java @@ -19,6 +19,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoop; +import io.netty.channel.FileDescriptor; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.SocketChannel; import io.netty.util.concurrent.GlobalEventExecutor; @@ -53,6 +54,19 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme config = new EpollSocketChannelConfig(this); } + /** + * Creates a new {@link EpollSocketChannel} from an existing {@link FileDescriptor}. + */ + public EpollSocketChannel(FileDescriptor fd) { + super(fd); + config = new EpollSocketChannelConfig(this); + + // As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local + // address from it. This is needed as the FileDescriptor may be bound/connected already. + remote = Native.remoteAddress(fd.intValue()); + local = Native.localAddress(fd.intValue()); + } + /** * Returns the {@code TCP_INFO} for the current socket. See man 7 tcp. */ diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java index 1b0fd2c459..b75091ba17 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/Native.java @@ -574,6 +574,7 @@ final class Native { public static native int getTcpKeepIdle(int fd); public static native int getTcpKeepIntvl(int fd); public static native int getTcpKeepCnt(int fd); + public static native int getSoError(int fd); public static native void setKeepAlive(int fd, int keepAlive); public static native void setReceiveBufferSize(int fd, int receiveBufferSize); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollFileDescriptor.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeFileDescriptor.java similarity index 80% rename from transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollFileDescriptor.java rename to transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeFileDescriptor.java index 5ef2d94ae4..6a9d272ef2 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollFileDescriptor.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/NativeFileDescriptor.java @@ -19,11 +19,15 @@ import io.netty.channel.FileDescriptor; import java.io.IOException; -final class EpollFileDescriptor implements FileDescriptor { +/** + * Native {@link FileDescriptor} implementation which allows to wrap an {@code int} and provide a + * {@link FileDescriptor} for it. + */ +public final class NativeFileDescriptor implements FileDescriptor { private final int fd; - EpollFileDescriptor(int fd) { + public NativeFileDescriptor(int fd) { if (fd < 0) { throw new IllegalArgumentException("fd must be >= 0"); } @@ -52,11 +56,11 @@ final class EpollFileDescriptor implements FileDescriptor { if (this == o) { return true; } - if (!(o instanceof EpollFileDescriptor)) { + if (!(o instanceof NativeFileDescriptor)) { return false; } - return fd == ((EpollFileDescriptor) o).fd; + return fd == ((NativeFileDescriptor) o).fd; } @Override diff --git a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketFdTest.java b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketFdTest.java index 33dc44046f..62c61fc392 100644 --- a/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketFdTest.java +++ b/transport-native-epoll/src/test/java/io/netty/channel/epoll/EpollDomainSocketFdTest.java @@ -71,7 +71,7 @@ public class EpollDomainSocketFdTest extends AbstractSocketTest { cb.handler(new ChannelInboundHandlerAdapter() { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - EpollFileDescriptor fd = (EpollFileDescriptor) msg; + NativeFileDescriptor fd = (NativeFileDescriptor) msg; queue.offer(fd); } @@ -90,9 +90,9 @@ public class EpollDomainSocketFdTest extends AbstractSocketTest { cc.close().sync(); sc.close().sync(); - if (received instanceof EpollFileDescriptor) { - Assert.assertNotSame(EpollFileDescriptor.INVALID, received); - ((EpollFileDescriptor) received).close(); + if (received instanceof NativeFileDescriptor) { + Assert.assertNotSame(NativeFileDescriptor.INVALID, received); + ((NativeFileDescriptor) received).close(); Assert.assertNull(queue.poll()); } else { throw (Throwable) received;