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