pax_global_header 0000666 0000000 0000000 00000000064 13055576131 0014520 g ustar 00root root 0000000 0000000 52 comment=1c0b217ef53a7d26d084b9643cf5870a0e8f7156
jnr-enxio-jnr-enxio-0.16/ 0000775 0000000 0000000 00000000000 13055576131 0015266 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/.gitignore 0000664 0000000 0000000 00000000124 13055576131 0017253 0 ustar 00root root 0000000 0000000 build
target
nbproject/private
dist
*.orig$
*.rej$
*.class$
*~
.idea
*.iml
/.redcar/ jnr-enxio-jnr-enxio-0.16/.travis.yml 0000664 0000000 0000000 00000000434 13055576131 0017400 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-jnr-enxio-0.16/LICENSE 0000664 0000000 0000000 00000001036 13055576131 0016273 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-jnr-enxio-0.16/README.md 0000664 0000000 0000000 00000000256 13055576131 0016550 0 ustar 00root root 0000000 0000000 jnr-enxio
=========
Native I/O access for java
Check out the [examples](https://github.com/jnr/jnr-enxio/tree/master/src/test/java/jnr/enxio/example) for more information.
jnr-enxio-jnr-enxio-0.16/pom.xml 0000664 0000000 0000000 00000010060 13055576131 0016600 0 ustar 00root root 0000000 0000000
4.0.0
org.sonatype.oss
oss-parent
7
com.github.jnr
jnr-enxio
jar
0.16
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.9.8
com.github.jnr
jnr-ffi
2.1.4
org.apache.felix
maven-bundle-plugin
2.3.7
<_nouses>true
*,jnr.ffi.mapper,jnr.ffi.provider.converters,com.kenai.jffi
bundle-manifest
process-classes
manifest
org.apache.maven.plugins
maven-jar-plugin
2.3.1
${project.build.outputDirectory}/META-INF/MANIFEST.MF
org.apache.maven.plugins
maven-source-plugin
2.2.1
attach-sources
jar-no-fork
org.apache.maven.plugins
maven-javadoc-plugin
attach-javadocs
jar
old-jdk
(,1.6]
com.github.jnr
jnr-enxio-protocolfamily
1.0
provided
true
jnr-enxio-jnr-enxio-0.16/src/ 0000775 0000000 0000000 00000000000 13055576131 0016055 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/main/ 0000775 0000000 0000000 00000000000 13055576131 0017001 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/main/java/ 0000775 0000000 0000000 00000000000 13055576131 0017722 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/main/java/jnr/ 0000775 0000000 0000000 00000000000 13055576131 0020513 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/main/java/jnr/enxio/ 0000775 0000000 0000000 00000000000 13055576131 0021635 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/ 0000775 0000000 0000000 00000000000 13055576131 0023430 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/KQSelectionKey.java 0000664 0000000 0000000 00000003527 13055576131 0027134 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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/KQSelector.java 0000664 0000000 0000000 00000030407 13055576131 0026313 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 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);
handleChangedKey(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 = handleCancelledKeys();
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 int handleCancelledKeys() {
Set cancelled = cancelledKeys();
synchronized (cancelled) {
int nchanged = 0;
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());
}
if (nchanged >= MAX_EVENTS) {
Native.libc().kevent(kqfd, changebuf, nchanged, null, 0, ZERO_TIMESPEC);
nchanged = 0;
}
}
}
cancelled.clear();
return nchanged;
}
}
private void handleChangedKey(Descriptor changed) {
synchronized (regLock) {
int _nchanged = 0;
int writers = 0, readers = 0;
for (KQSelectionKey k : changed.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 && !changed.read) {
flags = EV_ADD |EV_ENABLE | EV_CLEAR;
changed.read = true;
} else if (readers == 0 && changed.read) {
flags = EV_DISABLE;
changed.read = false;
}
}
if (filt == EVFILT_WRITE) {
if (writers > 0 && !changed.write) {
flags = EV_ADD | EV_ENABLE | EV_CLEAR;
changed.write = true;
} else if (writers == 0 && changed.write) {
flags = EV_DISABLE;
changed.write = false;
}
}
if (DEBUG) System.out.printf("Updating fd %d filt=0x%x flags=0x%x\n",
changed.fd, filt, flags);
if (flags != 0) {
io.put(changebuf, _nchanged++, changed.fd, filt, flags);
}
}
Native.libc().kevent(kqfd, changebuf, _nchanged, null, 0, ZERO_TIMESPEC);
}
}
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) {
handleChangedKey(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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/Native.java 0000664 0000000 0000000 00000012652 13055576131 0025527 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 jnr.ffi.Platform;
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 = LibraryLoader.create(LibC.class).load(Platform.getNativePlatform().getStandardCLibraryName());
static final jnr.ffi.Runtime runtime = Runtime.getRuntime(libc);
}
static LibC libc() {
return SingletonHolder.libc;
}
static jnr.ffi.Runtime getRuntime() {
return SingletonHolder.runtime;
}
public static int close(int fd) throws IOException {
int rc;
do {
rc = libc().close(fd);
} while (rc < 0 && Errno.EINTR.equals(getLastError()));
if (rc < 0) {
String message = String.format("Error closing fd %d: %s", fd, getLastErrorString());
throw new NativeException(message, getLastError());
} else {
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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/NativeDeviceChannel.java 0000664 0000000 0000000 00000005126 13055576131 0030136 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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/NativeException.java 0000664 0000000 0000000 00000000544 13055576131 0027403 0 ustar 00root root 0000000 0000000 package jnr.enxio.channels;
import jnr.constants.platform.Errno;
import java.io.IOException;
public class NativeException extends IOException {
private final Errno errno;
public NativeException(String message, Errno errno) {
super(message);
this.errno = errno;
}
public Errno getErrno() {
return errno;
}
}
jnr-enxio-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/NativeSelectableChannel.java 0000664 0000000 0000000 00000001440 13055576131 0030775 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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/NativeSelectorProvider.java 0000664 0000000 0000000 00000004360 13055576131 0030740 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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/NativeServerSocketChannel.java 0000664 0000000 0000000 00000003301 13055576131 0031347 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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/NativeSocketChannel.java 0000664 0000000 0000000 00000006536 13055576131 0030175 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.Shutdown;
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) {
switch (Native.getLastError()) {
case EAGAIN:
case EWOULDBLOCK:
return 0;
default:
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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/PollSelectionKey.java 0000664 0000000 0000000 00000003640 13055576131 0027523 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-jnr-enxio-0.16/src/main/java/jnr/enxio/channels/PollSelector.java 0000664 0000000 0000000 00000021045 13055576131 0026704 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-jnr-enxio-0.16/src/test/ 0000775 0000000 0000000 00000000000 13055576131 0017034 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/test/java/ 0000775 0000000 0000000 00000000000 13055576131 0017755 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/test/java/jnr/ 0000775 0000000 0000000 00000000000 13055576131 0020546 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/test/java/jnr/enxio/ 0000775 0000000 0000000 00000000000 13055576131 0021670 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/test/java/jnr/enxio/channels/ 0000775 0000000 0000000 00000000000 13055576131 0023463 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/test/java/jnr/enxio/channels/NativeTest.java 0000664 0000000 0000000 00000001414 13055576131 0026414 0 ustar 00root root 0000000 0000000 package jnr.enxio.channels;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
public class NativeTest {
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Test
public void closeThrowsOnNativeError() throws Exception {
FileOutputStream fos = new FileOutputStream("/dev/null");
FileDescriptor descriptor = fos.getFD();
Field fdField = descriptor.getClass().getDeclaredField("fd");
fdField.setAccessible(true);
int fd = (int)(Integer)fdField.get(descriptor);
Native.close(fd);
expectedEx.expect(NativeException.class);
Native.close(fd);
}
}
jnr-enxio-jnr-enxio-0.16/src/test/java/jnr/enxio/example/ 0000775 0000000 0000000 00000000000 13055576131 0023323 5 ustar 00root root 0000000 0000000 jnr-enxio-jnr-enxio-0.16/src/test/java/jnr/enxio/example/TCPServer.java 0000664 0000000 0000000 00000020145 13055576131 0026005 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[] { Platform.getNativePlatform().getStandardCLibraryName() };
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) {
}
}
}