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  *   http://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.unix.FileDescriptor;
19 import io.netty.channel.unix.Socket;
20 import io.netty.util.internal.NativeLibraryLoader;
21 import io.netty.util.internal.PlatformDependent;
22 import io.netty.util.internal.SystemPropertyUtil;
23 import io.netty.util.internal.ThrowableUtil;
24 import io.netty.util.internal.logging.InternalLogger;
25 import io.netty.util.internal.logging.InternalLoggerFactory;
26
27 import java.io.IOException;
28 import java.nio.channels.Selector;
29 import java.util.Locale;
30
31 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollerr;
32 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollet;
33 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollin;
34 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout;
35 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup;
36 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingRecvmmsg;
37 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingSendmmsg;
38 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingTcpFastopen;
39 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion;
40 import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.tcpMd5SigMaxKeyLen;
41 import static io.netty.channel.unix.Errors.ioResult;
42 import static io.netty.channel.unix.Errors.newIOException;
43
44 /**
45  * Native helper methods
46  * <p><strong>Internal usage only!</strong>
47  * <p>Static members which call JNI methods must be defined in {@link NativeStaticallyReferencedJniMethods}.
48  */

49 public final class Native {
50     private static final InternalLogger logger = InternalLoggerFactory.getInstance(Native.class);
51
52     static {
53         Selector selector = null;
54         try {
55             // We call Selector.open() as this will under the hood cause IOUtil to be loaded.
56             // This is a workaround for a possible classloader deadlock that could happen otherwise:
57             //
58             // See https://github.com/netty/netty/issues/10187
59             selector = Selector.open();
60         } catch (IOException ignore) {
61             // Just ignore
62         }
63         try {
64             // First, try calling a side-effect free JNI method to see if the library was already
65             // loaded by the application.
66             offsetofEpollData();
67         } catch (UnsatisfiedLinkError ignore) {
68             // The library was not previously loaded, load it now.
69             loadNativeLibrary();
70         } finally {
71             try {
72                 if (selector != null) {
73                     selector.close();
74                 }
75             } catch (IOException ignore) {
76                 // Just ignore
77             }
78         }
79         Socket.initialize();
80     }
81
82     // EventLoop operations and constants
83     public static final int EPOLLIN = epollin();
84     public static final int EPOLLOUT = epollout();
85     public static final int EPOLLRDHUP = epollrdhup();
86     public static final int EPOLLET = epollet();
87     public static final int EPOLLERR = epollerr();
88
89     public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg();
90     static final boolean IS_SUPPORTING_RECVMMSG = isSupportingRecvmmsg();
91
92     public static final boolean IS_SUPPORTING_TCP_FASTOPEN = isSupportingTcpFastopen();
93     public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
94     public static final String KERNEL_VERSION = kernelVersion();
95
96     public static FileDescriptor newEventFd() {
97         return new FileDescriptor(eventFd());
98     }
99
100     public static FileDescriptor newTimerFd() {
101         return new FileDescriptor(timerFd());
102     }
103
104     private static native int eventFd();
105     private static native int timerFd();
106     public static native void eventFdWrite(int fd, long value);
107     public static native void eventFdRead(int fd);
108     static native void timerFdRead(int fd);
109     static native void timerFdSetTime(int fd, int sec, int nsec) throws IOException;
110
111     public static FileDescriptor newEpollCreate() {
112         return new FileDescriptor(epollCreate());
113     }
114
115     private static native int epollCreate();
116
117     /**
118      * @deprecated this method is no longer supported. This functionality is internal to this package.
119      */

120     @Deprecated
121     public static int epollWait(FileDescriptor epollFd, EpollEventArray events, FileDescriptor timerFd,
122                                 int timeoutSec, int timeoutNs) throws IOException {
123         if (timeoutSec == 0 && timeoutNs == 0) {
124             // Zero timeout => poll (aka return immediately)
125             return epollWait(epollFd, events, 0);
126         }
127         if (timeoutSec == Integer.MAX_VALUE) {
128             // Max timeout => wait indefinitely: disarm timerfd first
129             timeoutSec = 0;
130             timeoutNs = 0;
131         }
132         int ready = epollWait0(epollFd.intValue(), events.memoryAddress(), events.length(), timerFd.intValue(),
133                                timeoutSec, timeoutNs);
134         if (ready < 0) {
135             throw newIOException("epoll_wait", ready);
136         }
137         return ready;
138     }
139
140     static int epollWait(FileDescriptor epollFd, EpollEventArray events, boolean immediatePoll) throws IOException {
141         return epollWait(epollFd, events, immediatePoll ? 0 : -1);
142     }
143
144     /**
145      * This uses epoll's own timeout and does not reset/re-arm any timerfd
146      */

147     static int epollWait(FileDescriptor epollFd, EpollEventArray events, int timeoutMillis) throws IOException {
148         int ready = epollWait(epollFd.intValue(), events.memoryAddress(), events.length(), timeoutMillis);
149         if (ready < 0) {
150             throw newIOException("epoll_wait", ready);
151         }
152         return ready;
153     }
154
155     /**
156      * Non-blocking variant of
157      * {@link #epollWait(FileDescriptor, EpollEventArray, FileDescriptor, intint)}
158      * that will also hint to processor we are in a busy-wait loop.
159      */

160     public static int epollBusyWait(FileDescriptor epollFd, EpollEventArray events) throws IOException {
161         int ready = epollBusyWait0(epollFd.intValue(), events.memoryAddress(), events.length());
162         if (ready < 0) {
163             throw newIOException("epoll_wait", ready);
164         }
165         return ready;
166     }
167
168     private static native int epollWait0(int efd, long address, int len, int timerFd, int timeoutSec, int timeoutNs);
169     private static native int epollWait(int efd, long address, int len, int timeout);
170     private static native int epollBusyWait0(int efd, long address, int len);
171
172     public static void epollCtlAdd(int efd, final int fd, final int flags) throws IOException {
173         int res = epollCtlAdd0(efd, fd, flags);
174         if (res < 0) {
175             throw newIOException("epoll_ctl", res);
176         }
177     }
178     private static native int epollCtlAdd0(int efd, int fd, int flags);
179
180     public static void epollCtlMod(int efd, final int fd, final int flags) throws IOException {
181         int res = epollCtlMod0(efd, fd, flags);
182         if (res < 0) {
183             throw newIOException("epoll_ctl", res);
184         }
185     }
186     private static native int epollCtlMod0(int efd, int fd, int flags);
187
188     public static void epollCtlDel(int efd, final int fd) throws IOException {
189         int res = epollCtlDel0(efd, fd);
190         if (res < 0) {
191             throw newIOException("epoll_ctl", res);
192         }
193     }
194     private static native int epollCtlDel0(int efd, int fd);
195
196     // File-descriptor operations
197     public static int splice(int fd, long offIn, int fdOut, long offOut, long len) throws IOException {
198         int res = splice0(fd, offIn, fdOut, offOut, len);
199         if (res >= 0) {
200             return res;
201         }
202         return ioResult("splice", res);
203     }
204
205     private static native int splice0(int fd, long offIn, int fdOut, long offOut, long len);
206
207     @Deprecated
208     public static int sendmmsg(int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
209                                int offset, int len) throws IOException {
210         return sendmmsg(fd, Socket.isIPv6Preferred(), msgs, offset, len);
211     }
212
213     static int sendmmsg(int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
214                                int offset, int len) throws IOException {
215         int res = sendmmsg0(fd, ipv6, msgs, offset, len);
216         if (res >= 0) {
217             return res;
218         }
219         return ioResult("sendmmsg", res);
220     }
221
222     private static native int sendmmsg0(
223             int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
224
225     static int recvmmsg(int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs,
226                         int offset, int len) throws IOException {
227         int res = recvmmsg0(fd, ipv6, msgs, offset, len);
228         if (res >= 0) {
229             return res;
230         }
231         return ioResult("recvmmsg", res);
232     }
233
234     private static native int recvmmsg0(
235             int fd, boolean ipv6, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
236
237     // epoll_event related
238     public static native int sizeofEpollEvent();
239     public static native int offsetofEpollData();
240
241     private static void loadNativeLibrary() {
242         String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
243         if (!name.startsWith("linux")) {
244             throw new IllegalStateException("Only supported on Linux");
245         }
246         String staticLibName = "netty_transport_native_epoll";
247         String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
248         ClassLoader cl = PlatformDependent.getClassLoader(Native.class);
249         try {
250             NativeLibraryLoader.load(sharedLibName, cl);
251         } catch (UnsatisfiedLinkError e1) {
252             try {
253                 NativeLibraryLoader.load(staticLibName, cl);
254                 logger.debug("Failed to load {}", sharedLibName, e1);
255             } catch (UnsatisfiedLinkError e2) {
256                 ThrowableUtil.addSuppressed(e1, e2);
257                 throw e1;
258             }
259         }
260     }
261
262     private Native() {
263         // utility
264     }
265 }
266