changeset 4902:5599aa5a4a51

7131697: (se) Need AsynchronousChannelProvider implementation for Mac OS X Reviewed-by: michaelm
author alanb
date Sat, 28 Jan 2012 11:37:15 +0000
parents 5780795f381e
children 789287639365
files make/java/nio/Makefile make/java/nio/mapfile-bsd src/solaris/classes/sun/nio/ch/BsdAsynchronousChannelProvider.java src/solaris/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java src/solaris/classes/sun/nio/ch/KQueue.java src/solaris/classes/sun/nio/ch/KQueuePort.java src/solaris/native/sun/nio/ch/KQueue.c src/solaris/native/sun/nio/ch/KQueuePort.c
diffstat 8 files changed, 737 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/make/java/nio/Makefile	Fri Jan 27 21:59:22 2012 +0300
+++ b/make/java/nio/Makefile	Sat Jan 28 11:37:15 2012 +0000
@@ -263,7 +263,10 @@
 ifeq ($(PLATFORM), macosx)
 FILES_java += \
         sun/nio/ch/AbstractPollSelectorImpl.java \
+	sun/nio/ch/BsdAsynchronousChannelProvider.java \
 	sun/nio/ch/InheritedChannel.java \
+	sun/nio/ch/KQueue.java \
+	sun/nio/ch/KQueuePort.java \
         sun/nio/ch/PollSelectorProvider.java \
         sun/nio/ch/PollSelectorImpl.java \
 	sun/nio/ch/Port.java \
@@ -306,10 +309,15 @@
 	GnomeFileTypeDetector.c \
 	BsdNativeDispatcher.c \
 	UnixCopyFile.c \
-	UnixNativeDispatcher.c
+	UnixNativeDispatcher.c \
+	\
+	KQueue.c \
+	KQueuePort.c
 
 FILES_export += \
 	sun/nio/ch/InheritedChannel.java \
+	sun/nio/ch/KQueue.java \
+	sun/nio/ch/KQueuePort.java \
 	sun/nio/ch/NativeThread.java \
 	sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java \
 	sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \
--- a/make/java/nio/mapfile-bsd	Fri Jan 27 21:59:22 2012 +0300
+++ b/make/java/nio/mapfile-bsd	Sat Jan 28 11:37:15 2012 +0000
@@ -73,6 +73,17 @@
                 Java_sun_nio_ch_IOUtil_makePipe;
                 Java_sun_nio_ch_IOUtil_randomBytes;
                 Java_sun_nio_ch_IOUtil_setfdVal;
+		Java_sun_nio_ch_KQueue_kqueue;
+		Java_sun_nio_ch_KQueue_keventRegister;
+		Java_sun_nio_ch_KQueue_keventPoll;
+		Java_sun_nio_ch_KQueue_keventSize;
+		Java_sun_nio_ch_KQueue_identOffset;
+		Java_sun_nio_ch_KQueue_filterOffset;
+		Java_sun_nio_ch_KQueue_flagsOffset;
+		Java_sun_nio_ch_KQueuePort_socketpair;
+		Java_sun_nio_ch_KQueuePort_interrupt;
+		Java_sun_nio_ch_KQueuePort_drain1;
+		Java_sun_nio_ch_KQueuePort_close0;
 		Java_sun_nio_ch_NativeThread_current;
 		Java_sun_nio_ch_NativeThread_init;
 		Java_sun_nio_ch_NativeThread_signal;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/sun/nio/ch/BsdAsynchronousChannelProvider.java	Sat Jan 28 11:37:15 2012 +0000
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.*;
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadFactory;
+import java.io.IOException;
+
+public class BsdAsynchronousChannelProvider
+    extends AsynchronousChannelProvider
+{
+    private static volatile KQueuePort defaultPort;
+
+    private KQueuePort defaultEventPort() throws IOException {
+        if (defaultPort == null) {
+            synchronized (BsdAsynchronousChannelProvider.class) {
+                if (defaultPort == null) {
+                    defaultPort = new KQueuePort(this, ThreadPool.getDefault()).start();
+                }
+            }
+        }
+        return defaultPort;
+    }
+
+    public BsdAsynchronousChannelProvider() {
+    }
+
+    @Override
+    public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory)
+        throws IOException
+    {
+        return new KQueuePort(this, ThreadPool.create(nThreads, factory)).start();
+    }
+
+    @Override
+    public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize)
+        throws IOException
+    {
+        return new KQueuePort(this, ThreadPool.wrap(executor, initialSize)).start();
+    }
+
+    private Port toPort(AsynchronousChannelGroup group) throws IOException {
+        if (group == null) {
+            return defaultEventPort();
+        } else {
+            if (!(group instanceof KQueuePort))
+                throw new IllegalChannelGroupException();
+            return (Port)group;
+        }
+    }
+
+    @Override
+    public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group)
+        throws IOException
+    {
+        return new UnixAsynchronousServerSocketChannelImpl(toPort(group));
+    }
+
+    @Override
+    public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group)
+        throws IOException
+    {
+        return new UnixAsynchronousSocketChannelImpl(toPort(group));
+    }
+}
--- a/src/solaris/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java	Fri Jan 27 21:59:22 2012 +0300
+++ b/src/solaris/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java	Sat Jan 28 11:37:15 2012 +0000
@@ -50,6 +50,8 @@
             return new SolarisAsynchronousChannelProvider();
         if (osname.equals("Linux"))
             return new LinuxAsynchronousChannelProvider();
