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    *   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.DefaultFileRegion;
19  import io.netty.channel.unix.Errors.NativeIoException;
20  import io.netty.channel.unix.NativeInetAddress;
21  import io.netty.channel.unix.Socket;
22  import io.netty.util.internal.NativeLibraryLoader;
23  import io.netty.util.internal.PlatformDependent;
24  import io.netty.util.internal.SystemPropertyUtil;
25  import io.netty.channel.unix.FileDescriptor;
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.nio.channels.ClosedChannelException;
33  import java.util.Locale;
34  
35  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollerr;
36  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollet;
37  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollin;
38  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout;
39  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup;
40  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.iovMax;
41  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingSendmmsg;
42  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.isSupportingTcpFastopen;
43  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.kernelVersion;
44  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.ssizeMax;
45  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.tcpMd5SigMaxKeyLen;
46  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.uioMaxIov;
47  import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
48  import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE;
49  import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
50  import static io.netty.channel.unix.Errors.ioResult;
51  import static io.netty.channel.unix.Errors.newConnectionResetException;
52  import static io.netty.channel.unix.Errors.newIOException;
53  
54  /**
55   * Native helper methods
56   * <p><strong>Internal usage only!</strong>
57   * <p>Static members which call JNI methods must be defined in {@link NativeStaticallyReferencedJniMethods}.
58   */
59  public final class Native {
60      private static final InternalLogger logger = InternalLoggerFactory.getInstance(Native.class);
61  
62      static {
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          }
71          Socket.initialize();
72      }
73  
74      // EventLoop operations and constants
75      public static final int EPOLLIN = epollin();
76      public static final int EPOLLOUT = epollout();
77      public static final int EPOLLRDHUP = epollrdhup();
78      public static final int EPOLLET = epollet();
79      public static final int EPOLLERR = epollerr();
80  
81      public static final int IOV_MAX = iovMax();
82      public static final int UIO_MAX_IOV = uioMaxIov();
83      public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg();
84      public static final boolean IS_SUPPORTING_TCP_FASTOPEN = isSupportingTcpFastopen();
85      public static final long SSIZE_MAX = ssizeMax();
86      public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
87      public static final String KERNEL_VERSION = kernelVersion();
88  
89      private static final NativeIoException SENDFILE_CONNECTION_RESET_EXCEPTION;
90      private static final NativeIoException SENDMMSG_CONNECTION_RESET_EXCEPTION;
91      private static final NativeIoException SPLICE_CONNECTION_RESET_EXCEPTION;
92      private static final ClosedChannelException SENDFILE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
93              new ClosedChannelException(), Native.class, "sendfile(...)");
94      private static final ClosedChannelException SENDMMSG_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
95              new ClosedChannelException(), Native.class, "sendmmsg(...)");
96      private static final ClosedChannelException SPLICE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
97              new ClosedChannelException(), Native.class, "splice(...)");
98  
99      static {
100         SENDFILE_CONNECTION_RESET_EXCEPTION = newConnectionResetException("syscall:sendfile(...)",
101                 ERRNO_EPIPE_NEGATIVE);
102         SENDMMSG_CONNECTION_RESET_EXCEPTION = newConnectionResetException("syscall:sendmmsg(...)",
103                 ERRNO_EPIPE_NEGATIVE);
104         SPLICE_CONNECTION_RESET_EXCEPTION = newConnectionResetException("syscall:splice(...)",
105                 ERRNO_EPIPE_NEGATIVE);
106     }
107 
108     public static FileDescriptor newEventFd() {
109         return new FileDescriptor(eventFd());
110     }
111 
112     public static FileDescriptor newTimerFd() {
113         return new FileDescriptor(timerFd());
114     }
115 
116     private static native int eventFd();
117     private static native int timerFd();
118     public static native void eventFdWrite(int fd, long value);
119     public static native void eventFdRead(int fd);
120     static native void timerFdRead(int fd);
121 
122     public static FileDescriptor newEpollCreate() {
123         return new FileDescriptor(epollCreate());
124     }
125 
126     private static native int epollCreate();
127 
128     public static int epollWait(FileDescriptor epollFd, EpollEventArray events, FileDescriptor timerFd,
129                                 int timeoutSec, int timeoutNs) throws IOException {
130         int ready = epollWait0(epollFd.intValue(), events.memoryAddress(), events.length(), timerFd.intValue(),
131                                timeoutSec, timeoutNs);
132         if (ready < 0) {
133             throw newIOException("epoll_wait", ready);
134         }
135         return ready;
136     }
137     private static native int epollWait0(int efd, long address, int len, int timerFd, int timeoutSec, int timeoutNs);
138 
139     public static void epollCtlAdd(int efd, final int fd, final int flags) throws IOException {
140         int res = epollCtlAdd0(efd, fd, flags);
141         if (res < 0) {
142             throw newIOException("epoll_ctl", res);
143         }
144     }
145     private static native int epollCtlAdd0(int efd, final int fd, final int flags);
146 
147     public static void epollCtlMod(int efd, final int fd, final int flags) throws IOException {
148         int res = epollCtlMod0(efd, fd, flags);
149         if (res < 0) {
150             throw newIOException("epoll_ctl", res);
151         }
152     }
153     private static native int epollCtlMod0(int efd, final int fd, final int flags);
154 
155     public static void epollCtlDel(int efd, final int fd) throws IOException {
156         int res = epollCtlDel0(efd, fd);
157         if (res < 0) {
158             throw newIOException("epoll_ctl", res);
159         }
160     }
161     private static native int epollCtlDel0(int efd, final int fd);
162 
163     // File-descriptor operations
164     public static int splice(int fd, long offIn, int fdOut, long offOut, long len) throws IOException {
165         int res = splice0(fd, offIn, fdOut, offOut, len);
166         if (res >= 0) {
167             return res;
168         }
169         return ioResult("splice", res, SPLICE_CONNECTION_RESET_EXCEPTION, SPLICE_CLOSED_CHANNEL_EXCEPTION);
170     }
171 
172     private static native int splice0(int fd, long offIn, int fdOut, long offOut, long len);
173 
174     public static long sendfile(
175             int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException {
176         // Open the file-region as it may be created via the lazy constructor. This is needed as we directly access
177         // the FileChannel field directly via JNI
178         src.open();
179 
180         long res = sendfile0(dest, src, baseOffset, offset, length);
181         if (res >= 0) {
182             return res;
183         }
184         return ioResult("sendfile", (int) res, SENDFILE_CONNECTION_RESET_EXCEPTION, SENDFILE_CLOSED_CHANNEL_EXCEPTION);
185     }
186 
187     private static native long sendfile0(
188             int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException;
189 
190     public static int sendmmsg(
191             int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len) throws IOException {
192         int res = sendmmsg0(fd, msgs, offset, len);
193         if (res >= 0) {
194             return res;
195         }
196         return ioResult("sendmmsg", res, SENDMMSG_CONNECTION_RESET_EXCEPTION, SENDMMSG_CLOSED_CHANNEL_EXCEPTION);
197     }
198 
199     private static native int sendmmsg0(
200             int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
201 
202     public static int recvFd(int fd) throws IOException {
203         int res = recvFd0(fd);
204         if (res > 0) {
205             return res;
206         }
207         if (res == 0) {
208             return -1;
209         }
210 
211         if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
212             // Everything consumed so just return -1 here.
213             return 0;
214         }
215         throw newIOException("recvFd", res);
216     }
217 
218     private static native int recvFd0(int fd);
219 
220     public static int sendFd(int socketFd, int fd) throws IOException {
221         int res = sendFd0(socketFd, fd);
222         if (res >= 0) {
223             return res;
224         }
225         if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
226             // Everything consumed so just return -1 here.
227             return -1;
228         }
229         throw newIOException("sendFd", res);
230     }
231 
232     private static native int sendFd0(int socketFd, int fd);
233 
234     // Socket option operations
235     public static native int isReuseAddress(int fd) throws IOException;
236     public static native int isReusePort(int fd) throws IOException;
237     public static native int getTcpNotSentLowAt(int fd) throws IOException;
238     public static native int getTrafficClass(int fd) throws IOException;
239     public static native int isBroadcast(int fd) throws IOException;
240     public static native int getTcpKeepIdle(int fd) throws IOException;
241     public static native int getTcpKeepIntvl(int fd) throws IOException;
242     public static native int getTcpKeepCnt(int fd) throws IOException;
243     public static native int getTcpUserTimeout(int milliseconds) throws IOException;
244     public static native int isIpFreeBind(int fd)throws IOException;
245 
246     public static native void setReuseAddress(int fd, int reuseAddress) throws IOException;
247     public static native void setReusePort(int fd, int reuseAddress) throws IOException;
248     public static native void setTcpFastopen(int fd, int tcpFastopenBacklog) throws IOException;
249     public static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt) throws IOException;
250     public static native void setTrafficClass(int fd, int tcpNoDelay) throws IOException;
251     public static native void setBroadcast(int fd, int broadcast) throws IOException;
252     public static native void setTcpKeepIdle(int fd, int seconds) throws IOException;
253     public static native void setTcpKeepIntvl(int fd, int seconds) throws IOException;
254     public static native void setTcpKeepCnt(int fd, int probes) throws IOException;
255     public static native void setTcpUserTimeout(int fd, int milliseconds)throws IOException;
256     public static native void setIpFreeBind(int fd, int freeBind) throws IOException;
257     public static void tcpInfo(int fd, EpollTcpInfo info) throws IOException {
258         tcpInfo0(fd, info.info);
259     }
260 
261     private static native void tcpInfo0(int fd, long[] array) throws IOException;
262 
263     public static void setTcpMd5Sig(int fd, InetAddress address, byte[] key) throws IOException {
264         final NativeInetAddress a = NativeInetAddress.newInstance(address);
265         setTcpMd5Sig0(fd, a.address(), a.scopeId(), key);
266     }
267 
268     private static native void setTcpMd5Sig0(int fd, byte[] address, int scopeId, byte[] key) throws IOException;
269 
270     // epoll_event related
271     public static native int sizeofEpollEvent();
272     public static native int offsetofEpollData();
273 
274     private Native() {
275         // utility
276     }
277 
278     private static void loadNativeLibrary() {
279         String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
280         if (!name.startsWith("linux")) {
281             throw new IllegalStateException("Only supported on Linux");
282         }
283         String staticLibName = "netty_transport_native_epoll";
284         String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
285         ClassLoader cl = PlatformDependent.getClassLoader(Native.class);
286         try {
287             NativeLibraryLoader.load(sharedLibName, cl);
288         } catch (UnsatisfiedLinkError e1) {
289             try {
290                 NativeLibraryLoader.load(staticLibName, cl);
291                 logger.debug("Failed to load {}", sharedLibName, e1);
292             } catch (UnsatisfiedLinkError e2) {
293                 ThrowableUtil.addSuppressed(e1, e2);
294                 throw e1;
295             }
296         }
297     }
298 }