1/*
2 * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26/*
27 * KQueueArrayWrapper.c
28 * Implementation of Selector using FreeBSD / Mac OS X kqueues
29 * Derived from Sun's DevPollArrayWrapper
30 */
31
32
33#include "jni.h"
34#include "jni_util.h"
35#include "jvm.h"
36#include "jlong.h"
37
38#include <sys/types.h>
39#include <sys/event.h>
40#include <sys/time.h>
41
42JNIEXPORT void JNICALL
43Java_sun_nio_ch_KQueueArrayWrapper_initStructSizes(JNIEnv *env, jclass clazz)
44{
45#define CHECK_EXCEPTION() { \
46    if ((*env)->ExceptionCheck(env)) { \
47        goto exceptionOccurred; \
48    } \
49}
50
51#define CHECK_ERROR_AND_EXCEPTION(_field) { \
52    if (_field == NULL) { \
53        goto badField; \
54    } \
55    CHECK_EXCEPTION(); \
56}
57
58
59    jfieldID field;
60
61    field = (*env)->GetStaticFieldID(env, clazz, "EVFILT_READ", "S");
62    CHECK_ERROR_AND_EXCEPTION(field);
63    (*env)->SetStaticShortField(env, clazz, field, EVFILT_READ);
64    CHECK_EXCEPTION();
65
66    field = (*env)->GetStaticFieldID(env, clazz, "EVFILT_WRITE", "S");
67    CHECK_ERROR_AND_EXCEPTION(field);
68    (*env)->SetStaticShortField(env, clazz, field, EVFILT_WRITE);
69    CHECK_EXCEPTION();
70
71    field = (*env)->GetStaticFieldID(env, clazz, "SIZEOF_KEVENT", "S");
72    CHECK_ERROR_AND_EXCEPTION(field);
73    (*env)->SetStaticShortField(env, clazz, field, (short) sizeof(struct kevent));
74    CHECK_EXCEPTION();
75
76    field = (*env)->GetStaticFieldID(env, clazz, "FD_OFFSET", "S");
77    CHECK_ERROR_AND_EXCEPTION(field);
78    (*env)->SetStaticShortField(env, clazz, field, (short) offsetof(struct kevent, ident));
79    CHECK_EXCEPTION();
80
81    field = (*env)->GetStaticFieldID(env, clazz, "FILTER_OFFSET", "S");
82    CHECK_ERROR_AND_EXCEPTION(field);
83    (*env)->SetStaticShortField(env, clazz, field, (short) offsetof(struct kevent, filter));
84    CHECK_EXCEPTION();
85    return;
86
87badField:
88    return;
89
90exceptionOccurred:
91    return;
92
93#undef CHECK_EXCEPTION
94#undef CHECK_ERROR_AND_EXCEPTION
95}
96
97JNIEXPORT jint JNICALL
98Java_sun_nio_ch_KQueueArrayWrapper_init(JNIEnv *env, jobject this)
99{
100    int kq = kqueue();
101    if (kq < 0) {
102        JNU_ThrowIOExceptionWithLastError(env, "KQueueArrayWrapper: kqueue() failed");
103    }
104    return kq;
105}
106
107
108JNIEXPORT void JNICALL
109Java_sun_nio_ch_KQueueArrayWrapper_register0(JNIEnv *env, jobject this,
110                                             jint kq, jint fd, jint r, jint w)
111{
112    struct kevent changes[2];
113    struct kevent errors[2];
114    struct timespec dontBlock = {0, 0};
115
116    // if (r) then { register for read } else { unregister for read }
117    // if (w) then { register for write } else { unregister for write }
118    // Ignore errors - they're probably complaints about deleting non-
119    //   added filters - but provide an error array anyway because
120    //   kqueue behaves erratically if some of its registrations fail.
121    EV_SET(&changes[0], fd, EVFILT_READ,  r ? EV_ADD : EV_DELETE, 0, 0, 0);
122    EV_SET(&changes[1], fd, EVFILT_WRITE, w ? EV_ADD : EV_DELETE, 0, 0, 0);
123    kevent(kq, changes, 2, errors, 2, &dontBlock);
124}
125
126JNIEXPORT jint JNICALL
127Java_sun_nio_ch_KQueueArrayWrapper_kevent0(JNIEnv *env, jobject this, jint kq,
128                                           jlong kevAddr, jint kevCount,
129                                           jlong timeout)
130{
131    struct kevent *kevs = (struct kevent *)jlong_to_ptr(kevAddr);
132    struct timespec ts;
133    struct timespec *tsp;
134    int result;
135
136    // Java timeout is in milliseconds. Convert to struct timespec.
137    // Java timeout == -1 : wait forever : timespec timeout of NULL
138    // Java timeout == 0  : return immediately : timespec timeout of zero
139    if (timeout >= 0) {
140        // For some indeterminate reason kevent(2) has been found to fail with
141        // an EINVAL error for timeout values greater than or equal to
142        // 100000001000L. To avoid this problem, clamp the timeout arbitrarily
143        // to the maximum value of a 32-bit signed integer which is
144        // approximately 25 days in milliseconds.
145        const jlong timeoutMax = 0x7fffffff; // java.lang.Integer.MAX_VALUE
146        if (timeout > timeoutMax) {
147            timeout = timeoutMax;
148        }
149        ts.tv_sec = timeout / 1000;
150        ts.tv_nsec = (timeout % 1000) * 1000000; //nanosec = 1 million millisec
151        tsp = &ts;
152    } else {
153        tsp = NULL;
154    }
155
156    result = kevent(kq, NULL, 0, kevs, kevCount, tsp);
157
158    if (result < 0) {
159        if (errno == EINTR) {
160            // ignore EINTR, pretend nothing was selected
161            result = 0;
162        } else {
163            JNU_ThrowIOExceptionWithLastError(env, "KQueueArrayWrapper: kqueue failed");
164        }
165    }
166
167    return result;
168}
169
170
171JNIEXPORT void JNICALL
172Java_sun_nio_ch_KQueueArrayWrapper_interrupt(JNIEnv *env, jclass cls, jint fd)
173{
174    char c = 1;
175    if (1 != write(fd, &c, 1)) {
176        JNU_ThrowIOExceptionWithLastError(env, "KQueueArrayWrapper: interrupt failed");
177    }
178}
179
180