1 /* gnu_java_nio_channel_KqueueSelectorImpl.c --
2    Copyright (C) 2006  Free Software Foundation, Inc.
3 
4 This file is a part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 USA
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version.  */
37 
38 
39 #if HAVE_CONFIG_H
40 #include <config.h>
41 #endif /* HAVE_CONFIG_H */
42 
43 #include <sys/types.h>
44 #if HAVE_SYS_EVENT_H
45 #include <sys/event.h>
46 #endif /* HAVE_SYS_EVENT_H */
47 #include <sys/time.h>
48 #include <errno.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #include <jni.h>
53 #include <gnu_java_nio_KqueueSelectorImpl.h>
54 
55 #include <jcl.h>
56 
57 #define KEY_OP_ACCEPT  16
58 #define KEY_OP_CONNECT  8
59 #define KEY_OP_READ     1
60 #define KEY_OP_WRITE    4
61 
62 /* XXX this requires -std=gnu99 or c99 */
63 /* #ifdef TRACE_KQUEUE */
64 /* #define TRACE(fmt, ...) fprintf (stderr, "%s: " fmt "\n", __FUNCTION__, __VA_ARGS__); */
65 /* #else */
66 /* #define TRACE(fmt, ...) */
67 /* #endif */
68 
69 /* #define TRACE_KQUEUE 1 */
70 
71 
72 #define throw_not_supported(env) JCL_ThrowException (env, "java/lang/UnsupportedOperationException", "kqueue/kevent support not available")
73 
74 
75 /*
76  * Class:     gnu_java_nio_KqueueSelectorImpl
77  * Method:    kqueue_supported
78  * Signature: ()Z
79  */
80 JNIEXPORT jboolean JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_kqueue_1supported(JNIEnv * env,jclass clazz)81 Java_gnu_java_nio_KqueueSelectorImpl_kqueue_1supported (JNIEnv *env __attribute__((unused)),
82                                                         jclass clazz __attribute__((unused)))
83 {
84 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
85   return JNI_TRUE;
86 #else
87   return JNI_FALSE;
88 #endif /* HAVE_KQUEUE && HAVE_KEVENT */
89 }
90 
91 
92 /*
93  * Class:     gnu_java_nio_KqueueSelectorImpl
94  * Method:    sizeof_struct_kevent
95  * Signature: ()I
96  */
97 JNIEXPORT jint JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_sizeof_1struct_1kevent(JNIEnv * env,jclass clazz)98 Java_gnu_java_nio_KqueueSelectorImpl_sizeof_1struct_1kevent
99 (JNIEnv *env __attribute__((unused)), jclass clazz __attribute__((unused)))
100 {
101 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
102 /*   TRACE("return sizeof %lu", sizeof (struct kevent)); */
103   return sizeof (struct kevent);
104 #else
105   throw_not_supported (env);
106   return -1;
107 #endif /* HAVE_KQUEUE && HAVE_KEVENT */
108 }
109 
110 
111 /*
112  * Class:     gnu_java_nio_KqueueSelectorImpl
113  * Method:    implOpen
114  * Signature: ()I
115  */
116 JNIEXPORT jint JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_implOpen(JNIEnv * env,jclass clazz)117 Java_gnu_java_nio_KqueueSelectorImpl_implOpen
118 (JNIEnv *env, jclass clazz __attribute__((unused)))
119 {
120 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
121   int kq = kqueue ();
122 /*   TRACE("kqueue returns %d", kq); */
123   if (kq == -1)
124     JCL_ThrowException (env, "java/io/IOException", strerror (errno));
125   return kq;
126 #else
127   throw_not_supported (env);
128   return -1;
129 #endif
130 }
131 
132 
133 /*
134  * Class:     gnu_java_nio_KqueueSelectorImpl
135  * Method:    implClose
136  * Signature: (I)V
137  */
138 JNIEXPORT void JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_implClose(JNIEnv * env,jclass clazz,jint kq)139 Java_gnu_java_nio_KqueueSelectorImpl_implClose (JNIEnv *env,
140                                                 jclass clazz __attribute__((unused)),
141                                                 jint kq)
142 {
143 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
144 /*   TRACE("closing %d", kq); */
145   if (close (kq) != 0)
146     JCL_ThrowException (env, "java/io/IOException", strerror (errno));
147 #else
148   (void) kq;
149   throw_not_supported (env);
150 #endif /* HAVE_KQUEUE && HAVE_KEVENT */
151 }
152 
153 
154 /*
155  * Class:     gnu_java_nio_KqueueSelectorImpl
156  * Method:    kevent_set
157  * Signature: (Ljava/nio/ByteBuffer;IIIZ)V
158  */
159 JNIEXPORT void JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_kevent_1set(JNIEnv * env,jclass clazz,jobject nstate,jint i,jint fd,jint ops,jint active,jint key)160 Java_gnu_java_nio_KqueueSelectorImpl_kevent_1set (JNIEnv *env,
161                                                   jclass clazz __attribute__((unused)),
162                                                   jobject nstate, jint i, jint fd,
163                                                   jint ops, jint active, jint key)
164 {
165 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
166   struct kevent *kev;
167   short ident;
168 
169   kev = (struct kevent *) (*env)->GetDirectBufferAddress (env, nstate);
170 
171 #ifdef TRACE_KQUEUE
172   printf ("kevent_set fd:%d p:%p i:%d ops:%x active:%x key:%x\n",
173           fd, (void *) kev, i, ops, active, key);
174 #endif /* TRACE_KQUEUE */
175 
176   if (kev == NULL)
177     {
178       JCL_ThrowException (env, "java/lang/InternalError",
179                           "GetDirectBufferAddress returned NULL!");
180       return;
181     }
182 
183   ident = fd;
184   memset (&kev[i], 0, sizeof (struct kevent));
185 
186   if ((ops & KEY_OP_READ) || (ops & KEY_OP_ACCEPT))
187     {
188       /* Add event if it wasn't previously added. */
189       if (!(active & KEY_OP_READ) && !(active & KEY_OP_ACCEPT))
190         EV_SET(&kev[i], ident, EVFILT_READ, EV_ADD, 0, 0, (void *) key);
191     }
192   else
193     {
194       /* Delete event if it was previously added */
195       if ((active & KEY_OP_READ) || (active & KEY_OP_ACCEPT))
196         EV_SET(&kev[i], ident, EVFILT_READ, EV_DELETE, 0, 0, (void *) key);
197     }
198 
199   /* Do the same thing for the write filter. */
200   if ((ops & KEY_OP_WRITE) || (ops & KEY_OP_CONNECT))
201     {
202       if (!(active & KEY_OP_WRITE) && !(active & KEY_OP_CONNECT))
203         EV_SET(&kev[i], ident, EVFILT_WRITE, EV_ADD, 0, 0, (void *) key);
204     }
205   else
206     {
207       if ((active & KEY_OP_WRITE) || (active & KEY_OP_CONNECT))
208         EV_SET(&kev[i], ident, EVFILT_WRITE, EV_DELETE, 0, 0, (void *) key);
209     }
210 
211 #ifdef TRACE_KQUEUE
212   printf (" set kevent %2d:  ident:%u filter:%x flags:%o fflags:%o data:%p udata:%p\n",
213           i, (unsigned) kev[i].ident, kev[i].filter, kev[i].flags, kev[i].fflags,
214           (void *) kev[i].data, kev[i].udata);
215 #endif /* TRACE_KQUEUE */
216 #else
217   (void) nstate;
218   (void) i;
219   (void) fd;
220   (void) ops;
221   (void) key;
222   (void) active;
223   throw_not_supported (env);
224 #endif /* HAVE_KQUEUE && HAVE_KEVENT */
225 }
226 
227 
228 /*
229  * Class:     gnu_java_nio_KqueueSelectorImpl
230  * Method:    kevent
231  * Signature: (ILjava/nio/ByteBuffer;IJ)I
232  */
233 JNIEXPORT jint JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_kevent(JNIEnv * env,jobject this,jint kq,jobject nstate,jint nevents,jint maxevents,jlong timeout)234 Java_gnu_java_nio_KqueueSelectorImpl_kevent (JNIEnv *env,
235                                              jobject this __attribute__((unused)),
236                                              jint kq, jobject nstate, jint nevents,
237                                              jint maxevents, jlong timeout)
238 {
239 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
240   struct timespec tv;
241   struct timespec *t = NULL;
242   struct kevent *kev = (struct kevent *) (*env)->GetDirectBufferAddress (env, nstate);
243   int ret;
244 
245 #ifdef TRACE_KQUEUE
246   int i;
247 
248   printf ("[%d] kevent nevents:%d maxevents:%d timeout:%lld\n", kq, nevents, maxevents, timeout);
249   printf ("[%d] addding/deleting %d events\n", kq, nevents);
250   for (i = 0; i < nevents; i++)
251     {
252       printf ("[%d] kevent input [%d]: ident:%u filter:%x flags:%o fflags:%o data:%p udata:%p\n",
253               kq, i, (unsigned) kev[i].ident, kev[i].filter, kev[i].flags, kev[i].fflags,
254               (void *) kev[i].data, kev[i].udata);
255     }
256 #endif
257 
258 /*   TRACE("events: %p; nevents: %d; timeout: %lld", (void *) kev, nevents, timeout); */
259 
260   if (timeout != -1)
261     {
262       tv.tv_sec = timeout / 1000;
263       tv.tv_nsec = (timeout % 1000) * 1000;
264       t = &tv;
265     }
266 
267   ret = kevent (kq, (const struct kevent *) kev, nevents, kev, maxevents, t);
268 
269   if (ret == -1)
270     {
271       if (errno == EINTR)
272         ret = 0;
273       else
274         JCL_ThrowException (env, "java/io/IOException", strerror (errno));
275     }
276 
277 #ifdef TRACE_KQUEUE
278   for (i = 0; i < ret; i++)
279     {
280       printf ("[%d] kevent output [%d]: ident:%u filter:%x flags:%o fflags:%o data:%p udata:%p\n",
281               kq, i, (unsigned) kev[i].ident, kev[i].filter, kev[i].flags, kev[i].fflags,
282               (void *) kev[i].data, kev[i].udata);
283     }
284 #endif
285 
286   return ret;
287 #else
288   (void) kq;
289   (void) nstate;
290   (void) nevents;
291   (void) maxevents;
292   (void) timeout;
293   throw_not_supported (env);
294   return -1;
295 #endif /* HAVE_KQUEUE && HAVE_KEVENT */
296 }
297 
298 
299 /*
300  * Class:     gnu_java_nio_KqueueSelectorImpl
301  * Method:    fetch_key
302  * Signature: (Ljava/nio/ByteBuffer;)I;
303  */
304 JNIEXPORT jint JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_fetch_1key(JNIEnv * env,jclass clazz,jobject nstate)305 Java_gnu_java_nio_KqueueSelectorImpl_fetch_1key (JNIEnv *env,
306                                                  jclass clazz __attribute__((unused)),
307                                                  jobject nstate)
308 {
309 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
310   struct kevent *kev = (struct kevent *) (*env)->GetDirectBufferAddress (env, nstate);
311 /*   TRACE("return key %p\n", kev->udata); */
312   return (jint) kev->udata;
313 #else
314   (void) nstate;
315   throw_not_supported (env);
316   return -1;
317 #endif /* HAVE_KQUEUE && HAVE_KEVENT */
318 }
319 
320 
321 /*
322  * Class:     gnu_java_nio_KqueueSelectorImpl
323  * Method:    ready_ops
324  * Signature: (Ljava/nio/ByteBuffer;I)I
325  */
326 JNIEXPORT jint JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_ready_1ops(JNIEnv * env,jclass clazz,jobject nstate,jint interest)327 Java_gnu_java_nio_KqueueSelectorImpl_ready_1ops (JNIEnv *env,
328                                                  jclass clazz __attribute__((unused)),
329                                                  jobject nstate, jint interest)
330 {
331 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
332   struct kevent *kev = (struct kevent *) (*env)->GetDirectBufferAddress (env, nstate);
333   jint ready = 0;
334 
335   if ((kev->flags & EV_ERROR) == EV_ERROR)
336     {
337       printf ("!!! error selecting fd %d: %s", (int) (kev->ident), strerror ((int) (kev->data)));
338       return 0;
339     }
340 
341   /* We poll for READ for OP_READ and OP_ACCEPT. */
342   if (kev->filter == EVFILT_READ)
343     {
344       ready = (interest & KEY_OP_READ) | (interest & KEY_OP_ACCEPT);
345 /*       TRACE("filter EVFILT_READ. Ready ops set to %x", ready); */
346     }
347 
348   /* Poll for WRITE for OP_WRITE and OP_CONNECT; I guess we *should*
349      get a WRITE event if we are connected, but I don't know if we do
350      for real. FIXME */
351   if (kev->filter == EVFILT_WRITE)
352     {
353       ready = (interest & KEY_OP_WRITE) | (interest & KEY_OP_CONNECT);
354 /*       TRACE("filter EVFILT_WRITE. Ready ops set to %x", ready); */
355     }
356 
357   return ready;
358 #else
359   (void) nstate;
360   (void) interest;
361   throw_not_supported (env);
362   return -1;
363 #endif /* HAVE_KQUEUE && HAVE_KEVENT */
364 }
365 
366 
367 /*
368  * Class:     gnu_java_nio_KqueueSelectorImpl
369  * Method:    check_eof
370  * Signature: (Ljava/nio/ByteBuffer;)Z
371  */
372 JNIEXPORT jboolean JNICALL
Java_gnu_java_nio_KqueueSelectorImpl_check_1eof(JNIEnv * env,jclass clazz,jobject nstate)373 Java_gnu_java_nio_KqueueSelectorImpl_check_1eof (JNIEnv *env,
374                                                  jclass clazz __attribute__((unused)),
375                                                  jobject nstate)
376 {
377 #if defined(HAVE_KQUEUE) && defined(HAVE_KEVENT)
378   struct kevent *kev = (struct kevent *) (*env)->GetDirectBufferAddress (env, nstate);
379   if ((kev->flags & EV_EOF) == EV_EOF)
380     return JNI_TRUE;
381   return JNI_FALSE;
382 #else
383   (void) nstate;
384   throw_not_supported (env);
385   return JNI_FALSE;
386 #endif
387 }
388