View Javadoc
1   /*
2    * Copyright 2013 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.netty5.channel.epoll;
17  
18  import io.netty5.channel.DefaultFileRegion;
19  import io.netty5.channel.unix.FileDescriptor;
20  import io.netty5.channel.unix.PeerCredentials;
21  import io.netty5.channel.unix.Socket;
22  import io.netty5.channel.unix.Unix;
23  import io.netty5.util.internal.ClassInitializerUtil;
24  import io.netty5.util.internal.NativeLibraryLoader;
25  import io.netty5.util.internal.PlatformDependent;
26  import io.netty5.util.internal.ThrowableUtil;
27  import io.netty5.util.internal.logging.InternalLogger;
28  import io.netty5.util.internal.logging.InternalLoggerFactory;
29  
30  import java.io.IOException;
31  import java.nio.channels.FileChannel;
32  import java.nio.channels.Selector;
33  
34  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.epollerr;
35  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.epollet;
36  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.epollin;
37  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.epollout;
38  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup;
39  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingRecvmmsg;
40  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingSendmmsg;
41  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion;
42  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.tcpFastopenMode;
43  import static io.netty5.channel.epoll.NativeStaticallyReferencedJniMethods.tcpMd5SigMaxKeyLen;
44  import static io.netty5.channel.unix.Errors.ioResult;
45  import static io.netty5.channel.unix.Errors.newIOException;
46  
47  /**
48   * Native helper methods
49   * <p><strong>Internal usage only!</strong>
50   * <p>Static members which call JNI methods must be defined in {@link NativeStaticallyReferencedJniMethods}.
51   */
52  public final class Native {
53      private static final InternalLogger logger = InternalLoggerFactory.getInstance(Native.class);
54  
55      static {
56          Selector selector = null;
57          try {
58              // We call Selector.open() as this will under the hood cause IOUtil to be loaded.
59              // This is a workaround for a possible classloader deadlock that could happen otherwise:
60              //
61              // See https://github.com/netty/netty/issues/10187
62              selector = Selector.open();
63          } catch (IOException ignore) {
64              // Just ignore
65          }
66  
67          // Preload all classes that will be used in the OnLoad(...) function of JNI to eliminate the possiblity of a
68          // class-loader deadlock. This is a workaround for https://github.com/netty/netty/issues/11209.
69  
70          // This needs to match all the classes that are loaded via NETTY_JNI_UTIL_LOAD_CLASS or looked up via
71          // NETTY_JNI_UTIL_FIND_CLASS.
72          ClassInitializerUtil.tryLoadClasses(Native.class,
73                  // netty_epoll_linuxsocket
74                  PeerCredentials.class, DefaultFileRegion.class, FileChannel.class, java.io.FileDescriptor.class,
75                  // netty_epoll_native
76                  NativeDatagramPacketArray.NativeDatagramPacket.class
77          );
78  
79          try {
80              // First, try calling a side-effect free JNI method to see if the library was already
81              // loaded by the application.
82              offsetofEpollData();
83          } catch (UnsatisfiedLinkError ignore) {
84              // The library was not previously loaded, load it now.
85              loadNativeLibrary();
86          } finally {
87              try {
88                  if (selector != null) {
89                      selector.close();
90                  }
91              } catch (IOException ignore) {
92                  // Just ignore
93              }
94          }
95          Unix.registerInternal(Native::registerUnix);
96      }
97  
98      private static native int registerUnix();
99  
100     // EventLoop operations and constants
101     public static final int EPOLLIN = epollin();
102     public static final int EPOLLOUT = epollout();
103     public static final int EPOLLRDHUP = epollrdhup();
104     public static final int EPOLLET = epollet();
105     public static final int EPOLLERR = epollerr();
106 
107     public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg();
108     static final boolean IS_SUPPORTING_RECVMMSG = isSupportingRecvmmsg();
109     static final boolean IS_SUPPORTING_UDP_SEGMENT = isSupportingUdpSegment();
110     private static final int TFO_ENABLED_CLIENT_MASK = 0x1;
111     private static final int TFO_ENABLED_SERVER_MASK = 0x2;
112     private static final int TCP_FASTOPEN_MODE = tcpFastopenMode();
113     /**
114      * <a href ="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">tcp_fastopen</a> client mode enabled
115      * state.
116      */
117     static final boolean IS_SUPPORTING_TCP_FASTOPEN_CLIENT =
118             (TCP_FASTOPEN_MODE & TFO_ENABLED_CLIENT_MASK) == TFO_ENABLED_CLIENT_MASK;
119     /**
120      * <a href ="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">tcp_fastopen</a> server mode enabled
121      * state.
122      */
123     static final boolean IS_SUPPORTING_TCP_FASTOPEN_SERVER =
124             (TCP_FASTOPEN_MODE & TFO_ENABLED_SERVER_MASK) == TFO_ENABLED_SERVER_MASK;
125     /**
126      * @deprecated Use {@link Epoll#isTcpFastOpenClientSideAvailable()}
127      * or {@link Epoll#isTcpFastOpenServerSideAvailable()}.
128      */
129     @Deprecated
130     public static final boolean IS_SUPPORTING_TCP_FASTOPEN = IS_SUPPORTING_TCP_FASTOPEN_CLIENT ||
131             IS_SUPPORTING_TCP_FASTOPEN_SERVER;
132     public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
133     public static final String KERNEL_VERSION = kernelVersion();
134 
135     public static FileDescriptor newEventFd() {
136         return new FileDescriptor(eventFd());
137     }
138 
139     public static FileDescriptor newTimerFd() {
140         return new FileDescriptor(timerFd());
141     }
142 
143     private static native boolean isSupportingUdpSegment();
144     private static native int eventFd();
145     private static native int timerFd();
146     public static native void eventFdWrite(int fd, long value);
147 
148     public static FileDescriptor newEpollCreate() {
149         return new FileDescriptor(epollCreate());
150     }
151 
152     private static native int epollCreate();
153 
154     static int epollWait(FileDescriptor epollFd, EpollEventArray events, boolean immediatePoll) throws IOException {
155         return epollWait(epollFd, events, immediatePoll ? 0 : -1);
156     }
157 
158     /**
159      * This uses epoll's own timeout and does not reset/re-arm any timerfd
160      */
161     static int epollWait(FileDescriptor epollFd, EpollEventArray events, int timeoutMillis) throws IOException {
162         int ready = epollWait(epollFd.intValue(), events.memoryAddress(), events.length(), timeoutMillis);
163         if (ready < 0) {
164             throw newIOException("epoll_wait", ready);
165         }
166         return ready;
167     }
168 
169     static long epollWait(FileDescriptor epollFd, EpollEventArray events, FileDescriptor timerFd,
170                           int timeoutSec, int timeoutNs, long millisThreshold) throws IOException {
171         if (timeoutSec == 0 && timeoutNs == 0) {
172             // Zero timeout => poll (aka return immediately)
173             // We shift this to be consistent with what is done in epollWait0(...)
174             return ((long) epollWait(epollFd, events, 0)) << 32;
175         }
176         if (timeoutSec == Integer.MAX_VALUE) {
177             // Max timeout => wait indefinitely: disarm timerfd first
178             timeoutSec = 0;
179             timeoutNs = 0;
180         }
181         long result = epollWait0(epollFd.intValue(), events.memoryAddress(), events.length(), timerFd.intValue(),
182                 timeoutSec, timeoutNs, millisThreshold);
183         int ready = epollReady(result);
184         if (ready < 0) {
185             throw newIOException("epoll_wait", ready);
186         }
187         return result;
188     }
189 
190     // IMPORTANT: This needs to be consistent with what is used in netty5_epoll_native.c
191     static int epollReady(long result) {
192         return (int) (result >> 32);
193     }
194 
195     // IMPORTANT: This needs to be consistent with what is used in netty5_epoll_native.c
196     static boolean epollTimerWasUsed(long result) {
197         return (result & 0xff) != 0;
198     }
199 
200     /**
201      * Non-blocking variant of
202      * {@link #epollWait(FileDescriptor, EpollEventArray, FileDescriptor, int, int, long)}
203      * that will also hint to processor we are in a busy-wait loop.
204      */
205     public static int epollBusyWait(FileDescriptor epollFd, EpollEventArray events) throws IOException {
206         int ready = epollBusyWait0(epollFd.intValue(), events.memoryAddress(), events.length());
207         if (ready < 0) {
208             throw newIOException("epoll_wait", ready);
209         }
210         return ready;
211     }
212 
213     private static native long epollWait0(int efd, long address, int len, int timerFd,
214                                          int timeoutSec, int timeoutNs, long millisThreshold);
215     private static native int epollWait(int efd, long address, int len, int timeout);
216     private static native int epollBusyWait0(int efd, long address, int len);
217 
218     public static void epollCtlAdd(int efd, final int fd, final int flags) throws IOException {
219         int res = epollCtlAdd0(efd, fd, flags);
220         if (res < 0) {
221             throw newIOException("epoll_ctl", res);
222         }
223     }
224     private static native int epollCtlAdd0(int efd, int fd, int flags);
225 
226     public static void epollCtlMod(int efd, final int fd, final int flags) throws IOException {
227         int res = epollCtlMod0(efd, fd, flags);
228         if (res < 0) {
229             throw newIOException("epoll_ctl", res);
230         }
231     }
232     private static native int epollCtlMod0(int efd, int fd, int flags);
233 
234     public static void epollCtlDel(int efd, final int fd) throws IOException {
235         int res = epollCtlDel0(efd, fd);
236         if (res < 0) {
237             throw newIOException("epoll_ctl", res);
238         }
239     }
240     private static native int epollCtlDel0(int efd, int fd);
241 
242     @Deprecated
243     public static int sendmmsg(int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
244                                int offset, int len) throws IOException {
245         return sendmmsg(fd, Socket.isIPv6Preferred(), msgs, offset, len);
246     }
247 
248     static int sendmmsg(int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
249                                int offset, int len) throws IOException {
250         int res = sendmmsg0(fd, ipv6, msgs, offset, len);
251         if (res >= 0) {
252             return res;
253         }
254         return ioResult("sendmmsg", res);
255     }
256 
257     private static native int sendmmsg0(
258             int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
259 
260     static int recvmmsg(int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
261                         int offset, int len) throws IOException {
262         int res = recvmmsg0(fd, ipv6, msgs, offset, len);
263         if (res >= 0) {
264             return res;
265         }
266         return ioResult("recvmmsg", res);
267     }
268 
269     private static native int recvmmsg0(
270             int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
271 
272     static int recvmsg(int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket packet) throws IOException {
273         int res = recvmsg0(fd, ipv6, packet);
274         if (res >= 0) {
275             return res;
276         }
277         return ioResult("recvmsg", res);
278     }
279 
280     private static native int recvmsg0(
281             int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket msg);
282 
283     // epoll_event related
284     public static native int sizeofEpollEvent();
285     public static native int offsetofEpollData();
286 
287     private static void loadNativeLibrary() {
288         String name = PlatformDependent.normalizedOs();
289         if (!"linux".equals(name)) {
290             throw new IllegalStateException("Only supported on Linux");
291         }
292         String staticLibName = "netty5_transport_native_epoll";
293         String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
294         ClassLoader cl = PlatformDependent.getClassLoader(Native.class);
295         try {
296             NativeLibraryLoader.load(sharedLibName, cl);
297         } catch (UnsatisfiedLinkError e1) {
298             try {
299                 NativeLibraryLoader.load(staticLibName, cl);
300                 logger.debug("Failed to load {}", sharedLibName, e1);
301             } catch (UnsatisfiedLinkError e2) {
302                 ThrowableUtil.addSuppressed(e1, e2);
303                 throw e1;
304             }
305         }
306     }
307 
308     private Native() {
309         // utility
310     }
311 }