summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--luni/src/main/java/java/io/FileInputStream.java4
-rw-r--r--luni/src/main/java/java/io/FileOutputStream.java5
-rw-r--r--luni/src/main/java/java/io/RandomAccessFile.java3
-rw-r--r--luni/src/main/java/java/net/InetAddress.java4
-rw-r--r--luni/src/main/java/java/net/PlainDatagramSocketImpl.java2
-rw-r--r--luni/src/main/java/java/net/PlainSocketImpl.java2
-rw-r--r--luni/src/main/java/java/nio/DatagramChannelImpl.java2
-rw-r--r--luni/src/main/java/java/nio/FileChannelImpl.java10
-rw-r--r--luni/src/main/java/java/nio/NioUtils.java5
-rw-r--r--luni/src/main/java/java/nio/SelectorImpl.java17
-rw-r--r--luni/src/main/java/java/nio/SocketChannelImpl.java6
-rw-r--r--luni/src/main/java/libcore/io/BlockGuardOs.java26
-rw-r--r--luni/src/main/java/libcore/io/ForwardingOs.java22
-rw-r--r--luni/src/main/java/libcore/io/IoBridge.java12
-rw-r--r--luni/src/main/java/libcore/io/Os.java22
-rw-r--r--luni/src/main/java/libcore/io/Posix.java30
-rw-r--r--luni/src/main/native/AsynchronousCloseMonitor.cpp (renamed from luni/src/main/native/AsynchronousSocketCloseMonitor.cpp)28
-rw-r--r--luni/src/main/native/AsynchronousCloseMonitor.h (renamed from luni/src/main/native/AsynchronousSocketCloseMonitor.h)36
-rw-r--r--luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp6
-rw-r--r--luni/src/main/native/libcore_io_Posix.cpp103
-rw-r--r--luni/src/main/native/sub.mk2
-rwxr-xr-xluni/src/test/java/libcore/java/io/InterruptedStreamTest.java7
-rw-r--r--luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java628
23 files changed, 844 insertions, 138 deletions
diff --git a/luni/src/main/java/java/io/FileInputStream.java b/luni/src/main/java/java/io/FileInputStream.java
index 644f749..5debe64 100644
--- a/luni/src/main/java/java/io/FileInputStream.java
+++ b/luni/src/main/java/java/io/FileInputStream.java
@@ -21,10 +21,8 @@ import dalvik.system.CloseGuard;
import java.nio.NioUtils;
import java.nio.channels.FileChannel;
-import java.util.Arrays;
import libcore.io.ErrnoException;
import libcore.io.IoBridge;
-import libcore.io.IoUtils;
import libcore.io.Libcore;
import libcore.io.Streams;
import static libcore.io.OsConstants.*;
@@ -118,7 +116,7 @@ public class FileInputStream extends InputStream {
channel.close();
}
if (shouldClose) {
- IoUtils.close(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
} else {
// An owned fd has been invalidated by IoUtils.close, but
// we need to explicitly stop using an unowned fd (http://b/4361076).
diff --git a/luni/src/main/java/java/io/FileOutputStream.java b/luni/src/main/java/java/io/FileOutputStream.java
index f5ba11e..e796e80 100644
--- a/luni/src/main/java/java/io/FileOutputStream.java
+++ b/luni/src/main/java/java/io/FileOutputStream.java
@@ -20,9 +20,8 @@ package java.io;
import dalvik.system.CloseGuard;
import java.nio.NioUtils;
import java.nio.channels.FileChannel;
-import java.util.Arrays;
import libcore.io.IoBridge;
-import libcore.io.IoUtils;
+
import static libcore.io.OsConstants.*;
/**
@@ -136,7 +135,7 @@ public class FileOutputStream extends OutputStream {
channel.close();
}
if (shouldClose) {
- IoUtils.close(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
} else {
// An owned fd has been invalidated by IoUtils.close, but
// we need to explicitly stop using an unowned fd (http://b/4361076).
diff --git a/luni/src/main/java/java/io/RandomAccessFile.java b/luni/src/main/java/java/io/RandomAccessFile.java
index 6e88fe0..eac7641 100644
--- a/luni/src/main/java/java/io/RandomAccessFile.java
+++ b/luni/src/main/java/java/io/RandomAccessFile.java
@@ -25,7 +25,6 @@ import java.nio.charset.ModifiedUtf8;
import java.util.Arrays;
import libcore.io.ErrnoException;
import libcore.io.IoBridge;
-import libcore.io.IoUtils;
import libcore.io.Libcore;
import libcore.io.Memory;
import libcore.io.SizeOf;
@@ -163,7 +162,7 @@ public class RandomAccessFile implements DataInput, DataOutput, Closeable {
channel.close();
channel = null;
}
- IoUtils.close(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
}
}
diff --git a/luni/src/main/java/java/net/InetAddress.java b/luni/src/main/java/java/net/InetAddress.java
index 98ad098..885b472 100644
--- a/luni/src/main/java/java/net/InetAddress.java
+++ b/luni/src/main/java/java/net/InetAddress.java
@@ -28,8 +28,6 @@ import java.io.Serializable;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Collections;
-import java.util.Comparator;
-import java.util.Enumeration;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -734,7 +732,7 @@ public class InetAddress implements Serializable {
}
}
- IoBridge.closeSocket(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
return reached;
}
diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
index edf7024..a9ade28 100644
--- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -78,7 +78,7 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
public synchronized void close() {
guard.close();
try {
- IoBridge.closeSocket(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
} catch (IOException ignored) {
}
}
diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java
index d81303f..e3988ed 100644
--- a/luni/src/main/java/java/net/PlainSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainSocketImpl.java
@@ -145,7 +145,7 @@ public class PlainSocketImpl extends SocketImpl {
@Override
protected synchronized void close() throws IOException {
guard.close();
- IoBridge.closeSocket(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
}
@Override
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 883ffea..2f04b30 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -524,7 +524,7 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
@Override protected synchronized void implCloseSelectableChannel() throws IOException {
// A closed channel is not connected.
onDisconnect(true /* updateSocketState */);
- IoBridge.closeSocket(fd);
+ IoBridge.closeAndSignalBlockedThreads(fd);
multicastMembershipHandler = null;
if (socket != null && !socket.isClosed()) {
diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java
index 6206d1b..9a47706 100644
--- a/luni/src/main/java/java/nio/FileChannelImpl.java
+++ b/luni/src/main/java/java/nio/FileChannelImpl.java
@@ -50,7 +50,7 @@ final class FileChannelImpl extends FileChannel {
}
};
- private final Object stream;
+ private final Closeable ioObject;
private final FileDescriptor fd;
private final int mode;
@@ -61,9 +61,9 @@ final class FileChannelImpl extends FileChannel {
* Create a new file channel implementation class that wraps the given
* fd and operates in the specified mode.
*/
- public FileChannelImpl(Object stream, FileDescriptor fd, int mode) {
+ public FileChannelImpl(Closeable ioObject, FileDescriptor fd, int mode) {
this.fd = fd;
- this.stream = stream;
+ this.ioObject = ioObject;
this.mode = mode;
}
@@ -86,9 +86,7 @@ final class FileChannelImpl extends FileChannel {
}
protected void implCloseChannel() throws IOException {
- if (stream instanceof Closeable) {
- ((Closeable) stream).close();
- }
+ ioObject.close();
}
private FileLock basicLock(long position, long size, boolean shared, boolean wait) throws IOException {
diff --git a/luni/src/main/java/java/nio/NioUtils.java b/luni/src/main/java/java/nio/NioUtils.java
index 34af76b..51adddb 100644
--- a/luni/src/main/java/java/nio/NioUtils.java
+++ b/luni/src/main/java/java/nio/NioUtils.java
@@ -16,6 +16,7 @@
package java.nio;
+import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.SocketOption;
@@ -48,8 +49,8 @@ public final class NioUtils {
/**
* Helps bridge between io and nio.
*/
- public static FileChannel newFileChannel(Object stream, FileDescriptor fd, int mode) {
- return new FileChannelImpl(stream, fd, mode);
+ public static FileChannel newFileChannel(Closeable ioObject, FileDescriptor fd, int mode) {
+ return new FileChannelImpl(ioObject, fd, mode);
}
/**
diff --git a/luni/src/main/java/java/nio/SelectorImpl.java b/luni/src/main/java/java/nio/SelectorImpl.java
index d63fa63..3495523 100644
--- a/luni/src/main/java/java/nio/SelectorImpl.java
+++ b/luni/src/main/java/java/nio/SelectorImpl.java
@@ -17,18 +17,15 @@ package java.nio;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.InterruptedIOException;
import java.nio.channels.ClosedSelectorException;
import java.nio.channels.IllegalSelectorException;
-import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
-import static java.nio.channels.SelectionKey.*;
import java.nio.channels.Selector;
-import java.nio.channels.SocketChannel;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.AbstractSelectionKey;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -40,8 +37,15 @@ import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Libcore;
import libcore.io.StructPollfd;
-import libcore.util.EmptyArray;
-import static libcore.io.OsConstants.*;
+
+import static java.nio.channels.SelectionKey.OP_ACCEPT;
+import static java.nio.channels.SelectionKey.OP_CONNECT;
+import static java.nio.channels.SelectionKey.OP_READ;
+import static java.nio.channels.SelectionKey.OP_WRITE;
+import static libcore.io.OsConstants.EINTR;
+import static libcore.io.OsConstants.POLLHUP;
+import static libcore.io.OsConstants.POLLIN;
+import static libcore.io.OsConstants.POLLOUT;
/*
* Default implementation of java.nio.channels.Selector
@@ -321,6 +325,7 @@ final class SelectorImpl extends AbstractSelector {
try {
Libcore.os.write(wakeupOut, new byte[] { 1 }, 0, 1);
} catch (ErrnoException ignored) {
+ } catch (InterruptedIOException ignored) {
}
return this;
}
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index d1e77d6..2ed11fc 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -525,9 +525,9 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
protected synchronized void implCloseSelectableChannel() throws IOException {
if (status != SOCKET_STATUS_CLOSED) {
status = SOCKET_STATUS_CLOSED;
- // IoBridge.closeSocket(fd) is idempotent: It is safe to call on an already-closed file
- // descriptor.
- IoBridge.closeSocket(fd);
+ // IoBridge.closeAndSignalBlockedThreads(fd) is idempotent: It is safe to call on an
+ // already-closed file descriptor.
+ IoBridge.closeAndSignalBlockedThreads(fd);
if (socket != null && !socket.isClosed()) {
socket.onClose();
}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 05a756f..eaa7f0c 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -19,6 +19,7 @@ package libcore.io;
import dalvik.system.BlockGuard;
import dalvik.system.SocketTagger;
import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
@@ -158,6 +159,11 @@ public class BlockGuardOs extends ForwardingOs {
os.mkdir(path, mode);
}
+ @Override public void mkfifo(String path, int mode) throws ErrnoException {
+ BlockGuard.getThreadPolicy().onWriteToDisk();
+ os.mkfifo(path, mode);
+ }
+
@Override public FileDescriptor open(String path, int flags, int mode) throws ErrnoException {
BlockGuard.getThreadPolicy().onReadFromDisk();
if ((mode & O_ACCMODE) != O_RDONLY) {
@@ -180,32 +186,32 @@ public class BlockGuardOs extends ForwardingOs {
os.posix_fallocate(fd, offset, length);
}
- @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+ @Override public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.pread(fd, buffer, offset);
}
- @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+ @Override public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.pread(fd, bytes, byteOffset, byteCount, offset);
}
- @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+ @Override public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.pwrite(fd, buffer, offset);
}
- @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+ @Override public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.pwrite(fd, bytes, byteOffset, byteCount, offset);
}
- @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ @Override public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.read(fd, buffer);
}
- @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ @Override public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.read(fd, bytes, byteOffset, byteCount);
}
@@ -215,7 +221,7 @@ public class BlockGuardOs extends ForwardingOs {
return os.readlink(path);
}
- @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
+ @Override public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onReadFromDisk();
return os.readv(fd, buffers, offsets, byteCounts);
}
@@ -283,17 +289,17 @@ public class BlockGuardOs extends ForwardingOs {
os.symlink(oldPath, newPath);
}
- @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ @Override public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.write(fd, buffer);
}
- @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ @Override public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.write(fd, bytes, byteOffset, byteCount);
}
- @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException {
+ @Override public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException {
BlockGuard.getThreadPolicy().onWriteToDisk();
return os.writev(fd, buffers, offsets, byteCounts);
}
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index 2de35e9..0b49071 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -17,6 +17,7 @@
package libcore.io;
import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -90,6 +91,7 @@ public class ForwardingOs implements Os {
public StructStat lstat(String path) throws ErrnoException { return os.lstat(path); }
public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException { os.mincore(address, byteCount, vector); }
public void mkdir(String path, int mode) throws ErrnoException { os.mkdir(path, mode); }
+ public void mkfifo(String path, int mode) throws ErrnoException { os.mkfifo(path, mode); }
public void mlock(long address, long byteCount) throws ErrnoException { os.mlock(address, byteCount); }
public long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException { return os.mmap(address, byteCount, prot, flags, fd, offset); }
public void msync(long address, long byteCount, int flags) throws ErrnoException { os.msync(address, byteCount, flags); }
@@ -99,14 +101,14 @@ public class ForwardingOs implements Os {
public FileDescriptor[] pipe() throws ErrnoException { return os.pipe(); }
public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException { return os.poll(fds, timeoutMs); }
public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException { os.posix_fallocate(fd, offset, length); }
- public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException { return os.pread(fd, buffer, offset); }
- public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException { return os.pread(fd, bytes, byteOffset, byteCount, offset); }
- public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException { return os.pwrite(fd, buffer, offset); }
- public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException { return os.pwrite(fd, bytes, byteOffset, byteCount, offset); }
- public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.read(fd, buffer); }
- public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.read(fd, bytes, byteOffset, byteCount); }
+ public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return os.pread(fd, buffer, offset); }
+ public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return os.pread(fd, bytes, byteOffset, byteCount, offset); }
+ public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException { return os.pwrite(fd, buffer, offset); }
+ public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException { return os.pwrite(fd, bytes, byteOffset, byteCount, offset); }
+ public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.read(fd, buffer); }
+ public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return os.read(fd, bytes, byteOffset, byteCount); }
public String readlink(String path) throws ErrnoException { return os.readlink(path); }
- public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.readv(fd, buffers, offsets, byteCounts); }
+ public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.readv(fd, buffers, offsets, byteCounts); }
public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, buffer, flags, srcAddress); }
public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException { return os.recvfrom(fd, bytes, byteOffset, byteCount, flags, srcAddress); }
public void remove(String path) throws ErrnoException { os.remove(path); }
@@ -143,7 +145,7 @@ public class ForwardingOs implements Os {
public StructUtsname uname() { return os.uname(); }
public void unsetenv(String name) throws ErrnoException { os.unsetenv(name); }
public int waitpid(int pid, MutableInt status, int options) throws ErrnoException { return os.waitpid(pid, status, options); }
- public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException { return os.write(fd, buffer); }
- public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException { return os.write(fd, bytes, byteOffset, byteCount); }
- public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException { return os.writev(fd, buffers, offsets, byteCounts); }
+ public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException { return os.write(fd, buffer); }
+ public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException { return os.write(fd, bytes, byteOffset, byteCount); }
+ public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException { return os.writev(fd, buffers, offsets, byteCounts); }
}
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
index 08d4837..89070cd 100644
--- a/luni/src/main/java/libcore/io/IoBridge.java
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -177,9 +177,15 @@ public final class IoBridge {
return detail;
}
- public static void closeSocket(FileDescriptor fd) throws IOException {
- if (!fd.valid()) {
- // Socket.close doesn't throw if you try to close an already-closed socket.
+ /**
+ * Closes the supplied file descriptor and sends a signal to any threads are currently blocking.
+ * In order for the signal to be sent the blocked threads must have registered with
+ * the AsynchronousCloseMonitor before they entered the blocking operation.
+ *
+ * <p>This method is a no-op if passed a {@code null} or already-closed file descriptor.
+ */
+ public static void closeAndSignalBlockedThreads(FileDescriptor fd) throws IOException {
+ if (fd == null || !fd.valid()) {
return;
}
int intFd = fd.getInt$();
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index a4541a7..2065c70 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -17,6 +17,7 @@
package libcore.io;
import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -82,6 +83,7 @@ public interface Os {
public StructStat lstat(String path) throws ErrnoException;
public void mincore(long address, long byteCount, byte[] vector) throws ErrnoException;
public void mkdir(String path, int mode) throws ErrnoException;
+ public void mkfifo(String path, int mode) throws ErrnoException;
public void mlock(long address, long byteCount) throws ErrnoException;
public long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;
public void msync(long address, long byteCount, int flags) throws ErrnoException;
@@ -92,14 +94,14 @@ public interface Os {
/* TODO: if we used the non-standard ppoll(2) behind the scenes, we could take a long timeout. */
public int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
public void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
- public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException;
- public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException;
- public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException;
- public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException;
- public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
- public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
+ public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException;
+ public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+ public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException;
+ public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+ public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
+ public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException;
public String readlink(String path) throws ErrnoException;
- public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ public int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public int recvfrom(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException;
public void remove(String path) throws ErrnoException;
@@ -136,7 +138,7 @@ public interface Os {
public StructUtsname uname();
public void unsetenv(String name) throws ErrnoException;
public int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
- public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
- public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException;
- public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
+ public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException;
+ public int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
}
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index c6f13ea..a5c3eb0 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -17,6 +17,7 @@
package libcore.io;
import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@@ -84,6 +85,7 @@ public final class Posix implements Os {
public native StructStat lstat(String path) throws ErrnoException;
public native void mincore(long address, long byteCount, byte[] vector) throws ErrnoException;
public native void mkdir(String path, int mode) throws ErrnoException;
+ public native void mkfifo(String path, int mode) throws ErrnoException;
public native void mlock(long address, long byteCount) throws ErrnoException;
public native long mmap(long address, long byteCount, int prot, int flags, FileDescriptor fd, long offset) throws ErrnoException;
public native void msync(long address, long byteCount, int flags) throws ErrnoException;
@@ -93,44 +95,44 @@ public final class Posix implements Os {
public native FileDescriptor[] pipe() throws ErrnoException;
public native int poll(StructPollfd[] fds, int timeoutMs) throws ErrnoException;
public native void posix_fallocate(FileDescriptor fd, long offset, long length) throws ErrnoException;
- public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+ public int pread(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
if (buffer.isDirect()) {
return preadBytes(fd, buffer, buffer.position(), buffer.remaining(), offset);
} else {
return preadBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), offset);
}
}
- public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+ public int pread(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return preadBytes(fd, bytes, byteOffset, byteCount, offset);
}
- private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException;
- public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException {
+ private native int preadBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+ public int pwrite(FileDescriptor fd, ByteBuffer buffer, long offset) throws ErrnoException, InterruptedIOException {
if (buffer.isDirect()) {
return pwriteBytes(fd, buffer, buffer.position(), buffer.remaining(), offset);
} else {
return pwriteBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining(), offset);
}
}
- public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException {
+ public int pwrite(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return pwriteBytes(fd, bytes, byteOffset, byteCount, offset);
}
- private native int pwriteBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException;
- public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ private native int pwriteBytes(FileDescriptor fd, Object buffer, int bufferOffset, int byteCount, long offset) throws ErrnoException, InterruptedIOException;
+ public int read(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
if (buffer.isDirect()) {
return readBytes(fd, buffer, buffer.position(), buffer.remaining());
} else {
return readBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
}
}
- public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ public int read(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return readBytes(fd, bytes, byteOffset, byteCount);
}
- private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
+ private native int readBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException;
public native String readlink(String path) throws ErrnoException;
- public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ public native int readv(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
public int recvfrom(FileDescriptor fd, ByteBuffer buffer, int flags, InetSocketAddress srcAddress) throws ErrnoException, SocketException {
if (buffer.isDirect()) {
return recvfromBytes(fd, buffer, buffer.position(), buffer.remaining(), flags, srcAddress);
@@ -193,17 +195,17 @@ public final class Posix implements Os {
public native StructUtsname uname();
public native void unsetenv(String name) throws ErrnoException;
public native int waitpid(int pid, MutableInt status, int options) throws ErrnoException;
- public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException {
+ public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
if (buffer.isDirect()) {
return writeBytes(fd, buffer, buffer.position(), buffer.remaining());
} else {
return writeBytes(fd, NioUtils.unsafeArray(buffer), NioUtils.unsafeArrayOffset(buffer) + buffer.position(), buffer.remaining());
}
}
- public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException {
+ public int write(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount) throws ErrnoException, InterruptedIOException {
// This indirection isn't strictly necessary, but ensures that our public interface is type safe.
return writeBytes(fd, bytes, byteOffset, byteCount);
}
- private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException;
- public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException;
+ private native int writeBytes(FileDescriptor fd, Object buffer, int offset, int byteCount) throws ErrnoException, InterruptedIOException;
+ public native int writev(FileDescriptor fd, Object[] buffers, int[] offsets, int[] byteCounts) throws ErrnoException, InterruptedIOException;
}
diff --git a/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp b/luni/src/main/native/AsynchronousCloseMonitor.cpp
index 9617e9d..92d5260 100644
--- a/luni/src/main/native/AsynchronousSocketCloseMonitor.cpp
+++ b/luni/src/main/native/AsynchronousCloseMonitor.cpp
@@ -14,9 +14,9 @@
* limitations under the License.
*/
-#define LOG_TAG "AsynchronousSocketCloseMonitor"
+#define LOG_TAG "AsynchronousCloseMonitor"
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
#include "cutils/log.h"
#include <errno.h>
@@ -27,12 +27,12 @@
* We use an intrusive doubly-linked list to keep track of blocked threads.
* This gives us O(1) insertion and removal, and means we don't need to do any allocation.
* (The objects themselves are stack-allocated.)
- * Waking potentially-blocked threads when a socket is closed is O(n) in the total number of
- * blocked threads (not the number of threads actually blocked on the socket in question).
- * For now at least, this seems like a good compromise for Android.
+ * Waking potentially-blocked threads when a file descriptor is closed is O(n) in the total number
+ * of blocked threads (not the number of threads actually blocked on the file descriptor in
+ * question). For now at least, this seems like a good compromise for Android.
*/
static pthread_mutex_t blockedThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
-static AsynchronousSocketCloseMonitor* blockedThreadList = NULL;
+static AsynchronousCloseMonitor* blockedThreadList = NULL;
/**
* The specific signal chosen here is arbitrary.
@@ -47,7 +47,7 @@ static void blockedThreadSignalHandler(int /*signal*/) {
// Do nothing. We only sent this signal for its side-effect of interrupting syscalls.
}
-void AsynchronousSocketCloseMonitor::init() {
+void AsynchronousCloseMonitor::init() {
// Ensure that the signal we send interrupts system calls but doesn't kill threads.
// Using sigaction(2) lets us ensure that the SA_RESTART flag is not set.
// (The whole reason we're sending this signal is to unblock system calls!)
@@ -61,21 +61,27 @@ void AsynchronousSocketCloseMonitor::init() {
}
}
-void AsynchronousSocketCloseMonitor::signalBlockedThreads(int fd) {
+void AsynchronousCloseMonitor::signalBlockedThreads(int fd) {
ScopedPthreadMutexLock lock(&blockedThreadListMutex);
- for (AsynchronousSocketCloseMonitor* it = blockedThreadList; it != NULL; it = it->mNext) {
+ for (AsynchronousCloseMonitor* it = blockedThreadList; it != NULL; it = it->mNext) {
if (it->mFd == fd) {
+ it->mSignaled = true;
pthread_kill(it->mThread, BLOCKED_THREAD_SIGNAL);
// Keep going, because there may be more than one thread...
}
}
}
-AsynchronousSocketCloseMonitor::AsynchronousSocketCloseMonitor(int fd) {
+bool AsynchronousCloseMonitor::wasSignaled() const {
+ return mSignaled;
+}
+
+AsynchronousCloseMonitor::AsynchronousCloseMonitor(int fd) {
ScopedPthreadMutexLock lock(&blockedThreadListMutex);
// Who are we, and what are we waiting for?
mThread = pthread_self();
mFd = fd;
+ mSignaled = false;
// Insert ourselves at the head of the intrusive doubly-linked list...
mPrev = NULL;
mNext = blockedThreadList;
@@ -85,7 +91,7 @@ AsynchronousSocketCloseMonitor::AsynchronousSocketCloseMonitor(int fd) {
blockedThreadList = this;
}
-AsynchronousSocketCloseMonitor::~AsynchronousSocketCloseMonitor() {
+AsynchronousCloseMonitor::~AsynchronousCloseMonitor() {
ScopedPthreadMutexLock lock(&blockedThreadListMutex);
// Unlink ourselves from the intrusive doubly-linked list...
if (mNext != NULL) {
diff --git a/luni/src/main/native/AsynchronousSocketCloseMonitor.h b/luni/src/main/native/AsynchronousCloseMonitor.h
index 3370e22..eefbbdf 100644
--- a/luni/src/main/native/AsynchronousSocketCloseMonitor.h
+++ b/luni/src/main/native/AsynchronousCloseMonitor.h
@@ -14,47 +14,53 @@
* limitations under the License.
*/
-#ifndef ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
-#define ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
+#ifndef ASYNCHRONOUS_CLOSE_MONITOR_H_included
+#define ASYNCHRONOUS_CLOSE_MONITOR_H_included
#include "ScopedPthreadMutexLock.h"
#include <pthread.h>
/**
- * AsynchronousSocketCloseMonitor helps implement Java's asynchronous Socket.close semantics.
+ * AsynchronousCloseMonitor helps implement Java's asynchronous close semantics.
*
- * AsynchronousSocketCloseMonitor::init must be called before anything else.
+ * AsynchronousCloseMonitor::init must be called before anything else.
*
- * Every blocking network I/O operation must be surrounded by an AsynchronousSocketCloseMonitor
+ * Every blocking I/O operation must be surrounded by an AsynchronousCloseMonitor
* instance. For example:
*
* {
- * AsynchronousSocketCloseMonitor monitor(fd);
+ * AsynchronousCloseMonitor monitor(fd);
* byteCount = ::read(fd, buf, sizeof(buf));
* }
*
* To interrupt all threads currently blocked on file descriptor 'fd', call signalBlockedThreads:
*
- * AsynchronousSocketCloseMonitor::signalBlockedThreads(fd);
+ * AsynchronousCloseMonitor::signalBlockedThreads(fd);
+ *
+ * To test to see if the interruption was due to the signalBlockedThreads call:
+ *
+ * monitor.wasSignaled();
*/
-class AsynchronousSocketCloseMonitor {
+class AsynchronousCloseMonitor {
public:
- AsynchronousSocketCloseMonitor(int fd);
- ~AsynchronousSocketCloseMonitor();
+ AsynchronousCloseMonitor(int fd);
+ ~AsynchronousCloseMonitor();
+ bool wasSignaled() const;
static void init();
static void signalBlockedThreads(int fd);
private:
- AsynchronousSocketCloseMonitor* mPrev;
- AsynchronousSocketCloseMonitor* mNext;
+ AsynchronousCloseMonitor* mPrev;
+ AsynchronousCloseMonitor* mNext;
pthread_t mThread;
int mFd;
+ bool mSignaled;
// Disallow copy and assignment.
- AsynchronousSocketCloseMonitor(const AsynchronousSocketCloseMonitor&);
- void operator=(const AsynchronousSocketCloseMonitor&);
+ AsynchronousCloseMonitor(const AsynchronousCloseMonitor&);
+ void operator=(const AsynchronousCloseMonitor&);
};
-#endif // ASYNCHRONOUS_SOCKET_CLOSE_MONITOR_H_included
+#endif // ASYNCHRONOUS_CLOSE_MONITOR_H_included
diff --git a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
index 4f50ce5..a27e7b8 100644
--- a/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
+++ b/luni/src/main/native/libcore_io_AsynchronousCloseMonitor.cpp
@@ -16,20 +16,20 @@
#define LOG_TAG "AsynchronousCloseMonitor"
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
#include "JNIHelp.h"
#include "JniConstants.h"
#include "jni.h"
static void AsynchronousCloseMonitor_signalBlockedThreads(JNIEnv* env, jclass, jobject javaFd) {
int fd = jniGetFDFromFileDescriptor(env, javaFd);
- AsynchronousSocketCloseMonitor::signalBlockedThreads(fd);
+ AsynchronousCloseMonitor::signalBlockedThreads(fd);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(AsynchronousCloseMonitor, signalBlockedThreads, "(Ljava/io/FileDescriptor;)V"),
};
void register_libcore_io_AsynchronousCloseMonitor(JNIEnv* env) {
- AsynchronousSocketCloseMonitor::init();
+ AsynchronousCloseMonitor::init();
jniRegisterNativeMethods(env, "libcore/io/AsynchronousCloseMonitor", gMethods, NELEM(gMethods));
}
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index b9f16a7..65ee36f 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "Posix"
-#include "AsynchronousSocketCloseMonitor.h"
+#include "AsynchronousCloseMonitor.h"
#include "cutils/log.h"
#include "ExecStrings.h"
#include "JNIHelp.h"
@@ -68,31 +68,77 @@ struct addrinfo_deleter {
};
/**
- * Used to retry networking system calls that can return EINTR. Unlike TEMP_FAILURE_RETRY,
- * this also handles the case where the reason for failure is that another thread called
- * Socket.close. This macro also throws exceptions on failure.
+ * Used to retry networking system calls that can be interrupted with a signal. Unlike
+ * TEMP_FAILURE_RETRY, this also handles the case where
+ * AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal a close() or
+ * Thread.interrupt(). Other signals that result in an EINTR result are ignored and the system call
+ * is retried.
*
- * Returns the result of 'exp', though a Java exception will be pending if the result is -1.
+ * Returns the result of the system call though a Java exception will be pending if the result is
+ * -1: a SocketException if signaled via AsynchronousCloseMonitor, or ErrnoException for other
+ * failures.
*/
#define NET_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
return_type _rc = -1; \
do { \
+ bool _wasSignaled; \
+ int _syscallErrno; \
{ \
int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
- AsynchronousSocketCloseMonitor _monitor(_fd); \
+ AsynchronousCloseMonitor _monitor(_fd); \
_rc = syscall_name(_fd, __VA_ARGS__); \
+ _syscallErrno = errno; \
+ _wasSignaled = _monitor.wasSignaled(); \
} \
- if (_rc == -1) { \
- if (jniGetFDFromFileDescriptor(jni_env, java_fd) == -1) { \
- jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
+ if (_wasSignaled) { \
+ jniThrowException(jni_env, "java/net/SocketException", "Socket closed"); \
+ break; \
+ } \
+ if (_rc == -1 && _syscallErrno != EINTR) { \
+ /* TODO: with a format string we could show the arguments too, like strace(1). */ \
+ throwErrnoException(jni_env, # syscall_name); \
+ break; \
+ } \
+ } while (_rc == -1); /* _syscallErrno == EINTR && !_wasSignaled */ \
+ _rc; })
+
+/**
+ * Used to retry system calls that can be interrupted with a signal. Unlike TEMP_FAILURE_RETRY, this
+ * also handles the case where AsynchronousCloseMonitor::signalBlockedThreads(fd) is used to signal
+ * a close() or Thread.interrupt(). Other signals that result in an EINTR result are ignored and the
+ * system call is retried.
+ *
+ * Returns the result of the system call though a Java exception will be pending if the result is
+ * -1: an IOException if the file descriptor is already closed, a InterruptedIOException if signaled
+ * via AsynchronousCloseMonitor, or ErrnoException for other failures.
+ */
+#define IO_FAILURE_RETRY(jni_env, return_type, syscall_name, java_fd, ...) ({ \
+ return_type _rc = -1; \
+ int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
+ if (_fd == -1) { \
+ jniThrowException(jni_env, "java/io/IOException", "File descriptor closed"); \
+ } else { \
+ do { \
+ bool _wasSignaled; \
+ int _syscallErrno; \
+ { \
+ int _fd = jniGetFDFromFileDescriptor(jni_env, java_fd); \
+ AsynchronousCloseMonitor _monitor(_fd); \
+ _rc = syscall_name(_fd, __VA_ARGS__); \
+ _syscallErrno = errno; \
+ _wasSignaled = _monitor.wasSignaled(); \
+ } \
+ if (_wasSignaled) { \
+ jniThrowException(jni_env, "java/io/InterruptedIOException", # syscall_name " interrupted"); \
break; \
- } else if (errno != EINTR) { \
+ } \
+ if (_rc == -1 && _syscallErrno != EINTR) { \
/* TODO: with a format string we could show the arguments too, like strace(1). */ \
throwErrnoException(jni_env, # syscall_name); \
break; \
} \
- } \
- } while (_rc == -1); \
+ } while (_rc == -1); /* && _syscallErrno == EINTR && !_wasSignaled */ \
+ } \
_rc; })
static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2,
@@ -907,6 +953,14 @@ static void Posix_mkdir(JNIEnv* env, jobject, jstring javaPath, jint mode) {
throwIfMinusOne(env, "mkdir", TEMP_FAILURE_RETRY(mkdir(path.c_str(), mode)));
}
+static void Posix_mkfifo(JNIEnv* env, jobject, jstring javaPath, jint mode) {
+ ScopedUtfChars path(env, javaPath);
+ if (path.c_str() == NULL) {
+ return;
+ }
+ throwIfMinusOne(env, "mkfifo", TEMP_FAILURE_RETRY(mkfifo(path.c_str(), mode)));
+}
+
static void Posix_mlock(JNIEnv* env, jobject, jlong address, jlong byteCount) {
void* ptr = reinterpret_cast<void*>(static_cast<uintptr_t>(address));
throwIfMinusOne(env, "mlock", TEMP_FAILURE_RETRY(mlock(ptr, byteCount)));
@@ -990,11 +1044,9 @@ static jint Posix_poll(JNIEnv* env, jobject, jobjectArray javaStructs, jint time
++count;
}
- // Since we don't know which fds -- if any -- are sockets, be conservative and register
- // all fds for asynchronous socket close monitoring.
- std::vector<AsynchronousSocketCloseMonitor*> monitors;
+ std::vector<AsynchronousCloseMonitor*> monitors;
for (size_t i = 0; i < count; ++i) {
- monitors.push_back(new AsynchronousSocketCloseMonitor(fds[i].fd));
+ monitors.push_back(new AsynchronousCloseMonitor(fds[i].fd));
}
int rc = poll(fds.get(), count, timeoutMs);
for (size_t i = 0; i < monitors.size(); ++i) {
@@ -1029,8 +1081,7 @@ static jint Posix_preadBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaB
if (bytes.get() == NULL) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "pread", TEMP_FAILURE_RETRY(pread64(fd, bytes.get() + byteOffset, byteCount, offset)));
+ return IO_FAILURE_RETRY(env, ssize_t, pread64, javaFd, bytes.get() + byteOffset, byteCount, offset);
}
static jint Posix_pwriteBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray javaBytes, jint byteOffset, jint byteCount, jlong offset) {
@@ -1038,8 +1089,7 @@ static jint Posix_pwriteBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray j
if (bytes.get() == NULL) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "pwrite", TEMP_FAILURE_RETRY(pwrite64(fd, bytes.get() + byteOffset, byteCount, offset)));
+ return IO_FAILURE_RETRY(env, ssize_t, pwrite64, javaFd, bytes.get() + byteOffset, byteCount, offset);
}
static jint Posix_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount) {
@@ -1047,8 +1097,7 @@ static jint Posix_readBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBy
if (bytes.get() == NULL) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "read", TEMP_FAILURE_RETRY(read(fd, bytes.get() + byteOffset, byteCount)));
+ return IO_FAILURE_RETRY(env, ssize_t, read, javaFd, bytes.get() + byteOffset, byteCount);
}
static jstring Posix_readlink(JNIEnv* env, jobject, jstring javaPath) {
@@ -1070,8 +1119,7 @@ static jint Posix_readv(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffe
if (!ioVec.init(buffers, offsets, byteCounts)) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "readv", TEMP_FAILURE_RETRY(readv(fd, ioVec.get(), ioVec.size())));
+ return IO_FAILURE_RETRY(env, ssize_t, readv, javaFd, ioVec.get(), ioVec.size());
}
static jint Posix_recvfromBytes(JNIEnv* env, jobject, jobject javaFd, jobject javaBytes, jint byteOffset, jint byteCount, jint flags, jobject javaInetSocketAddress) {
@@ -1417,8 +1465,7 @@ static jint Posix_writeBytes(JNIEnv* env, jobject, jobject javaFd, jbyteArray ja
if (bytes.get() == NULL) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "write", TEMP_FAILURE_RETRY(write(fd, bytes.get() + byteOffset, byteCount)));
+ return IO_FAILURE_RETRY(env, ssize_t, write, javaFd, bytes.get() + byteOffset, byteCount);
}
static jint Posix_writev(JNIEnv* env, jobject, jobject javaFd, jobjectArray buffers, jintArray offsets, jintArray byteCounts) {
@@ -1426,8 +1473,7 @@ static jint Posix_writev(JNIEnv* env, jobject, jobject javaFd, jobjectArray buff
if (!ioVec.init(buffers, offsets, byteCounts)) {
return -1;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- return throwIfMinusOne(env, "writev", TEMP_FAILURE_RETRY(writev(fd, ioVec.get(), ioVec.size())));
+ return IO_FAILURE_RETRY(env, ssize_t, writev, javaFd, ioVec.get(), ioVec.size());
}
static JNINativeMethod gMethods[] = {
@@ -1486,6 +1532,7 @@ static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Posix, lstat, "(Ljava/lang/String;)Llibcore/io/StructStat;"),
NATIVE_METHOD(Posix, mincore, "(JJ[B)V"),
NATIVE_METHOD(Posix, mkdir, "(Ljava/lang/String;I)V"),
+ NATIVE_METHOD(Posix, mkfifo, "(Ljava/lang/String;I)V"),
NATIVE_METHOD(Posix, mlock, "(JJ)V"),
NATIVE_METHOD(Posix, mmap, "(JJIILjava/io/FileDescriptor;J)J"),
NATIVE_METHOD(Posix, msync, "(JJI)V"),
diff --git a/luni/src/main/native/sub.mk b/luni/src/main/native/sub.mk
index e8b6e4a..6bec778 100644
--- a/luni/src/main/native/sub.mk
+++ b/luni/src/main/native/sub.mk
@@ -4,7 +4,7 @@
# or BUILD_*_LIBRARY.
LOCAL_SRC_FILES := \
- AsynchronousSocketCloseMonitor.cpp \
+ AsynchronousCloseMonitor.cpp \
ExecStrings.cpp \
IcuUtilities.cpp \
JniException.cpp \
diff --git a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
index e5fd39f..30ae7eb 100755
--- a/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
+++ b/luni/src/test/java/libcore/java/io/InterruptedStreamTest.java
@@ -207,7 +207,10 @@ public final class InterruptedStreamTest extends TestCase {
private static void confirmInterrupted(Thread thread) throws InterruptedException {
// validate and clear interrupted bit before join
- assertTrue(Thread.interrupted());
- thread.join();
+ try {
+ assertTrue(Thread.interrupted());
+ } finally {
+ thread.join();
+ }
}
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java b/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java
new file mode 100644
index 0000000..2ac8827
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/channels/FileIOInterruptTest.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2014 The Android Open Source 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 libcore.java.nio.channels;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.AsynchronousCloseException;
+import java.nio.channels.ClosedByInterruptException;
+import java.nio.channels.ClosedChannelException;
+import java.nio.channels.FileChannel;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.OsConstants;
+
+import static libcore.io.IoUtils.closeQuietly;
+
+/**
+ * A test for file interrupt behavior. Because forcing a real file to block on read or write is
+ * difficult this test uses Unix FIFO / Named Pipes. FIFOs appear to Java as files but the test
+ * has more control over the available data. Reader will block until the other end writes, and
+ * writers can also be made to block.
+ *
+ * <p>Using FIFOs has a few drawbacks:
+ * <ol>
+ * <li>FIFOs are not supported from Java or the command-line on Android, so this test includes
+ * native code to create the FIFO.
+ * <li>FIFOs will not open() until there is both a reader and a writer of the FIFO; each test must
+ * always attach both ends or experience a blocked test.
+ * <li>FIFOs are not supported on some file systems. e.g. VFAT, so the test has to be particular
+ * about the temporary directory it uses to hold the FIFO.
+ * <li>Writes to FIFOs are buffered by the OS which makes blocking behavior more difficult to
+ * induce. See {@link ChannelWriter} and {@link StreamWriter}.
+ * </ol>
+ */
+public class FileIOInterruptTest extends TestCase {
+
+ private static File VOGAR_DEVICE_TEMP_DIR = new File("/data/data/file_io_interrupt_test");
+
+ private File fifoFile;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // This test relies on a FIFO file. The file system must support FIFOs, so we check the path.
+ String tmpDirName = System.getProperty("java.io.tmpdir");
+ File tmpDir;
+ if (tmpDirName.startsWith("/sdcard")) {
+ // Vogar execution on device runs in /sdcard. Unfortunately the file system used does not
+ // support FIFOs so the test must use one that is more likely to work.
+ if (!VOGAR_DEVICE_TEMP_DIR.exists()) {
+ assertTrue(VOGAR_DEVICE_TEMP_DIR.mkdir());
+ }
+ VOGAR_DEVICE_TEMP_DIR.deleteOnExit();
+ tmpDir = VOGAR_DEVICE_TEMP_DIR;
+ } else {
+ tmpDir = new File(tmpDirName);
+ }
+ fifoFile = new File(tmpDir, "fifo_file.tmp");
+ if (fifoFile.exists()) {
+ fifoFile.delete();
+ }
+ fifoFile.deleteOnExit();
+
+ // Create the fifo. This will throw an exception if the file system does not support it.
+ Libcore.os.mkfifo(fifoFile.getAbsolutePath(), OsConstants.S_IRWXU);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ fifoFile.delete();
+ VOGAR_DEVICE_TEMP_DIR.delete();
+ }
+
+ public void testStreamRead_exceptionWhenAlreadyClosed() throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+
+ FileInputStream fis = new FileInputStream(fifoFile);
+ fis.close();
+
+ byte[] buffer = new byte[10];
+ try {
+ fis.read(buffer);
+ fail();
+ } catch (IOException expected) {
+ assertSame(IOException.class, expected.getClass());
+ }
+
+ fifoWriter.tidyUp();
+ }
+
+ // This test fails on the RI: close() does not wake up a blocking FileInputStream.read() call.
+ public void testStreamRead_exceptionOnCloseWhenBlocked() throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+
+ FileInputStream fis = new FileInputStream(fifoFile);
+ StreamReader streamReader = new StreamReader(fis);
+ Thread streamReaderThread = createAndStartThread("StreamReader", streamReader);
+
+ // Delay until we can be fairly sure the reader thread is blocking.
+ streamReader.waitForThreadToBlock();
+
+ // Now close the OutputStream to see what happens.
+ fis.close();
+
+ // Test for expected behavior in the reader thread.
+ waitToDie(streamReaderThread);
+ assertSame(InterruptedIOException.class, streamReader.ioe.getClass());
+ assertFalse(streamReader.wasInterrupted);
+
+ // Tidy up the writer thread.
+ fifoWriter.tidyUp();
+ }
+
+ public void testStreamWrite_exceptionWhenAlreadyClosed() throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+
+ FileOutputStream fos = new FileOutputStream(fifoFile);
+ byte[] buffer = new byte[10];
+ fos.close();
+
+ try {
+ fos.write(buffer);
+ fail();
+ } catch (IOException expected) {
+ assertSame(IOException.class, expected.getClass());
+ }
+
+ fifoReader.tidyUp();
+ }
+
+ // This test fails on the RI: close() does not wake up a blocking FileInputStream.write() call.
+ public void testStreamWrite_exceptionOnCloseWhenBlocked() throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+
+ FileOutputStream fos = new FileOutputStream(fifoFile);
+ StreamWriter streamWriter = new StreamWriter(fos);
+ Thread streamWriterThread = createAndStartThread("StreamWriter", streamWriter);
+
+ // Delay until we can be fairly sure the writer thread is blocking.
+ streamWriter.waitForThreadToBlock();
+
+ // Now close the OutputStream to see what happens.
+ fos.close();
+
+ // Test for expected behavior in the writer thread.
+ waitToDie(streamWriterThread);
+ assertSame(InterruptedIOException.class, streamWriter.ioe.getClass());
+ assertFalse(streamWriter.wasInterrupted);
+
+ // Tidy up the reader thread.
+ fifoReader.tidyUp();
+ }
+
+ public void testChannelRead_exceptionWhenAlreadyClosed() throws Exception {
+ testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method.READ);
+ }
+
+ public void testChannelReadV_exceptionWhenAlreadyClosed() throws Exception {
+ testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method.READV);
+ }
+
+ private void testChannelRead_exceptionWhenAlreadyClosed(ChannelReader.Method method)
+ throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+ FileInputStream fis = new FileInputStream(fifoFile);
+ FileChannel fileInputChannel = fis.getChannel();
+ fileInputChannel.close();
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+ try {
+ if (method == ChannelReader.Method.READ) {
+ fileInputChannel.read(buffer);
+ } else {
+ ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+ fileInputChannel.read(new ByteBuffer[] { buffer, buffer2});
+ }
+ fail();
+ } catch (IOException expected) {
+ assertSame(ClosedChannelException.class, expected.getClass());
+ }
+
+ fifoWriter.tidyUp();
+ }
+
+ public void testChannelRead_exceptionOnCloseWhenBlocked() throws Exception {
+ testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method.READ);
+ }
+
+ public void testChannelReadV_exceptionOnCloseWhenBlocked() throws Exception {
+ testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method.READV);
+ }
+
+ private void testChannelRead_exceptionOnCloseWhenBlocked(ChannelReader.Method method)
+ throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+ FileInputStream fis = new FileInputStream(fifoFile);
+ FileChannel fileInputChannel = fis.getChannel();
+
+ ChannelReader channelReader = new ChannelReader(fileInputChannel, method);
+ Thread channelReaderThread = createAndStartThread("ChannelReader", channelReader);
+
+ // Delay until we can be fairly sure the reader thread is blocking.
+ channelReader.waitForThreadToBlock();
+
+ // Now close the FileChannel to see what happens.
+ fileInputChannel.close();
+
+ // Test for expected behavior in the reader thread.
+ waitToDie(channelReaderThread);
+ assertSame(AsynchronousCloseException.class, channelReader.ioe.getClass());
+ assertFalse(channelReader.wasInterrupted);
+
+ // Tidy up the writer thread.
+ fifoWriter.tidyUp();
+ }
+
+ public void testChannelRead_exceptionOnInterrupt() throws Exception {
+ testChannelRead_exceptionOnInterrupt(ChannelReader.Method.READ);
+ }
+
+ public void testChannelReadV_exceptionOnInterrupt() throws Exception {
+ testChannelRead_exceptionOnInterrupt(ChannelReader.Method.READV);
+ }
+
+ private void testChannelRead_exceptionOnInterrupt(ChannelReader.Method method) throws Exception {
+ FifoWriter fifoWriter = new FifoWriter(fifoFile);
+ fifoWriter.start();
+ FileChannel fileChannel = new FileInputStream(fifoFile).getChannel();
+
+ ChannelReader channelReader = new ChannelReader(fileChannel, method);
+ Thread channelReaderThread = createAndStartThread("ChannelReader", channelReader);
+
+ // Delay until we can be fairly sure the reader thread is blocking.
+ channelReader.waitForThreadToBlock();
+
+ // Now interrupt the reader thread to see what happens.
+ channelReaderThread.interrupt();
+
+ // Test for expected behavior in the reader thread.
+ waitToDie(channelReaderThread);
+ assertSame(ClosedByInterruptException.class, channelReader.ioe.getClass());
+ assertTrue(channelReader.wasInterrupted);
+
+ // Tidy up the writer thread.
+ fifoWriter.tidyUp();
+ }
+
+ public void testChannelWrite_exceptionWhenAlreadyClosed() throws Exception {
+ testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method.WRITE);
+ }
+
+ public void testChannelWriteV_exceptionWhenAlreadyClosed() throws Exception {
+ testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method.WRITEV);
+ }
+
+ private void testChannelWrite_exceptionWhenAlreadyClosed(ChannelWriter.Method method)
+ throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+ FileChannel fileOutputChannel = new FileOutputStream(fifoFile).getChannel();
+ fileOutputChannel.close();
+
+ ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+ try {
+ if (method == ChannelWriter.Method.WRITE) {
+ fileOutputChannel.write(buffer);
+ } else {
+ ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+ fileOutputChannel.write(new ByteBuffer[] { buffer, buffer2 });
+ }
+ fail();
+ } catch (IOException expected) {
+ assertSame(ClosedChannelException.class, expected.getClass());
+ }
+
+ fifoReader.tidyUp();
+ }
+
+ public void testChannelWrite_exceptionOnCloseWhenBlocked() throws Exception {
+ testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method.WRITE);
+ }
+
+ public void testChannelWriteV_exceptionOnCloseWhenBlocked() throws Exception {
+ testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method.WRITEV);
+ }
+
+ private void testChannelWrite_exceptionOnCloseWhenBlocked(ChannelWriter.Method method)
+ throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+ FileChannel fileOutputChannel = new FileOutputStream(fifoFile).getChannel();
+
+ ChannelWriter channelWriter = new ChannelWriter(fileOutputChannel, method);
+ Thread channelWriterThread = createAndStartThread("ChannelWriter", channelWriter);
+
+ // Delay until we can be fairly sure the writer thread is blocking.
+ channelWriter.waitForThreadToBlock();
+
+ // Now close the channel to see what happens.
+ fileOutputChannel.close();
+
+ // Test for expected behavior in the writer thread.
+ waitToDie(channelWriterThread);
+ // The RI throws ChannelClosedException. AsynchronousCloseException is more correct according to
+ // the docs.
+ assertSame(AsynchronousCloseException.class, channelWriter.ioe.getClass());
+ assertFalse(channelWriter.wasInterrupted);
+
+ // Tidy up the writer thread.
+ fifoReader.tidyUp();
+ }
+
+ public void testChannelWrite_exceptionOnInterrupt() throws Exception {
+ testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method.WRITE);
+ }
+
+ public void testChannelWriteV_exceptionOnInterrupt() throws Exception {
+ testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method.WRITEV);
+ }
+
+ private void testChannelWrite_exceptionOnInterrupt(ChannelWriter.Method method) throws Exception {
+ FifoReader fifoReader = new FifoReader(fifoFile);
+ fifoReader.start();
+
+ FileChannel fileChannel = new FileOutputStream(fifoFile).getChannel();
+ ChannelWriter channelWriter = new ChannelWriter(fileChannel, method);
+ Thread channelWriterThread = createAndStartThread("ChannelWriter", channelWriter);
+
+ // Delay until we can be fairly sure the writer thread is blocking.
+ channelWriter.waitForThreadToBlock();
+
+ // Now interrupt the writer thread to see what happens.
+ channelWriterThread.interrupt();
+
+ // Test for expected behavior in the writer thread.
+ waitToDie(channelWriterThread);
+ assertSame(ClosedByInterruptException.class, channelWriter.ioe.getClass());
+ assertTrue(channelWriter.wasInterrupted);
+
+ // Tidy up the reader thread.
+ fifoReader.tidyUp();
+ }
+
+ private static class StreamReader implements Runnable {
+
+ private final FileInputStream inputStream;
+ volatile boolean started;
+ volatile IOException ioe;
+ volatile boolean wasInterrupted;
+
+ StreamReader(FileInputStream inputStream) {
+ this.inputStream = inputStream;
+ }
+
+ @Override
+ public void run() {
+ byte[] buffer = new byte[10];
+ try {
+ started = true;
+ int bytesRead = inputStream.read(buffer);
+ fail("This isn't supposed to happen: read() returned: " + bytesRead);
+ } catch (IOException e) {
+ this.ioe = e;
+ }
+ wasInterrupted = Thread.interrupted();
+ }
+
+ public void waitForThreadToBlock() {
+ for (int i = 0; i < 10 && !started; i++) {
+ delay(100);
+ }
+ assertTrue(started);
+ // Just give it some more time to start blocking.
+ delay(100);
+ }
+ }
+
+ private static class StreamWriter implements Runnable {
+
+ private final FileOutputStream outputStream;
+ volatile int bytesWritten;
+ volatile IOException ioe;
+ volatile boolean wasInterrupted;
+
+ StreamWriter(FileOutputStream outputStream) {
+ this.outputStream = outputStream;
+ }
+
+ @Override
+ public void run() {
+ // Writes to FIFOs are buffered. We try to fill the buffer and induce blocking (the
+ // buffer is typically 64k).
+ byte[] buffer = new byte[10000];
+ while (true) {
+ try {
+ outputStream.write(buffer);
+ bytesWritten += buffer.length;
+ } catch (IOException e) {
+ this.ioe = e;
+ break;
+ }
+ wasInterrupted = Thread.interrupted();
+ }
+ }
+
+ public void waitForThreadToBlock() {
+ int lastCount = bytesWritten;
+ for (int i = 0; i < 10; i++) {
+ delay(500);
+ int newBytesWritten = bytesWritten;
+ if (newBytesWritten > 0 && lastCount == newBytesWritten) {
+ // The thread is probably blocking.
+ return;
+ }
+ lastCount = bytesWritten;
+ }
+ fail("Writer never started blocking. Bytes written: " + bytesWritten);
+ }
+ }
+
+ private static class ChannelReader implements Runnable {
+ enum Method {
+ READ,
+ READV,
+ }
+
+ private final FileChannel channel;
+ private final Method method;
+ volatile boolean started;
+ volatile IOException ioe;
+ volatile boolean wasInterrupted;
+
+ ChannelReader(FileChannel channel, Method method) {
+ this.channel = channel;
+ this.method = method;
+ }
+
+ @Override
+ public void run() {
+ ByteBuffer buffer = ByteBuffer.allocateDirect(10);
+ try {
+ started = true;
+ if (method == Method.READ) {
+ channel.read(buffer);
+ } else {
+ ByteBuffer buffer2 = ByteBuffer.allocateDirect(10);
+ channel.read(new ByteBuffer[] { buffer, buffer2 });
+ }
+ fail("All tests should block until an exception");
+ } catch (IOException e) {
+ this.ioe = e;
+ }
+ wasInterrupted = Thread.interrupted();
+ }
+
+ public void waitForThreadToBlock() {
+ for (int i = 0; i < 10 && !started; i++) {
+ delay(100);
+ }
+ assertTrue(started);
+ // Just give it some more time to start blocking.
+ delay(100);
+ }
+ }
+
+ private static class ChannelWriter implements Runnable {
+ enum Method {
+ WRITE,
+ WRITEV,
+ }
+
+ private final FileChannel channel;
+ private final Method method;
+ volatile int bytesWritten;
+ volatile IOException ioe;
+ volatile boolean wasInterrupted;
+
+ ChannelWriter(FileChannel channel, Method method) {
+ this.channel = channel;
+ this.method = method;
+ }
+
+ @Override
+ public void run() {
+ ByteBuffer buffer1 = ByteBuffer.allocateDirect(10000);
+ ByteBuffer buffer2 = ByteBuffer.allocateDirect(10000);
+ // Writes to FIFOs are buffered. We try to fill the buffer and induce blocking (the
+ // buffer is typically 64k).
+ while (true) {
+ // Make the buffers look non-empty.
+ buffer1.position(0).limit(buffer1.capacity());
+ buffer2.position(0).limit(buffer2.capacity());
+ try {
+ if (method == Method.WRITE) {
+ bytesWritten += channel.write(buffer1);
+ } else {
+ bytesWritten += channel.write(new ByteBuffer[]{ buffer1, buffer2 });
+ }
+ } catch (IOException e) {
+ this.ioe = e;
+ break;
+ }
+ }
+ wasInterrupted = Thread.interrupted();
+ }
+
+ public void waitForThreadToBlock() {
+ int lastCount = bytesWritten;
+ for (int i = 0; i < 10; i++) {
+ delay(500);
+ int newBytesWritten = bytesWritten;
+ if (newBytesWritten > 0 && lastCount == newBytesWritten) {
+ // The thread is probably blocking.
+ return;
+ }
+ lastCount = bytesWritten;
+ }
+ fail("Writer never started blocking. Bytes written: " + bytesWritten);
+ }
+ }
+
+ /**
+ * Opens a FIFO for writing. Exists to unblock the other end of the FIFO.
+ */
+ private static class FifoWriter extends Thread {
+
+ private final File file;
+ private FileOutputStream fos;
+
+ public FifoWriter(File file) {
+ super("FifoWriter");
+ this.file = file;
+ }
+
+ @Override
+ public void run() {
+ try {
+ fos = new FileOutputStream(file);
+ } catch (IOException ignored) {
+ }
+ }
+
+ public void tidyUp() {
+ FileIOInterruptTest.waitToDie(this);
+ closeQuietly(fos);
+ }
+ }
+
+ /**
+ * Opens a FIFO for reading. Exists to unblock the other end of the FIFO.
+ */
+ private static class FifoReader extends Thread {
+
+ private final File file;
+ private FileInputStream fis;
+
+ public FifoReader(File file) {
+ super("FifoReader");
+ this.file = file;
+ }
+
+ @Override
+ public void run() {
+ try {
+ fis = new FileInputStream(file);
+ } catch (IOException ignored) {
+ }
+ }
+
+ public void tidyUp() {
+ FileIOInterruptTest.waitToDie(this);
+ closeQuietly(fis);
+ }
+ }
+
+ private static Thread createAndStartThread(String name, Runnable runnable) {
+ Thread t = new Thread(runnable, name);
+ t.setDaemon(true);
+ t.start();
+ return t;
+ }
+
+ private static void waitToDie(Thread thread) {
+ try {
+ thread.join(5000);
+ } catch (InterruptedException ignored) {
+ }
+
+ if (thread.isAlive()) {
+ fail("Thread \"" + thread.getName() + "\" did not exit.");
+ }
+ }
+
+ private static void delay(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+}