View Javadoc
1   /*
2    * Copyright 2016 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.kqueue;
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.Unix;
22  import io.netty.util.internal.ClassInitializerUtil;
23  import io.netty.util.internal.NativeLibraryLoader;
24  import io.netty.util.internal.PlatformDependent;
25  import io.netty.util.internal.ThrowableUtil;
26  import io.netty.util.internal.logging.InternalLogger;
27  import io.netty.util.internal.logging.InternalLoggerFactory;
28  
29  import java.io.IOException;
30  import java.net.InetAddress;
31  import java.net.UnknownHostException;
32  import java.nio.channels.FileChannel;
33  
34  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.connectDataIdempotent;
35  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.connectResumeOnReadWrite;
36  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evAdd;
37  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evClear;
38  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evDelete;
39  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evDisable;
40  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evEOF;
41  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evEnable;
42  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evError;
43  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltRead;
44  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltSock;
45  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltUser;
46  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.evfiltWrite;
47  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.fastOpenClient;
48  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.fastOpenServer;
49  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteConnReset;
50  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteDisconnected;
51  import static io.netty.channel.kqueue.KQueueStaticallyReferencedJniMethods.noteReadClosed;
52  import static io.netty.channel.unix.Errors.newIOException;
53  
54  /**
55   * Native helper methods
56   * <p><strong>Internal usage only!</strong>
57   */
58  final class Native {
59      private static final InternalLogger logger = InternalLoggerFactory.getInstance(Native.class);
60      static final InetAddress INET6_ANY;
61  
62      static {
63          // Preload all classes that will be used in the OnLoad(...) function of JNI to eliminate the possibility of a
64          // class-loader deadlock. This is a workaround for https://github.com/netty/netty/issues/11209.
65  
66          // This needs to match all the classes that are loaded via NETTY_JNI_UTIL_LOAD_CLASS or looked up via
67          // NETTY_JNI_UTIL_FIND_CLASS.
68          ClassInitializerUtil.tryLoadClasses(Native.class,
69                  // netty_kqueue_bsdsocket
70                  PeerCredentials.class, DefaultFileRegion.class, FileChannel.class, java.io.FileDescriptor.class
71          );
72  
73          try {
74              INET6_ANY = InetAddress.getByName("::");
75          } catch (UnknownHostException e) {
76              throw new ExceptionInInitializerError(e);
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              sizeofKEvent();
83          } catch (UnsatisfiedLinkError ignore) {
84              // The library was not previously loaded, load it now.
85              loadNativeLibrary();
86          }
87          Unix.registerInternal(new Runnable() {
88              @Override
89              public void run() {
90                  registerUnix();
91              }
92          });
93      }
94  
95      private static native int registerUnix();
96  
97      static final short EV_ADD = evAdd();
98      static final short EV_ENABLE = evEnable();
99      static final short EV_DISABLE = evDisable();
100     static final short EV_DELETE = evDelete();
101     static final short EV_CLEAR = evClear();
102     static final short EV_ERROR = evError();
103     static final short EV_EOF = evEOF();
104 
105     static final int NOTE_READCLOSED = noteReadClosed();
106     static final int NOTE_CONNRESET = noteConnReset();
107     static final int NOTE_DISCONNECTED = noteDisconnected();
108 
109     static final int NOTE_RDHUP = NOTE_READCLOSED | NOTE_CONNRESET | NOTE_DISCONNECTED;
110 
111     // Commonly used combinations of EV defines
112     static final short EV_ADD_ENABLE = (short) (EV_ADD | EV_ENABLE);
113     static final short EV_DELETE_DISABLE = (short) (EV_DELETE | EV_DISABLE);
114 
115     static final short EVFILT_READ = evfiltRead();
116     static final short EVFILT_WRITE = evfiltWrite();
117     static final short EVFILT_USER = evfiltUser();
118     static final short EVFILT_SOCK = evfiltSock();
119 
120     // Flags for connectx(2)
121     private static final int CONNECT_RESUME_ON_READ_WRITE = connectResumeOnReadWrite();
122     private static final int CONNECT_DATA_IDEMPOTENT = connectDataIdempotent();
123     static final int CONNECT_TCP_FASTOPEN = CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT;
124     static final boolean IS_SUPPORTING_TCP_FASTOPEN_CLIENT = isSupportingFastOpenClient();
125     static final boolean IS_SUPPORTING_TCP_FASTOPEN_SERVER = isSupportingFastOpenServer();
126 
127     static final KQueueIoOps READ_ENABLED_OPS =
128             KQueueIoOps.newOps(Native.EVFILT_READ, Native.EV_ADD_ENABLE, 0);
129     static final KQueueIoOps WRITE_ENABLED_OPS =
130             KQueueIoOps.newOps(Native.EVFILT_WRITE, Native.EV_ADD_ENABLE, 0);
131     static final KQueueIoOps READ_DISABLED_OPS =
132             KQueueIoOps.newOps(Native.EVFILT_READ, Native.EV_DELETE_DISABLE, 0);
133     static final KQueueIoOps WRITE_DISABLED_OPS =
134             KQueueIoOps.newOps(Native.EVFILT_WRITE, Native.EV_DELETE_DISABLE, 0);
135 
136     static FileDescriptor newKQueue() {
137         return new FileDescriptor(kqueueCreate());
138     }
139 
140     static int keventWait(int kqueueFd, KQueueEventArray changeList, KQueueEventArray eventList,
141                           int tvSec, int tvNsec) throws IOException {
142         int ready = keventWait(kqueueFd, changeList.memoryAddress(), changeList.size(),
143                                eventList.memoryAddress(), eventList.capacity(), tvSec, tvNsec);
144         if (ready < 0) {
145             throw newIOException("kevent", ready);
146         }
147         return ready;
148     }
149 
150     private static native int kqueueCreate();
151     private static native int keventWait(int kqueueFd, long changeListAddress, int changeListLength,
152                                          long eventListAddress, int eventListLength, int tvSec, int tvNsec);
153     static native int keventTriggerUserEvent(int kqueueFd, int ident);
154     static native int keventAddUserEvent(int kqueueFd, int ident);
155 
156     // kevent related
157     static native int sizeofKEvent();
158     static native int offsetofKEventIdent();
159     static native int offsetofKEventFlags();
160     static native int offsetofKEventFFlags();
161     static native int offsetofKEventFilter();
162     static native int offsetofKeventData();
163     static native int offsetofKeventUdata();
164 
165     private static void loadNativeLibrary() {
166         String name = PlatformDependent.normalizedOs();
167         if (!"osx".equals(name) && !name.contains("bsd")) {
168             throw new IllegalStateException("Only supported on OSX/BSD");
169         }
170         String staticLibName = "netty_transport_native_kqueue";
171         String sharedLibName = staticLibName + '_' + PlatformDependent.normalizedArch();
172         ClassLoader cl = PlatformDependent.getClassLoader(Native.class);
173         try {
174             NativeLibraryLoader.load(sharedLibName, cl);
175         } catch (UnsatisfiedLinkError e1) {
176             try {
177                 NativeLibraryLoader.load(staticLibName, cl);
178                 logger.debug("Failed to load {}", sharedLibName, e1);
179             } catch (UnsatisfiedLinkError e2) {
180                 ThrowableUtil.addSuppressed(e1, e2);
181                 throw e1;
182             }
183         }
184     }
185 
186     private static boolean isSupportingFastOpenClient() {
187         try {
188             return fastOpenClient() == 1;
189         } catch (Exception e) {
190             logger.debug("Failed to probe fastOpenClient sysctl, assuming client-side TCP FastOpen cannot be used.", e);
191         }
192         return false;
193     }
194 
195     private static boolean isSupportingFastOpenServer() {
196         try {
197             return fastOpenServer() == 1;
198         } catch (Exception e) {
199             logger.debug("Failed to probe fastOpenServer sysctl, assuming server-side TCP FastOpen cannot be used.", e);
200         }
201         return false;
202     }
203 
204     private Native() {
205         // utility
206     }
207 }