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.netty.channel.epoll;
17  
18  import io.netty.channel.DefaultFileRegion;
19  import io.netty.channel.unix.FileDescriptor;
20  import io.netty.channel.unix.PeerCredentials;
21  import io.netty.channel.unix.Socket;
22  import io.netty.channel.unix.Unix;
23  import io.netty.util.internal.ClassInitializerUtil;
24  import io.netty.util.internal.NativeLibraryLoader;
25  import io.netty.util.internal.PlatformDependent;
26  import io.netty.util.internal.ThrowableUtil;
27  import io.netty.util.internal.logging.InternalLogger;
28  import io.netty.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.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollerr;
35  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollet;
36  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollin;
37  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout;
38  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup;
39  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingRecvmmsg;
40  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingSendmmsg;
41  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion;
42  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.tcpFastopenMode;
43  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.tcpMd5SigMaxKeyLen;
44  import static io.netty.channel.unix.Errors.ioResult;
45  import static io.netty.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(new Runnable() {
96              @Override
97              public void run() {
98                  registerUnix();
99              }
100         });
101     }
102 
103     private static native int registerUnix();
104 
105     // EventLoop operations and constants
106     public static final int EPOLLIN = epollin();
107     public static final int EPOLLOUT = epollout();
108     public static final int EPOLLRDHUP = epollrdhup();
109     public static final int EPOLLET = epollet();
110     public static final int EPOLLERR = epollerr();
111 
112     public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg();
113     static final boolean IS_SUPPORTING_RECVMMSG = isSupportingRecvmmsg();
114     static final boolean IS_SUPPORTING_UDP_SEGMENT = isSupportingUdpSegment();
115     private static final int TFO_ENABLED_CLIENT_MASK = 0x1;
116     private static final int TFO_ENABLED_SERVER_MASK = 0x2;
117     private static final int TCP_FASTOPEN_MODE = tcpFastopenMode();
118     /**
119      * <a href ="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">tcp_fastopen</a> client mode enabled
120      * state.
121      */
122     static final boolean IS_SUPPORTING_TCP_FASTOPEN_CLIENT =
123             (TCP_FASTOPEN_MODE & TFO_ENABLED_CLIENT_MASK) == TFO_ENABLED_CLIENT_MASK;
124     /**
125      * <a href ="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">tcp_fastopen</a> server mode enabled
126      * state.
127      */
128     static final boolean IS_SUPPORTING_TCP_FASTOPEN_SERVER =
129             (TCP_FASTOPEN_MODE & TFO_ENABLED_SERVER_MASK) == TFO_ENABLED_SERVER_MASK;
130     /**
131      * @deprecated Use {@link Epoll#isTcpFastOpenClientSideAvailable()}
132      * or {@link Epoll#isTcpFastOpenServerSideAvailable()}.
133      */
134     @Deprecated
135     public static final boolean IS_SUPPORTING_TCP_FASTOPEN = IS_SUPPORTING_TCP_FASTOPEN_CLIENT ||
136             IS_SUPPORTING_TCP_FASTOPEN_SERVER;
137     public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
138     public static final String KERNEL_VERSION = kernelVersion();
139 
140     public static FileDescriptor newEventFd() {
141         return new FileDescriptor(eventFd());
142     }
143 
144     public static FileDescriptor newTimerFd() {
145         return new FileDescriptor(timerFd());
146     }
147 
148     private static native boolean isSupportingUdpSegment();
149     private static native int eventFd();
150     private static native int timerFd();
151     public static native void eventFdWrite(int fd, long value);
152     public static native void eventFdRead(int fd);
153 
154     public static FileDescriptor newEpollCreate() {
155         return new FileDescriptor(epollCreate());
156     }
157 
158     private static native int epollCreate();
159 
160     /**
161      * @deprecated this method is no longer supported. This functionality is internal to this package.
162      */
163     @Deprecated
164     public static int epollWait(FileDescriptor epollFd, EpollEventArray events, FileDescriptor timerFd,
165                                 int timeoutSec, int timeoutNs) throws IOException {
166         long result = epollWait(epollFd, events, timerFd, timeoutSec, timeoutNs, -1);
167         return epollReady(result);
168     }
169 
170     static long epollWait(FileDescriptor epollFd, EpollEventArray events, FileDescriptor timerFd,
171                                 int timeoutSec, int timeoutNs, long millisThreshold) throws IOException {
172         if (timeoutSec == 0 && timeoutNs == 0) {
173             // Zero timeout => poll (aka return immediately)
174             // We shift this to be consistent with what is done in epollWait0(...)
175             return ((long) epollWait(epollFd, events, 0)) << 32;
176         }
177         if (timeoutSec == Integer.MAX_VALUE) {
178             // Max timeout => wait indefinitely: disarm timerfd first
179             timeoutSec = 0;
180             timeoutNs = 0;
181         }
182         long result = epollWait0(epollFd.intValue(), events.memoryAddress(), events.length(), timerFd.intValue(),
183                 timeoutSec, timeoutNs, millisThreshold);
184         int ready = epollReady(result);
185         if (ready < 0) {
186             throw newIOException("epoll_wait", ready);
187         }
188         return result;
189     }
190 
191     // IMPORTANT: This needs to be consistent with what is used in netty_epoll_native.c
192     static int epollReady(long result) {
193         return (int) (result >> 32);
194     }
195 
196     // IMPORTANT: This needs to be consistent with what is used in netty_epoll_native.c
197     static boolean epollTimerWasUsed(long result) {
198         return (result & 0xff) != 0;
199     }
200 
201     static int epollWait(FileDescriptor epollFd, EpollEventArray events, boolean immediatePoll) throws IOException {
202         return epollWait(epollFd, events, immediatePoll ? 0 : -1);
203     }
204 
205     /**
206      * This uses epoll's own timeout and does not reset/re-arm any timerfd
207      */
208     static int epollWait(FileDescriptor epollFd, EpollEventArray events, int timeoutMillis) throws IOException {
209         int ready = epollWait(epollFd.intValue(), events.memoryAddress(), events.length(), timeoutMillis);
210         if (ready < 0) {
211             throw newIOException("epoll_wait", ready);
212         }
213         return ready;
214     }
215 
216     /**
217      * Non-blocking variant of
218      * {@link #epollWait(FileDescriptor, EpollEventArray, FileDescriptor, int, int)}
219      * that will also hint to processor we are in a busy-wait loop.
220      */
221     public static int epollBusyWait(FileDescriptor epollFd, EpollEventArray events) throws IOException {
222         int ready = epollBusyWait0(epollFd.intValue(), events.memoryAddress(), events.length());
223         if (ready < 0) {
224             throw newIOException("epoll_wait", ready);
225         }
226         return ready;
227     }
228 
229     private static native long epollWait0(
230             int efd, long address, int len, int timerFd, int timeoutSec, int timeoutNs, long millisThreshold);
231     private static native int epollWait(int efd, long address, int len, int timeout);
232     private static native int epollBusyWait0(int efd, long address, int len);
233 
234     public static void epollCtlAdd(int efd, final int fd, final int flags) throws IOException {
235         int res = epollCtlAdd0(efd, fd, flags);
236         if (res < 0) {
237             throw newIOException("epoll_ctl", res);
238         }
239     }
240     private static native int epollCtlAdd0(int efd, int fd, int flags);
241 
242     public static void epollCtlMod(int efd, final int fd, final int flags) throws IOException {
243         int res = epollCtlMod0(efd, fd, flags);
244         if (res < 0) {
245             throw newIOException("epoll_ctl", res);
246         }
247     }
248     private static native int epollCtlMod0(int efd, int fd, int flags);
249 
250     public static void epollCtlDel(int efd, final int fd) throws IOException {
251         int res = epollCtlDel0(efd, fd);
252         if (res < 0) {
253             throw newIOException("epoll_ctl", res);
254         }
255     }
256     private static native int epollCtlDel0(int efd, int fd);
257 
258     // File-descriptor operations
259     public static int splice(int fd, long offIn, int fdOut, long offOut, long len) throws IOException {
260         int res = splice0(fd, offIn, fdOut, offOut, len);
261         if (res >= 0) {
262             return res;
263         }
264         return ioResult("splice", res);
265     }
266 
267     private static native int splice0(int fd, long offIn, int fdOut, long offOut, long len);
268 
269     @Deprecated
270     public static int sendmmsg(int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
271                                int offset, int len) throws IOException {
272         return sendmmsg(fd, Socket.isIPv6Preferred(), msgs, offset, len);
273     }
274 
275     static int sendmmsg(int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
276                                int offset, int len) throws IOException {
277         int res = sendmmsg0(fd, ipv6, msgs, offset, len);
278         if (res >= 0) {
279             return res;
280         }
281         return ioResult("sendmmsg", res);
282     }
283 
284     private static native int sendmmsg0(
285             int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
286 
287     static int recvmmsg(int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
288                         int offset, int len) throws IOException {
289         int res = recvmmsg0(fd, ipv6, msgs, offset, len);
290         if (res >= 0) {
291             return res;
292         }
293         return ioResult("recvmmsg", res);
294     }
295 
296     private static native int recvmmsg0(
297             int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
298 
299     static int recvmsg(int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket packet) throws IOException {
300         int res = recvmsg0(fd, ipv6, packet);
301         if (res >= 0) {
302             return res;
303         }
304         return ioResult("recvmsg", res);
305     }
306 
307     private static native int recvmsg0(
308             int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket msg);
309 
310     // epoll_event related
311     public static native int sizeofEpollEvent();
312     public static native int offsetofEpollData();
313 
314     private static void loadNativeLibrary() {
315         String name = PlatformDependent.normalizedOs();
316         if (!"linux".equals(name)) {
317             throw new IllegalStateException("Only supported on Linux");
318         }
319         String staticLibName = "netty_transport_native_epoll";
320         String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
321         ClassLoader cl = PlatformDependent.getClassLoader(Native.class);
322         try {
323             NativeLibraryLoader.load(sharedLibName, cl);
324         } catch (UnsatisfiedLinkError e1) {
325             try {
326                 NativeLibraryLoader.load(staticLibName, cl);
327                 logger.debug("Failed to load {}", sharedLibName, e1);
328             } catch (UnsatisfiedLinkError e2) {
329                 ThrowableUtil.addSuppressed(e1, e2);
330                 throw e1;
331             }
332         }
333     }
334 
335     private Native() {
336         // utility
337     }
338 }