+        if (osname.startsWith("Mac OS"))
+            return new BsdAsynchronousChannelProvider();
         throw new InternalError("platform not recognized");
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/sun/nio/ch/KQueue.java	Sat Jan 28 11:37:15 2012 +0000
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.io.IOException;
+import sun.misc.Unsafe;
+
+/**
+ * Provides access to the BSD kqueue facility.
+ */
+
+class KQueue {
+    private KQueue() { }
+
+    private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+    /**
+     * struct kevent {
+     *        uintptr_t       ident;          // identifier for this event, usually the fd
+     *        int16_t         filter;         // filter for event
+     *        uint16_t        flags;          // general flags
+     *        uint32_t        fflags;         // filter-specific flags
+     *        intptr_t        data;           // filter-specific data
+     *        void            *udata;         // opaque user data identifier
+     * };
+     */
+    private static final int SIZEOF_KQUEUEEVENT    = keventSize();
+    private static final int OFFSET_IDENT          = identOffset();
+    private static final int OFFSET_FILTER         = filterOffset();
+    private static final int OFFSET_FLAGS          = flagsOffset();
+
+    // filters
+    static final int EVFILT_READ  = -1;
+    static final int EVFILT_WRITE = -2;
+
+    // flags
+    static final int EV_ADD     = 0x0001;
+    static final int EV_ONESHOT = 0x0010;
+    static final int EV_CLEAR   = 0x0020;
+
+    /**
+     * Allocates a poll array to handle up to {@code count} events.
+     */
+    static long allocatePollArray(int count) {
+        return unsafe.allocateMemory(count * SIZEOF_KQUEUEEVENT);
+    }
+
+    /**
+     * Free a poll array
+     */
+    static void freePollArray(long address) {
+        unsafe.freeMemory(address);
+    }
+
+    /**
+     * Returns kevent[i].
+     */
+    static long getEvent(long address, int i) {
+        return address + (SIZEOF_KQUEUEEVENT*i);
+    }
+
+    /**
+     * Returns the file descriptor from a kevent (assuming to be in ident field)
+     */
+    static int getDescriptor(long address) {
+        return unsafe.getInt(address + OFFSET_IDENT);
+    }
+
+    static int getFilter(long address) {
+        return unsafe.getShort(address + OFFSET_FILTER);
+    }
+
+    static int getFlags(long address) {
+        return unsafe.getShort(address + OFFSET_FLAGS);
+    }
+
+    // -- Native methods --
+
+    private static native int keventSize();
+
+    private static native int identOffset();
+
+    private static native int filterOffset();
+
+    private static native int flagsOffset();
+
+    static native int kqueue() throws IOException;
+
+    static native int keventRegister(int kqpfd, int fd, int filter, int flags);
+
+    static native int keventPoll(int kqpfd, long pollAddress, int nevents)
+        throws IOException;
+
+    static {
+        Util.load();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/classes/sun/nio/ch/KQueuePort.java	Sat Jan 28 11:37:15 2012 +0000
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.ch;
+
+import java.nio.channels.spi.AsynchronousChannelProvider;
+import java.io.IOException;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.atomic.AtomicInteger;
+import static sun.nio.ch.KQueue.*;
+
+/**
+ * AsynchronousChannelGroup implementation based on the BSD kqueue facility.
+ */
+
+final class KQueuePort
+    extends Port
+{
+    // maximum number of events to poll at a time
+    private static final int MAX_KEVENTS_TO_POLL = 512;
+
+    // kqueue file descriptor
+    private final int kqfd;
+
+    // true if kqueue closed
+    private boolean closed;
+
+    // socket pair used for wakeup
+    private final int sp[];
+
+    // number of wakeups pending
+    private final AtomicInteger wakeupCount = new AtomicInteger();
+
+    // address of the poll array passed to kqueue_wait
+    private final long address;
+
+    // encapsulates an event for a channel
+    static class Event {
+        final PollableChannel channel;
+        final int events;
+
+        Event(PollableChannel channel, int events) {
+            this.channel = channel;
+            this.events = events;
+        }
+
+        PollableChannel channel()   { return channel; }
+        int events()                { return events; }
+    }
+
+    // queue of events for cases that a polling thread dequeues more than one
+    // event
+    private final ArrayBlockingQueue<Event> queue;
+    private final Event NEED_TO_POLL = new Event(null, 0);
+    private final Event EXECUTE_TASK_OR_SHUTDOWN = new Event(null, 0);
+
+    KQueuePort(AsynchronousChannelProvider provider, ThreadPool pool)
+        throws IOException
+    {
+        super(provider, pool);
+
+        // open kqueue
+        this.kqfd = kqueue();
+
+        // create socket pair for wakeup mechanism
+        int[] sv = new int[2];
+        try {
+            socketpair(sv);
+
+            // register one end with kqueue
+            keventRegister(kqfd, sv[0], EVFILT_READ, EV_ADD);
+        } catch (IOException x) {
+            close0(kqfd);
+            throw x;
+        }
+        this.sp = sv;
+
+        // allocate the poll array
+        this.address = allocatePollArray(MAX_KEVENTS_TO_POLL);
+
+        // create the queue and offer the special event to ensure that the first
+        // threads polls
+        this.queue = new ArrayBlockingQueue<Event>(MAX_KEVENTS_TO_POLL);
+        this.queue.offer(NEED_TO_POLL);
+    }
+
+    KQueuePort start() {
+        startThreads(new EventHandlerTask());
+        return this;
+    }
+
+    /**
+     * Release all resources
+     */
+    private void implClose() {
+        synchronized (this) {
+            if (closed)
+                return;
+            closed = true;
+        }
+        freePollArray(address);
+        close0(sp[0]);
+        close0(sp[1]);
+        close0(kqfd);
+    }
+
+    private void wakeup() {
+        if (wakeupCount.incrementAndGet() == 1) {
+            // write byte to socketpair to force wakeup
+            try {
+                interrupt(sp[1]);
+            } catch (IOException x) {
+                throw new AssertionError(x);
+            }
+        }
+    }
+
+    @Override
+    void executeOnHandlerTask(Runnable task) {
+        synchronized (this) {
+            if (closed)
+                throw new RejectedExecutionException();
+            offerTask(task);
+            wakeup();
+        }
+    }
+
+    @Override
+    void shutdownHandlerTasks() {
+        /*
+         * If no tasks are running then just release resources; otherwise
+         * write to the one end of the socketpair to wakeup any polling threads.
+         */
+        int nThreads = threadCount();
+        if (nThreads == 0) {
+            implClose();
+        } else {
+            // send interrupt to each thread
+            while (nThreads-- > 0) {
+                wakeup();
+            }
+        }
+    }
+
+    // invoked by clients to register a file descriptor
+    @Override
+    void startPoll(int fd, int events) {
+        // We use a separate filter for read and write events.
+        // TBD: Measure cost of EV_ONESHOT vs. EV_CLEAR, either will do here.
+        int err = 0;
+        int flags = (EV_ADD|EV_ONESHOT);
+        if ((events & Port.POLLIN) > 0)
+            err = keventRegister(kqfd, fd, EVFILT_READ, flags);
+        if (err == 0 && (events & Port.POLLOUT) > 0)
+            err = keventRegister(kqfd, fd, EVFILT_WRITE, flags);
+        if (err != 0)
+            throw new InternalError("kevent failed: " + err);  // should not happen
+    }
+
+    /*
+     * Task to process events from kqueue and dispatch to the channel's
+     * onEvent handler.
+     *
+     * Events are retreived from kqueue in batch and offered to a BlockingQueue
+     * where they are consumed by handler threads. A special "NEED_TO_POLL"
+     * event is used to signal one consumer to re-poll when all events have
+     * been consumed.
+     */
+    private class EventHandlerTask implements Runnable {
+        private Event poll() throws IOException {
+            try {
+                for (;;) {
+                    int n = keventPoll(kqfd, address, MAX_KEVENTS_TO_POLL);
+                    /*
+                     * 'n' events have been read. Here we map them to their
+                     * corresponding channel in batch and queue n-1 so that
+                     * they can be handled by other handler threads. The last
+                     * event is handled by this thread (and so is not queued).
+                     */
+                    fdToChannelLock.readLock().lock();
+                    try {
+                        while (n-- > 0) {
+                            long keventAddress = getEvent(address, n);
+                            int fd = getDescriptor(keventAddress);
+
+                            // wakeup
+                            if (fd == sp[0]) {
+                                if (wakeupCount.decrementAndGet() == 0) {
+                                    // no more wakeups so drain pipe
+                                    drain1(sp[0]);
+                                }
+
+                                // queue special event if there are more events
+                                // to handle.
+                                if (n > 0) {
+                                    queue.offer(EXECUTE_TASK_OR_SHUTDOWN);
+                                    continue;
+                                }
+                                return EXECUTE_TASK_OR_SHUTDOWN;
+                            }
+
+                            PollableChannel channel = fdToChannel.get(fd);
+                            if (channel != null) {
+                                int filter = getFilter(keventAddress);
+                                int events = 0;
+                                if (filter == EVFILT_READ)
+                                    events = Port.POLLIN;
+                                else if (filter == EVFILT_WRITE)
+                                    events = Port.POLLOUT;
+
+                                Event ev = new Event(channel, events);
+
+                                // n-1 events are queued; This thread handles
+                                // the last one except for the wakeup
+                                if (n > 0) {
+                                    queue.offer(ev);
+                                } else {
+                                    return ev;
+                                }
+                            }
+                        }
+                    } finally {
+                        fdToChannelLock.readLock().unlock();
+                    }
+                }
+            } finally {
+                // to ensure that some thread will poll when all events have
+                // been consumed
+                queue.offer(NEED_TO_POLL);
+            }
+        }
+
+        public void run() {
+            Invoker.GroupAndInvokeCount myGroupAndInvokeCount =
+                Invoker.getGroupAndInvokeCount();
+            final boolean isPooledThread = (myGroupAndInvokeCount != null);
+            boolean replaceMe = false;
+            Event ev;
+            try {
+                for (;;) {
+                    // reset invoke count
+                    if (isPooledThread)
+                        myGroupAndInvokeCount.resetInvokeCount();
+
+                    try {
+                        replaceMe = false;
+                        ev = queue.take();
+
+                        // no events and this thread has been "selected" to
+                        // poll for more.
+                        if (ev == NEED_TO_POLL) {
+                            try {
+                                ev = poll();
+                            } catch (IOException x) {
+                                x.printStackTrace();
+                                return;
+                            }
+                        }
+                    } catch (InterruptedException x) {
+                        continue;
+                    }
+
+                    // handle wakeup to execute task or shutdown
+                    if (ev == EXECUTE_TASK_OR_SHUTDOWN) {
+                        Runnable task = pollTask();
+                        if (task == null) {
+                            // shutdown request
+                            return;
+                        }
+                        // run task (may throw error/exception)
+                        replaceMe = true;
+                        task.run();
+                        continue;
+                    }
+
+                    // process event
+                    try {
+                        ev.channel().onEvent(ev.events(), isPooledThread);
+                    } catch (Error x) {
+                        replaceMe = true; throw x;
+                    } catch (RuntimeException x) {
+                        replaceMe = true; throw x;
+                    }
+                }
+            } finally {
+                // last handler to exit when shutdown releases resources
+                int remaining = threadExit(this, replaceMe);
+                if (remaining == 0 && isShutdown()) {
+                    implClose();
+                }
+            }
+        }
+    }
+
+    // -- Native methods --
+
+    private static native void socketpair(int[] sv) throws IOException;
+
+    private static native void interrupt(int fd) throws IOException;
+
+    private static native void drain1(int fd) throws IOException;
+
+    private static native void close0(int fd);
+
+    static {
+        Util.load();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/sun/nio/ch/KQueue.c	Sat Jan 28 11:37:15 2012 +0000
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+#include "nio_util.h"
+
+#include "sun_nio_ch_KQueue.h"
+
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_KQueue_keventSize(JNIEnv* env, jclass this)
+{
+    return sizeof(struct kevent);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_KQueue_identOffset(JNIEnv* env, jclass this)
+{
+    return offsetof(struct kevent, ident);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_KQueue_filterOffset(JNIEnv* env, jclass this)
+{
+    return offsetof(struct kevent, filter);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_KQueue_flagsOffset(JNIEnv* env, jclass this)
+{
+    return offsetof(struct kevent, flags);
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_KQueue_kqueue(JNIEnv *env, jclass c) {
+    int kqfd = kqueue();
+    if (kqfd < 0) {
+        JNU_ThrowIOExceptionWithLastError(env, "kqueue failed");
+    }
+    return kqfd;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_KQueue_keventRegister(JNIEnv *env, jclass c, jint kqfd,
+                                      jint fd, jint filter, jint flags)
+
+{
+    struct kevent changes[1];
+    struct timespec timeout = {0, 0};
+    int res;
+
+    EV_SET(&changes[0], fd, filter, flags, 0, 0, 0);
+    RESTARTABLE(kevent(kqfd, &changes[0], 1, NULL, 0, &timeout), res);
+    return (res == -1) ? errno : 0;
+}
+
+JNIEXPORT jint JNICALL
+Java_sun_nio_ch_KQueue_keventPoll(JNIEnv *env, jclass c,
+                                  jint kqfd, jlong address, jint nevents)
+{
+    struct kevent *events = jlong_to_ptr(address);
+    int res;
+
+    RESTARTABLE(kevent(kqfd, NULL, 0, events, nevents, NULL), res);
+    if (res < 0) {
+        JNU_ThrowIOExceptionWithLastError(env, "kqueue failed");
+    }
+    return res;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/solaris/native/sun/nio/ch/KQueuePort.c	Sat Jan 28 11:37:15 2012 +0000
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "jvm.h"
+#include "jlong.h"
+#include "nio_util.h"
+
+#include "sun_nio_ch_KQueuePort.h"
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_KQueuePort_socketpair(JNIEnv* env, jclass clazz, jintArray sv) {
+    int sp[2];
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) {
+        JNU_ThrowIOExceptionWithLastError(env, "socketpair failed");
+    } else {
+        jint res[2];
+        res[0] = (jint)sp[0];
+        res[1] = (jint)sp[1];
+        (*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]);
+    }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_KQueuePort_interrupt(JNIEnv *env, jclass c, jint fd) {
+    int res;
+    int buf[1];
+    buf[0] = 1;
+    RESTARTABLE(write(fd, buf, 1), res);
+    if (res < 0) {
+        JNU_ThrowIOExceptionWithLastError(env, "write failed");
+    }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_KQueuePort_drain1(JNIEnv *env, jclass cl, jint fd) {
+    int res;
+    char buf[1];
+    RESTARTABLE(read(fd, buf, 1), res);
+    if (res < 0) {
+        JNU_ThrowIOExceptionWithLastError(env, "drain1 failed");
+    }
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_ch_KQueuePort_close0(JNIEnv *env, jclass c, jint fd) {
+    int res;
+    RESTARTABLE(close(fd), res);
+}