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