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.unix.Errors.NativeIoException;
19  import io.netty.channel.unix.FileDescriptor;
20  import io.netty.channel.unix.Socket;
21  import io.netty.util.internal.NativeLibraryLoader;
22  import io.netty.util.internal.PlatformDependent;
23  import io.netty.util.internal.SystemPropertyUtil;
24  import io.netty.util.internal.ThrowableUtil;
25  import io.netty.util.internal.logging.InternalLogger;
26  import io.netty.util.internal.logging.InternalLoggerFactory;
27  
28  import java.io.IOException;
29  import java.nio.channels.ClosedChannelException;
30  import java.util.Locale;
31  
32  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollerr;
33  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollet;
34  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollin;
35  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollout;
36  import static io.netty.channel.epoll.NativeStaticallyReferencedJniMethods.epollrdhup;
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.ERRNO_EPIPE_NEGATIVE;
42  import static io.netty.channel.unix.Errors.ioResult;
43  import static io.netty.channel.unix.Errors.newConnectionResetException;
44  import static io.netty.channel.unix.Errors.newIOException;
45  
46  /**
47   * Native helper methods
48   * <p><strong>Internal usage only!</strong>
49   * <p>Static members which call JNI methods must be defined in {@link NativeStaticallyReferencedJniMethods}.
50   */
51  public final class Native {
52      private static final InternalLogger logger = InternalLoggerFactory.getInstance(Native.class);
53  
54      static {
55          try {
56              // First, try calling a side-effect free JNI method to see if the library was already
57              // loaded by the application.
58              offsetofEpollData();
59          } catch (UnsatisfiedLinkError ignore) {
60              // The library was not previously loaded, load it now.
61              loadNativeLibrary();
62          }
63          Socket.initialize();
64      }
65  
66      // EventLoop operations and constants
67      public static final int EPOLLIN = epollin();
68      public static final int EPOLLOUT = epollout();
69      public static final int EPOLLRDHUP = epollrdhup();
70      public static final int EPOLLET = epollet();
71      public static final int EPOLLERR = epollerr();
72  
73      public static final boolean IS_SUPPORTING_SENDMMSG = isSupportingSendmmsg();
74      public static final boolean IS_SUPPORTING_TCP_FASTOPEN = isSupportingTcpFastopen();
75      public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
76      public static final String KERNEL_VERSION = kernelVersion();
77  
78      private static final NativeIoException SENDMMSG_CONNECTION_RESET_EXCEPTION;
79      private static final NativeIoException SPLICE_CONNECTION_RESET_EXCEPTION;
80      private static final ClosedChannelException SENDMMSG_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
81              new ClosedChannelException(), Native.class, "sendmmsg(...)");
82      private static final ClosedChannelException SPLICE_CLOSED_CHANNEL_EXCEPTION = ThrowableUtil.unknownStackTrace(
83              new ClosedChannelException(), Native.class, "splice(...)");
84  
85      static {
86          SENDMMSG_CONNECTION_RESET_EXCEPTION = newConnectionResetException("syscall:sendmmsg(...)",
87                  ERRNO_EPIPE_NEGATIVE);
88          SPLICE_CONNECTION_RESET_EXCEPTION = newConnectionResetException("syscall:splice(...)",
89                  ERRNO_EPIPE_NEGATIVE);
90      }
91  
92      public static FileDescriptor newEventFd() {
93          return new FileDescriptor(eventFd());
94      }
95  
96      public static FileDescriptor newTimerFd() {
97          return new FileDescriptor(timerFd());
98      }
99  
100     private static native int eventFd();
101     private static native int timerFd();
102     public static native void eventFdWrite(int fd, long value);
103     public static native void eventFdRead(int fd);
104     static native void timerFdRead(int fd);
105 
106     public static FileDescriptor newEpollCreate() {
107         return new FileDescriptor(epollCreate());
108     }
109 
110     private static native int epollCreate();
111 
112     public static int epollWait(FileDescriptor epollFd, EpollEventArray events, FileDescriptor timerFd,
113                                 int timeoutSec, int timeoutNs) throws IOException {
114         int ready = epollWait0(epollFd.intValue(), events.memoryAddress(), events.length(), timerFd.intValue(),
115                                timeoutSec, timeoutNs);
116         if (ready < 0) {
117             throw newIOException("epoll_wait", ready);
118         }
119         return ready;
120     }
121     private static native int epollWait0(int efd, long address, int len, int timerFd, int timeoutSec, int timeoutNs);
122 
123     /**
124      * Non-blocking variant of
125      * {@link #epollWait(FileDescriptor, EpollEventArray, FileDescriptor, int, int)}
126      * that will also hint to processor we are in a busy-wait loop.
127      */
128     public static int epollBusyWait(FileDescriptor epollFd, EpollEventArray events) throws IOException {
129         int ready = epollBusyWait0(epollFd.intValue(), events.memoryAddress(), events.length());
130         if (ready < 0) {
131             throw newIOException("epoll_wait", ready);
132         }
133         return ready;
134     }
135 
136     private static native int epollBusyWait0(int efd, long address, int len);
137 
138     public static void epollCtlAdd(int efd, final int fd, final int flags) throws IOException {
139         int res = epollCtlAdd0(efd, fd, flags);
140         if (res < 0) {
141             throw newIOException("epoll_ctl", res);
142         }
143     }
144     private static native int epollCtlAdd0(int efd, int fd, int flags);
145 
146     public static void epollCtlMod(int efd, final int fd, final int flags) throws IOException {
147         int res = epollCtlMod0(efd, fd, flags);
148         if (res < 0) {
149             throw newIOException("epoll_ctl", res);
150         }
151     }
152     private static native int epollCtlMod0(int efd, int fd, int flags);
153 
154     public static void epollCtlDel(int efd, final int fd) throws IOException {
155         int res = epollCtlDel0(efd, fd);
156         if (res < 0) {
157             throw newIOException("epoll_ctl", res);
158         }
159     }
160     private static native int epollCtlDel0(int efd, int fd);
161 
162     // File-descriptor operations
163     public static int splice(int fd, long offIn, int fdOut, long offOut, long len) throws IOException {
164         int res = splice0(fd, offIn, fdOut, offOut, len);
165         if (res >= 0) {
166             return res;
167         }
168         return ioResult("splice", res, SPLICE_CONNECTION_RESET_EXCEPTION, SPLICE_CLOSED_CHANNEL_EXCEPTION);
169     }
170 
171     private static native int splice0(int fd, long offIn, int fdOut, long offOut, long len);
172 
173     public static int sendmmsg(
174             int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len) throws IOException {
175         int res = sendmmsg0(fd, msgs, offset, len);
176         if (res >= 0) {
177             return res;
178         }
179         return ioResult("sendmmsg", res, SENDMMSG_CONNECTION_RESET_EXCEPTION, SENDMMSG_CLOSED_CHANNEL_EXCEPTION);
180     }
181 
182     private static native int sendmmsg0(
183             int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len);
184 
185     // epoll_event related
186     public static native int sizeofEpollEvent();
187     public static native int offsetofEpollData();
188 
189     private static void loadNativeLibrary() {
190         String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
191         if (!name.startsWith("linux")) {
192             throw new IllegalStateException("Only supported on Linux");
193         }
194         String staticLibName = "netty_transport_native_epoll";
195         String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
196         ClassLoader cl = PlatformDependent.getClassLoader(Native.class);
197         try {
198             NativeLibraryLoader.load(sharedLibName, cl);
199         } catch (UnsatisfiedLinkError e1) {
200             try {
201                 NativeLibraryLoader.load(staticLibName, cl);
202                 logger.debug("Failed to load {}", sharedLibName, e1);
203             } catch (UnsatisfiedLinkError e2) {
204                 ThrowableUtil.addSuppressed(e1, e2);
205                 throw e1;
206             }
207         }
208     }
209 
210     private Native() {
211         // utility
212     }
213 }