pax_global_header 0000666 0000000 0000000 00000000064 12521741223 0014511 g ustar 00root root 0000000 0000000 52 comment=3dc7bc1426561ef4fe8f6e7f1d789b0a86c6604d
jnr-enxio-0.9/ 0000775 0000000 0000000 00000000000 12521741223 0013272 5 ustar 00root root 0000000 0000000 jnr-enxio-0.9/.gitignore 0000664 0000000 0000000 00000000124 12521741223 0015257 0 ustar 00root root 0000000 0000000 build
target
nbproject/private
dist
*.orig$
*.rej$
*.class$
*~
.idea
*.iml
/.redcar/ jnr-enxio-0.9/.travis.yml 0000664 0000000 0000000 00000000433 12521741223 0015403 0 ustar 00root root 0000000 0000000 language: 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/LICENSE 0000664 0000000 0000000 00000001036 12521741223 0014277 0 ustar 00root root 0000000 0000000
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.xml 0000664 0000000 0000000 00000004523 12521741223 0014613 0 ustar 00root root 0000000 0000000
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/ 0000775 0000000 0000000 00000000000 12521741223 0014061 5 ustar 00root root 0000000 0000000 jnr-enxio-0.9/src/main/ 0000775 0000000 0000000 00000000000 12521741223 0015005 5 ustar 00root root 0000000 0000000 jnr-enxio-0.9/src/main/java/ 0000775 0000000 0000000 00000000000 12521741223 0015726 5 ustar 00root root 0000000 0000000 jnr-enxio-0.9/src/main/java/jnr/ 0000775 0000000 0000000 00000000000 12521741223 0016517 5 ustar 00root root 0000000 0000000 jnr-enxio-0.9/src/main/java/jnr/enxio/ 0000775 0000000 0000000 00000000000 12521741223 0017641 5 ustar 00root root 0000000 0000000 jnr-enxio-0.9/src/main/java/jnr/enxio/channels/ 0000775 0000000 0000000 00000000000 12521741223 0021434 5 ustar 00root root 0000000 0000000 jnr-enxio-0.9/src/main/java/jnr/enxio/channels/KQSelectionKey.java 0000664 0000000 0000000 00000003527 12521741223 0025140 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000030701 12521741223 0024314 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000012157 12521741223 0023533 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000005126 12521741223 0026142 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000001440 12521741223 0027001 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000004360 12521741223 0026744 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000003301 12521741223 0027353 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000006354 12521741223 0026177 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000003640 12521741223 0025527 0 ustar 00root root 0000000 0000000 /*
* 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.java 0000664 0000000 0000000 00000021045 12521741223 0024710 0 ustar 00root root 0000000 0000000 /*
* 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/ 0000775 0000000 0000000 00000000000 12521741223 0021274 5 ustar 00root root 0000000 0000000 jnr-enxio-0.9/src/main/java/jnr/enxio/example/TCPServer.java 0000664 0000000 0000000 00000020062 12521741223 0023754 0 ustar 00root root 0000000 0000000 /*
* 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) {
}
}
}