pax_global_header00006660000000000000000000000064125217412230014511gustar00rootroot0000000000000052 comment=3dc7bc1426561ef4fe8f6e7f1d789b0a86c6604d jnr-enxio-0.9/000077500000000000000000000000001252174122300132725ustar00rootroot00000000000000jnr-enxio-0.9/.gitignore000066400000000000000000000001241252174122300152570ustar00rootroot00000000000000build target nbproject/private dist *.orig$ *.rej$ *.class$ *~ .idea *.iml /.redcar/jnr-enxio-0.9/.travis.yml000066400000000000000000000004331252174122300154030ustar00rootroot00000000000000language: java jdk: - oraclejdk7 - openjdk6 os: - linux - osx notifications: irc: channels: - "irc.freenode.org#jnr" on_success: change on_failure: always template: - "%{repository} (%{branch}:%{commit} by %{author}): %{message} (%{build_url})" jnr-enxio-0.9/LICENSE000066400000000000000000000010361252174122300142770ustar00rootroot00000000000000 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.jnr-enxio-0.9/pom.xml000066400000000000000000000045231252174122300146130ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 7 com.github.jnr jnr-enxio jar 0.9 jnr-enxio Native I/O access for java http://github.com/jnr/jnr-enxio The Apache Software License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0.txt repo scm:git:git@github.com:jnr/jnr-enxio.git scm:git:git@github.com:jnr/jnr-enxio.git git@github.com:jnr/jnr-enxio.git wmeissner Wayne Meissner wmeissner@gmail.com UTF-8 1.5 1.5 junit junit 4.11 test com.github.jnr jnr-constants 0.8.7 com.github.jnr jnr-ffi 2.0.3 old-jdk (,1.6] com.github.jnr jnr-enxio-protocolfamily 1.0 provided true jnr-enxio-0.9/src/000077500000000000000000000000001252174122300140615ustar00rootroot00000000000000jnr-enxio-0.9/src/main/000077500000000000000000000000001252174122300150055ustar00rootroot00000000000000jnr-enxio-0.9/src/main/java/000077500000000000000000000000001252174122300157265ustar00rootroot00000000000000jnr-enxio-0.9/src/main/java/jnr/000077500000000000000000000000001252174122300165175ustar00rootroot00000000000000jnr-enxio-0.9/src/main/java/jnr/enxio/000077500000000000000000000000001252174122300176415ustar00rootroot00000000000000jnr-enxio-0.9/src/main/java/jnr/enxio/channels/000077500000000000000000000000001252174122300214345ustar00rootroot00000000000000jnr-enxio-0.9/src/main/java/jnr/enxio/channels/KQSelectionKey.java000066400000000000000000000035271252174122300251400ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.spi.AbstractSelectionKey; class KQSelectionKey extends AbstractSelectionKey { private final KQSelector selector; private final NativeSelectableChannel channel; private int interestOps = 0; private int readyOps = 0; public KQSelectionKey(KQSelector selector, NativeSelectableChannel channel, int ops) { this.selector = selector; this.channel = channel; this.interestOps = ops; } int getFD() { return channel.getFD(); } @Override public SelectableChannel channel() { return (SelectableChannel) channel; } @Override public Selector selector() { return selector; } @Override public int interestOps() { return interestOps; } @Override public SelectionKey interestOps(int ops) { interestOps = ops; selector.interestOps(this, ops); return this; } @Override public int readyOps() { return readyOps; } void readyOps(int readyOps) { this.readyOps = readyOps; } } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/KQSelector.java000066400000000000000000000307011252174122300243140ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import jnr.constants.platform.Errno; import jnr.ffi.Memory; import jnr.ffi.Pointer; import jnr.ffi.StructLayout; import jnr.ffi.TypeAlias; import jnr.ffi.provider.jffi.NativeRuntime; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.spi.AbstractSelectableChannel; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; /** * An implementation of a {@link java.nio.channels.Selector} that uses the BSD (including MacOS) * kqueue(2) mechanism */ class KQSelector extends java.nio.channels.spi.AbstractSelector { private static final boolean DEBUG = false; private static final int MAX_EVENTS = 100; private static final int EVFILT_READ = -1; private static final int EVFILT_WRITE = -2; private static final int EV_ADD = 0x0001; private static final int EV_DELETE = 0x0002; private static final int EV_ENABLE = 0x0004; private static final int EV_DISABLE = 0x0008; private static final int EV_CLEAR = 0x0020; private int kqfd = -1; private final jnr.ffi.Runtime runtime = NativeRuntime.getSystemRuntime(); private final Pointer changebuf; private final Pointer eventbuf; private final EventIO io = EventIO.getInstance(); private final int[] pipefd = { -1, -1 }; private final Object regLock = new Object(); private final Map descriptors = new ConcurrentHashMap(); private final Set selected = new LinkedHashSet(); private final Set changed = new LinkedHashSet(); private final Native.Timespec ZERO_TIMESPEC = new Native.Timespec(0, 0); public KQSelector(NativeSelectorProvider provider) { super(provider); changebuf = Memory.allocateDirect(runtime, MAX_EVENTS * io.size()); eventbuf = Memory.allocateDirect(runtime, MAX_EVENTS * io.size()); Native.libc().pipe(pipefd); kqfd = Native.libc().kqueue(); io.put(changebuf, 0, pipefd[0], EVFILT_READ, EV_ADD); Native.libc().kevent(kqfd, changebuf, 1, null, 0, ZERO_TIMESPEC); } private static class Descriptor { private final int fd; private final Set keys = new HashSet(); private boolean write = false, read = false; public Descriptor(int fd) { this.fd = fd; } } @Override protected void implCloseSelector() throws IOException { if (kqfd != -1) { Native.close(kqfd); } if (pipefd[0] != -1) { Native.close(pipefd[0]); } if (pipefd[1] != -1) { Native.close(pipefd[1]); } pipefd[0] = pipefd[1] = kqfd = -1; // deregister all keys for (Map.Entry entry : descriptors.entrySet()) { for (KQSelectionKey k : entry.getValue().keys) { deregister(k); } } } @Override protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att) { KQSelectionKey k = new KQSelectionKey(this, (NativeSelectableChannel) ch, ops); synchronized (regLock) { Descriptor d = descriptors.get(k.getFD()); if (d == null) { d = new Descriptor(k.getFD()); descriptors.put(k.getFD(), d); } d.keys.add(k); changed.add(d); } k.attach(att); return k; } @Override public Set keys() { Set keys = new HashSet(); for (Descriptor fd : descriptors.values()) { keys.addAll(fd.keys); } return Collections.unmodifiableSet(keys); } @Override public Set selectedKeys() { return selected; } @Override public int selectNow() throws IOException { return poll(0); } @Override public int select(long timeout) throws IOException { return poll(timeout); } @Override public int select() throws IOException { return poll(-1); } private int poll(long timeout) { int nchanged = 0; // // Remove any cancelled keys // Set cancelled = cancelledKeys(); synchronized (cancelled) { synchronized (regLock) { for (SelectionKey k : cancelled) { KQSelectionKey kqs = (KQSelectionKey) k; Descriptor d = descriptors.get(kqs.getFD()); deregister(kqs); synchronized (selected) { selected.remove(kqs); } d.keys.remove(kqs); if (d.keys.isEmpty()) { io.put(changebuf, nchanged++, kqs.getFD(), EVFILT_READ, EV_DELETE); io.put(changebuf, nchanged++, kqs.getFD(), EVFILT_WRITE, EV_DELETE); descriptors.remove(kqs.getFD()); changed.remove(d); } if (nchanged >= MAX_EVENTS) { Native.libc().kevent(kqfd, changebuf, nchanged, null, 0, ZERO_TIMESPEC); nchanged = 0; } } } cancelled.clear(); } synchronized (regLock) { for (Descriptor d : changed) { int writers = 0, readers = 0; for (KQSelectionKey k : d.keys) { if ((k.interestOps() & (SelectionKey.OP_ACCEPT | SelectionKey.OP_READ)) != 0) { ++readers; } if ((k.interestOps() & (SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE)) != 0) { ++writers; } } for (Integer filt : new Integer[] { EVFILT_READ, EVFILT_WRITE }) { int flags = 0; // // If no one is interested in events on the fd, disable it // if (filt == EVFILT_READ) { if (readers > 0 && !d.read) { flags = EV_ADD |EV_ENABLE | EV_CLEAR; d.read = true; } else if (readers == 0 && d.read) { flags = EV_DISABLE; d.read = false; } } if (filt == EVFILT_WRITE) { if (writers > 0 && !d.write) { flags = EV_ADD | EV_ENABLE | EV_CLEAR; d.write = true; } else if (writers == 0 && d.write) { flags = EV_DISABLE; d.write = false; } } if (DEBUG) System.out.printf("Updating fd %d filt=0x%x flags=0x%x\n", d.fd, filt, flags); if (flags != 0) { io.put(changebuf, nchanged++, d.fd, filt, flags); } if (nchanged >= MAX_EVENTS) { Native.libc().kevent(kqfd, changebuf, nchanged, null, 0, ZERO_TIMESPEC); nchanged = 0; } } } changed.clear(); } Native.Timespec ts = null; if (timeout >= 0) { long sec = TimeUnit.MILLISECONDS.toSeconds(timeout); long nsec = TimeUnit.MILLISECONDS.toNanos(timeout % 1000); ts = new Native.Timespec(sec, nsec); } if (DEBUG) System.out.printf("nchanged=%d\n", nchanged); int nready = 0; try { begin(); do { nready = Native.libc().kevent(kqfd, changebuf, nchanged, eventbuf, MAX_EVENTS, ts); } while (nready < 0 && Errno.EINTR.equals(Errno.valueOf(Native.getRuntime().getLastError()))); if (DEBUG) System.out.println("kevent returned " + nready + " events ready"); } finally { end(); } int updatedKeyCount = 0; synchronized (regLock) { for (int i = 0; i < nready; ++i) { int fd = io.getFD(eventbuf, i); Descriptor d = descriptors.get(fd); if (d != null) { int filt = io.getFilter(eventbuf, i); if (DEBUG) System.out.printf("fd=%d filt=0x%x\n", d.fd, filt); for (KQSelectionKey k : d.keys) { int iops = k.interestOps(); int ops = 0; if (filt == EVFILT_READ) { ops |= iops & (SelectionKey.OP_ACCEPT | SelectionKey.OP_READ); } if (filt == EVFILT_WRITE) { ops |= iops & (SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE); } ++updatedKeyCount; k.readyOps(ops); if (!selected.contains(k)) { selected.add(k); } } } else if (fd == pipefd[0]) { if (DEBUG) System.out.println("Waking up"); wakeupReceived(); } } } return updatedKeyCount; } private void wakeupReceived() { Native.libc().read(pipefd[0], new byte[1], 1); } @Override public Selector wakeup() { Native.libc().write(pipefd[1], new byte[1], 1); return this; } void interestOps(KQSelectionKey k, int ops) { synchronized (regLock) { changed.add(descriptors.get(k.getFD())); } } private static final class EventIO { private static final EventIO INSTANCE = new EventIO(); private final EventLayout layout = new EventLayout(NativeRuntime.getSystemRuntime()); private final jnr.ffi.Type uintptr_t = layout.getRuntime().findType(TypeAlias.uintptr_t); public static EventIO getInstance() { return EventIO.INSTANCE; } public final void put(Pointer buf, int index, int fd, int filt, int flags) { buf.putInt(uintptr_t, (index * layout.size()) + layout.ident.offset(), fd); buf.putShort((index * layout.size()) + layout.filter.offset(), (short) filt); buf.putInt((index * layout.size()) + layout.flags.offset(), flags); } public final int size() { return layout.size(); } int getFD(Pointer ptr, int index) { return (int) ptr.getInt(uintptr_t, (index * layout.size()) + layout.ident.offset()); } public final void putFilter(Pointer buf, int index, int filter) { buf.putShort((index * layout.size()) + layout.filter.offset(), (short) filter); } public final int getFilter(Pointer buf, int index) { return buf.getShort((index * layout.size()) + layout.filter.offset()); } public final void putFlags(Pointer buf, int index, int flags) { buf.putShort((index * layout.size()) + layout.flags.offset(), (short) flags); } } private static class EventLayout extends StructLayout { private EventLayout(jnr.ffi.Runtime runtime) { super(runtime); } public final uintptr_t ident = new uintptr_t(); public final int16_t filter = new int16_t(); public final u_int16_t flags = new u_int16_t(); public final u_int32_t fflags = new u_int32_t(); public final intptr_t data = new intptr_t(); public final Pointer udata = new Pointer(); } } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/Native.java000066400000000000000000000121571252174122300235330ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import jnr.constants.platform.Errno; import jnr.ffi.*; import jnr.ffi.Runtime; import jnr.ffi.annotations.IgnoreError; import jnr.ffi.annotations.In; import jnr.ffi.annotations.Out; import jnr.ffi.annotations.Transient; import jnr.ffi.types.size_t; import jnr.ffi.types.ssize_t; import java.io.IOException; import java.nio.ByteBuffer; final class Native { public static interface LibC { public static final int F_GETFL = jnr.constants.platform.Fcntl.F_GETFL.intValue(); public static final int F_SETFL = jnr.constants.platform.Fcntl.F_SETFL.intValue(); public static final int O_NONBLOCK = jnr.constants.platform.OpenFlags.O_NONBLOCK.intValue(); public int close(int fd); public @ssize_t int read(int fd, @Out ByteBuffer data, @size_t long size); public @ssize_t int read(int fd, @Out byte[] data, @size_t long size); public @ssize_t int write(int fd, @In ByteBuffer data, @size_t long size); public @ssize_t int write(int fd, @In byte[] data, @size_t long size); public int fcntl(int fd, int cmd, int data); public int poll(@In @Out ByteBuffer pfds, int nfds, int timeout); public int poll(@In @Out Pointer pfds, int nfds, int timeout); public int kqueue(); public int kevent(int kq, @In ByteBuffer changebuf, int nchanges, @Out ByteBuffer eventbuf, int nevents, @In @Transient Timespec timeout); public int kevent(int kq, @In Pointer changebuf, int nchanges, @Out Pointer eventbuf, int nevents, @In @Transient Timespec timeout); public int pipe(@Out int[] fds); public int shutdown(int s, int how); @IgnoreError String strerror(int error); } private static final class SingletonHolder { static final LibC libc = Library.loadLibrary("c", LibC.class); static final jnr.ffi.Runtime runtime = Library.getRuntime(libc); } static LibC libc() { return SingletonHolder.libc; } static jnr.ffi.Runtime getRuntime() { return SingletonHolder.runtime; } public static int close(int fd) { int rc; do { rc = libc().close(fd); } while (rc < 0 && Errno.EINTR.equals(getLastError())); return rc; } public static int read(int fd, ByteBuffer dst) throws IOException { if (dst == null) { throw new NullPointerException("Destination buffer cannot be null"); } if (dst.isReadOnly()) { throw new IllegalArgumentException("Read-only buffer"); } int n; do { n = libc().read(fd, dst, dst.remaining()); } while (n < 0 && Errno.EINTR.equals(getLastError())); if (n > 0) { dst.position(dst.position() + n); } return n; } public static int write(int fd, ByteBuffer src) throws IOException { if (src == null) { throw new NullPointerException("Source buffer cannot be null"); } int n; do { n = libc().write(fd, src, src.remaining()); } while (n < 0 && Errno.EINTR.equals(getLastError())); if (n > 0) { src.position(src.position() + n); } return n; } public static void setBlocking(int fd, boolean block) { int flags = libc().fcntl(fd, LibC.F_GETFL, 0); if (block) { flags &= ~LibC.O_NONBLOCK; } else { flags |= LibC.O_NONBLOCK; } libc().fcntl(fd, LibC.F_SETFL, flags); } public static int shutdown(int fd, int how) { return libc().shutdown(fd, how); } public static String getLastErrorString() { return libc().strerror(LastError.getLastError(getRuntime())); } static Errno getLastError() { return Errno.valueOf(LastError.getLastError(getRuntime())); } public static final class Timespec extends Struct { public final SignedLong tv_sec = new SignedLong(); public final SignedLong tv_nsec = new SignedLong(); public Timespec() { super(Native.getRuntime()); } public Timespec(Runtime runtime) { super(runtime); } public Timespec(long sec, long nsec) { super(Native.getRuntime()); tv_sec.set(sec); tv_nsec.set(nsec); } } } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/NativeDeviceChannel.java000066400000000000000000000051261252174122300261420ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.SelectionKey; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; public class NativeDeviceChannel extends AbstractSelectableChannel implements ByteChannel, NativeSelectableChannel { private final int fd; private final int validOps; public NativeDeviceChannel(int fd) { this(NativeSelectorProvider.getInstance(), fd, SelectionKey.OP_READ | SelectionKey.OP_WRITE); } public NativeDeviceChannel(SelectorProvider provider, int fd, int ops) { super(provider); this.fd = fd; this.validOps = ops; } @Override protected void implCloseSelectableChannel() throws IOException { int n = Native.close(fd); if (n < 0) { throw new IOException(Native.getLastErrorString()); } } @Override protected void implConfigureBlocking(boolean block) throws IOException { Native.setBlocking(fd, block); } @Override public final int validOps() { return validOps; } public final int getFD() { return fd; } public int read(ByteBuffer dst) throws IOException { int n = Native.read(fd, dst); switch (n) { case 0: return -1; case -1: switch (Native.getLastError()) { case EAGAIN: case EWOULDBLOCK: return 0; default: throw new IOException(Native.getLastErrorString()); } default: return n; } } public int write(ByteBuffer src) throws IOException { int n = Native.write(fd, src); if (n < 0) { throw new IOException(Native.getLastErrorString()); } return n; } } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/NativeSelectableChannel.java000066400000000000000000000014401252174122300270010ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import java.nio.channels.Channel; public interface NativeSelectableChannel extends Channel { public int getFD(); } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/NativeSelectorProvider.java000066400000000000000000000043601252174122300267440ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import jnr.ffi.Platform; import java.io.IOException; import java.nio.channels.DatagramChannel; import java.nio.channels.Pipe; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.spi.AbstractSelector; import java.nio.channels.spi.SelectorProvider; public final class NativeSelectorProvider extends SelectorProvider { private static final class SingletonHolder { static NativeSelectorProvider INSTANCE = new NativeSelectorProvider(); } public static final SelectorProvider getInstance() { return SingletonHolder.INSTANCE; } @Override public DatagramChannel openDatagramChannel() throws IOException { throw new UnsupportedOperationException("Not supported yet."); } public DatagramChannel openDatagramChannel(java.net.ProtocolFamily family) throws IOException { throw new UnsupportedOperationException("Not supported yet."); } @Override public Pipe openPipe() throws IOException { throw new UnsupportedOperationException("Not supported yet."); } @Override public AbstractSelector openSelector() throws IOException { return Platform.getNativePlatform().isBSD() ? new KQSelector(this) : new PollSelector(this); } @Override public ServerSocketChannel openServerSocketChannel() throws IOException { throw new UnsupportedOperationException("Not supported yet."); } @Override public SocketChannel openSocketChannel() throws IOException { throw new UnsupportedOperationException("Not supported yet."); } } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/NativeServerSocketChannel.java000066400000000000000000000033011252174122300273530ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; public class NativeServerSocketChannel extends AbstractSelectableChannel implements NativeSelectableChannel { private final int fd; private final int validOps; public NativeServerSocketChannel(int fd) { this(NativeSelectorProvider.getInstance(), fd, SelectionKey.OP_ACCEPT | SelectionKey.OP_READ); } public NativeServerSocketChannel(SelectorProvider provider, int fd, int ops) { super(provider); this.fd = fd; this.validOps = ops; } @Override protected void implCloseSelectableChannel() throws IOException { Native.close(fd); } @Override protected void implConfigureBlocking(boolean block) throws IOException { Native.setBlocking(fd, block); } @Override public final int validOps() { return validOps; } public final int getFD() { return fd; } } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/NativeSocketChannel.java000066400000000000000000000063541252174122300261770ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import jnr.constants.platform.Errno; import jnr.constants.platform.Shutdown; import jnr.ffi.LastError; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.channels.SelectionKey; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; public class NativeSocketChannel extends AbstractSelectableChannel implements ByteChannel, NativeSelectableChannel { private final int fd; private final int validOps; public NativeSocketChannel(int fd) { this(NativeSelectorProvider.getInstance(), fd, SelectionKey.OP_READ | SelectionKey.OP_WRITE); } public NativeSocketChannel(int fd, int ops) { this(NativeSelectorProvider.getInstance(), fd, ops); } NativeSocketChannel(SelectorProvider provider, int fd, int ops) { super(provider); this.fd = fd; this.validOps = ops; } @Override protected void implCloseSelectableChannel() throws IOException { Native.close(fd); } @Override protected void implConfigureBlocking(boolean block) throws IOException { Native.setBlocking(fd, block); } @Override public final int validOps() { return validOps; } public final int getFD() { return fd; } public int read(ByteBuffer dst) throws IOException { int n = Native.read(fd, dst); switch (n) { case 0: return -1; case -1: switch (Native.getLastError()) { case EAGAIN: case EWOULDBLOCK: return 0; default: throw new IOException(Native.getLastErrorString()); } default: return n; } } public int write(ByteBuffer src) throws IOException { int n = Native.write(fd, src); if (n < 0) { throw new IOException(Native.getLastErrorString()); } return n; } public void shutdownInput() throws IOException { int n = Native.shutdown(fd, SHUT_RD); if (n < 0) { throw new IOException(Native.getLastErrorString()); } } public void shutdownOutput() throws IOException { int n = Native.shutdown(fd, SHUT_WR); if (n < 0) { throw new IOException(Native.getLastErrorString()); } } private final static int SHUT_RD = Shutdown.SHUT_RD.intValue(); private final static int SHUT_WR = Shutdown.SHUT_WR.intValue(); } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/PollSelectionKey.java000066400000000000000000000036401252174122300255270ustar00rootroot00000000000000/* * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.spi.AbstractSelectionKey; class PollSelectionKey extends AbstractSelectionKey { private final PollSelector selector; private final NativeSelectableChannel channel; private int interestOps = 0; private int readyOps = 0; private int index = -1; public PollSelectionKey(PollSelector selector, NativeSelectableChannel channel) { this.selector = selector; this.channel = channel; } void setIndex(int index) { this.index = index; } int getIndex() { return index; } int getFD() { return channel.getFD(); } @Override public SelectableChannel channel() { return (SelectableChannel) channel; } @Override public Selector selector() { return selector; } @Override public int interestOps() { return interestOps; } @Override public SelectionKey interestOps(int ops) { interestOps = ops; selector.interestOps(this, ops); return this; } @Override public int readyOps() { return readyOps; } void readyOps(int readyOps) { this.readyOps = readyOps; } } jnr-enxio-0.9/src/main/java/jnr/enxio/channels/PollSelector.java000066400000000000000000000210451252174122300247100ustar00rootroot00000000000000/* * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.channels; import jnr.constants.platform.Errno; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.spi.AbstractSelectableChannel; import java.nio.channels.spi.SelectorProvider; import java.util.*; import java.util.concurrent.ConcurrentHashMap; /** * An implementation of a {@link java.nio.channels.Selector} that uses good old * poll(2) */ class PollSelector extends java.nio.channels.spi.AbstractSelector { private static final int POLLFD_SIZE = 8; private static final int FD_OFFSET = 0; private static final int EVENTS_OFFSET = 4; private static final int REVENTS_OFFSET = 6; static final int POLLIN = 0x1; static final int POLLOUT = 0x4; static final int POLLERR = 0x8; static final int POLLHUP = 0x10; private PollSelectionKey[] keyArray = new PollSelectionKey[0]; private ByteBuffer pollData = null; private int nfds; private final int[] pipefd = { -1, -1 }; private final Object regLock = new Object(); private final Map keys = new ConcurrentHashMap(); private final Set selected = new HashSet(); public PollSelector(SelectorProvider provider) { super(provider); Native.libc().pipe(pipefd); // Register the wakeup pipe as the first element in the pollfd array pollData = ByteBuffer.allocateDirect(8).order(ByteOrder.nativeOrder()); putPollFD(0, pipefd[0]); putPollEvents(0, POLLIN); nfds = 1; keyArray = new PollSelectionKey[1]; } private void putPollFD(int idx, int fd) { pollData.putInt((idx * POLLFD_SIZE) + FD_OFFSET, fd); } private void putPollEvents(int idx, int events) { pollData.putShort((idx * POLLFD_SIZE) + EVENTS_OFFSET, (short) events); } private int getPollFD(int idx) { return pollData.getInt((idx * POLLFD_SIZE) + FD_OFFSET); } private short getPollEvents(int idx) { return pollData.getShort((idx * POLLFD_SIZE) + EVENTS_OFFSET); } private short getPollRevents(int idx) { return pollData.getShort((idx * POLLFD_SIZE) + REVENTS_OFFSET); } private void putPollRevents(int idx, int events) { pollData.putShort((idx * POLLFD_SIZE) + REVENTS_OFFSET, (short) events); } @Override protected void implCloseSelector() throws IOException { if (pipefd[0] != -1) { Native.close(pipefd[0]); } if (pipefd[1] != -1) { Native.close(pipefd[1]); } // remove all keys for (SelectionKey key : keys.keySet()) { remove((PollSelectionKey)key); } } @Override protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att) { PollSelectionKey key = new PollSelectionKey(this, (NativeSelectableChannel) ch); add(key); key.attach(att); key.interestOps(ops); return key; } @Override public Set keys() { return new HashSet(Arrays.asList(keyArray).subList(0, nfds)); } @Override public Set selectedKeys() { return selected; } void interestOps(PollSelectionKey k, int ops) { short events = 0; if ((ops & (SelectionKey.OP_ACCEPT | SelectionKey.OP_READ)) != 0) { events |= POLLIN; } if ((ops & (SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT)) != 0) { events |= POLLOUT; } putPollEvents(k.getIndex(), events); } private void add(PollSelectionKey k) { synchronized (regLock) { ++nfds; if (keyArray.length < nfds) { PollSelectionKey[] newArray = new PollSelectionKey[nfds + (nfds / 2)]; System.arraycopy(keyArray, 0, newArray, 0, nfds - 1); keyArray = newArray; ByteBuffer newBuffer = ByteBuffer.allocateDirect(newArray.length * 8); if (pollData != null) { newBuffer.put(pollData); } newBuffer.position(0); pollData = newBuffer.order(ByteOrder.nativeOrder()); } k.setIndex(nfds - 1); keyArray[nfds - 1] = k; putPollFD(k.getIndex(), k.getFD()); putPollEvents(k.getIndex(), 0); keys.put(k, true); } } private void remove(PollSelectionKey k) { int idx = k.getIndex(); synchronized (regLock) { // // If not the last key, swap last one into the removed key's position // if (idx < (nfds - 1)) { PollSelectionKey last = keyArray[nfds - 1]; keyArray[idx] = last; // Copy the data for the last key into place putPollFD(idx, getPollFD(last.getIndex())); putPollEvents(idx, getPollEvents(last.getIndex())); last.setIndex(idx); } else { putPollFD(idx, -1); putPollEvents(idx, 0); } keyArray[nfds - 1] = null; --nfds; synchronized (selected) { selected.remove(k); } keys.remove(k); } deregister(k); } @Override public int selectNow() throws IOException { return poll(0); } @Override public int select(long timeout) throws IOException { return poll(timeout > 0 ? timeout : -1); } @Override public int select() throws IOException { return poll(-1); } private int poll(long timeout) throws IOException { // // Remove any cancelled keys // Set cancelled = cancelledKeys(); synchronized (cancelled) { for (SelectionKey k : cancelled) { remove((PollSelectionKey) k); } cancelled.clear(); } int nready = 0; try { begin(); do { nready = Native.libc().poll(pollData, nfds, (int) timeout); } while (nready < 0 && Errno.EINTR.equals(Errno.valueOf(Native.getRuntime().getLastError()))); } finally { end(); } if (nready < 1) { return nready; } if ((getPollRevents(0) & POLLIN) != 0) { wakeupReceived(); } int updatedKeyCount = 0; for (SelectionKey k : keys.keySet()) { PollSelectionKey pk = (PollSelectionKey) k; int revents = getPollRevents(pk.getIndex()); if (revents != 0) { putPollRevents(pk.getIndex(), 0); int iops = k.interestOps(); int ops = 0; if ((revents & POLLIN) != 0) { ops |= iops & (SelectionKey.OP_ACCEPT | SelectionKey.OP_READ); } if ((revents & POLLOUT) != 0) { ops |= iops & (SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE); } // If an error occurred, enable all interested ops and let the // event handling code deal with it if ((revents & (POLLHUP | POLLERR)) != 0) { ops = iops; } ((PollSelectionKey) k).readyOps(ops); ++updatedKeyCount; if (!selected.contains(k)) { selected.add(k); } } } return updatedKeyCount; } private void wakeupReceived() throws IOException { Native.read(pipefd[0], ByteBuffer.allocate(1)); } @Override public Selector wakeup() { try { Native.write(pipefd[1], ByteBuffer.allocate(1)); } catch (IOException ioe) { throw new RuntimeException(ioe); } return this; } } jnr-enxio-0.9/src/main/java/jnr/enxio/example/000077500000000000000000000000001252174122300212745ustar00rootroot00000000000000jnr-enxio-0.9/src/main/java/jnr/enxio/example/TCPServer.java000066400000000000000000000200621252174122300237540ustar00rootroot00000000000000/* * Copyright (C) 2008 Wayne Meissner * * This file is part of the JNR project. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jnr.enxio.example; import jnr.enxio.channels.NativeSelectableChannel; import jnr.enxio.channels.NativeSelectorProvider; import jnr.enxio.channels.NativeServerSocketChannel; import jnr.enxio.channels.NativeSocketChannel; import jnr.ffi.*; import jnr.ffi.annotations.In; import jnr.ffi.annotations.Out; import jnr.ffi.types.size_t; import jnr.ffi.types.ssize_t; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; /** * * @author wayne */ public class TCPServer { static final String[] libnames = Platform.getNativePlatform().getOS() == Platform.OS.SOLARIS ? new String[] { "socket", "nsl", "c" } : new String[] { "c" }; static final LibC libc = Library.loadLibrary(LibC.class, libnames); static final jnr.ffi.Runtime runtime = jnr.ffi.Runtime.getSystemRuntime(); public static class SockAddr extends Struct { public SockAddr() { super(runtime); } } static class BSDSockAddrIN extends SockAddr { public final Unsigned8 sin_len = new Unsigned8(); public final Unsigned8 sin_family = new Unsigned8(); public final Unsigned16 sin_port = new Unsigned16(); public final Unsigned32 sin_addr = new Unsigned32(); public final Padding sin_zero = new Padding(NativeType.SCHAR, 8); } static class SockAddrIN extends SockAddr { public final Unsigned16 sin_family = new Unsigned16(); public final Unsigned16 sin_port = new Unsigned16(); public final Unsigned32 sin_addr = new Unsigned32(); public final Padding sin_zero = new Padding(NativeType.SCHAR, 8); } public static interface LibC { static final int AF_INET = jnr.constants.platform.AddressFamily.AF_INET.intValue(); static final int SOCK_STREAM = jnr.constants.platform.Sock.SOCK_STREAM.intValue(); int socket(int domain, int type, int protocol); int close(int fd); int listen(int fd, int backlog); int bind(int fd, SockAddr addr, int len); int accept(int fd, @Out SockAddr addr, int[] len); @ssize_t int read(int fd, @Out ByteBuffer data, @size_t int len); @ssize_t int read(int fd, @Out byte[] data, @size_t int len); @ssize_t int write(int fd, @In ByteBuffer data, @size_t int len); String strerror(int error); } static short htons(short val) { return Short.reverseBytes(val); } static NativeServerSocketChannel serverSocket(int port) { int fd = libc.socket(LibC.AF_INET, LibC.SOCK_STREAM, 0); System.out.println("fd=" + fd); SockAddr addr; if (Platform.getNativePlatform().isBSD()) { BSDSockAddrIN sin = new BSDSockAddrIN(); sin.sin_family.set((byte) LibC.AF_INET); sin.sin_port.set(htons((short) port)); addr = sin; } else { SockAddrIN sin = new SockAddrIN(); sin.sin_family.set(htons((short) LibC.AF_INET)); sin.sin_port.set(htons((short) port)); addr = sin; } System.out.println("sizeof addr=" + Struct.size(addr)); if (libc.bind(fd, addr, Struct.size(addr)) < 0) { System.err.println("bind failed: " + libc.strerror(LastError.getLastError(runtime))); System.exit(1); } if (libc.listen(fd, 5) < 0) { System.err.println("listen failed: " + libc.strerror(LastError.getLastError(runtime))); System.exit(1); } System.out.println("bind+listen succeeded"); return new NativeServerSocketChannel(fd); } private static abstract class IO { protected final SelectableChannel channel; protected final Selector selector; public IO(Selector selector, SelectableChannel ch) { this.selector = selector; this.channel = ch; } public abstract void read(); public abstract void write(); } private static class Accepter extends IO { public Accepter(Selector selector, NativeServerSocketChannel ch) { super(selector, ch); } public void read() { SockAddrIN sin = new SockAddrIN(); int[] addrSize = { Struct.size(sin) }; int clientfd = libc.accept(((NativeSelectableChannel) channel).getFD(), sin, addrSize); System.out.println("client fd = " + clientfd); NativeSocketChannel ch = new NativeSocketChannel(clientfd); try { ch.configureBlocking(false); ch.register(selector, SelectionKey.OP_READ, new Client(selector, ch)); selector.wakeup(); } catch (IOException ex) {} } public void write() { SelectionKey k = channel.keyFor(selector); k.interestOps(SelectionKey.OP_ACCEPT); } } private static class Client extends IO { private final ByteBuffer buf = ByteBuffer.allocateDirect(1024); public Client(Selector selector, NativeSocketChannel ch) { super(selector, ch); } public void read() { int n = libc.read(((NativeSelectableChannel) channel).getFD(), buf, buf.remaining()); System.out.println("Read " + n + " bytes from client"); if (n <= 0) { SelectionKey k = channel.keyFor(selector); k.cancel(); libc.close(((NativeSelectableChannel) channel).getFD()); return; } buf.position(n); buf.flip(); channel.keyFor(selector).interestOps(SelectionKey.OP_WRITE); } public void write() { while (buf.hasRemaining()) { int n = libc.write(((NativeSelectableChannel) channel).getFD(), buf, buf.remaining()); System.out.println("write returned " + n); if (n > 0) { buf.position(buf.position() + n); } if (n == 0) { return; } if (n < 0) { channel.keyFor(selector).cancel(); libc.close(((NativeSelectableChannel) channel).getFD()); return; } } System.out.println("outbuf empty"); buf.clear(); channel.keyFor(selector).interestOps(SelectionKey.OP_READ); } } public static void main(String[] args) { short baseport = 2000; try { Selector selector = NativeSelectorProvider.getInstance().openSelector(); for (int i = 0; i < 2; ++i) { NativeServerSocketChannel ch = serverSocket(baseport + i); ch.configureBlocking(false); ch.register(selector, SelectionKey.OP_ACCEPT, new Accepter(selector, ch)); } while (true) { selector.select(); for (SelectionKey k : selector.selectedKeys()) { if ((k.readyOps() & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0) { ((IO) k.attachment()).read(); } if ((k.readyOps() & (SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT)) != 0) { ((IO) k.attachment()).write(); } } } } catch (IOException ex) { } } }