diff options
12 files changed, 96 insertions, 64 deletions
diff --git a/dalvik/src/main/java/dalvik/system/BlockGuard.java b/dalvik/src/main/java/dalvik/system/BlockGuard.java index 119833b..fd4be27 100644 --- a/dalvik/src/main/java/dalvik/system/BlockGuard.java +++ b/dalvik/src/main/java/dalvik/system/BlockGuard.java @@ -195,11 +195,6 @@ public final class BlockGuard { return mNetwork.writeDirect(fd, address, offset, count); } - public boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws IOException { - BlockGuard.getThreadPolicy().onNetwork(); - return mNetwork.connect(fd, inetAddress, port); - } - public boolean isConnected(FileDescriptor fd, int timeout) throws IOException { if (timeout != 0) { // Greater than 0 is a timeout, but zero means "poll and return immediately". diff --git a/luni/src/main/java/java/net/ConnectException.java b/luni/src/main/java/java/net/ConnectException.java index 554316f..4fd478e 100644 --- a/luni/src/main/java/java/net/ConnectException.java +++ b/luni/src/main/java/java/net/ConnectException.java @@ -26,18 +26,23 @@ public class ConnectException extends SocketException { private static final long serialVersionUID = 3831404271622369215L; /** - * This implementation does nothing. + * Constructs a new instance. */ public ConnectException() { } /** - * This implementation does nothing. - * - * @param detailMessage - * detail message of the exception. + * Constructs a new instance with the given detail message. */ public ConnectException(String detailMessage) { super(detailMessage); } + + /** + * Constructs a new instance with the given detail message and cause. + * @hide + */ + public ConnectException(String detailMessage, Throwable cause) { + super(detailMessage); + } } diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java index 34eec20..346984e 100644 --- a/luni/src/main/java/java/net/PlainSocketImpl.java +++ b/luni/src/main/java/java/net/PlainSocketImpl.java @@ -172,14 +172,10 @@ public class PlainSocketImpl extends SocketImpl { */ private void connect(InetAddress anAddr, int aPort, int timeout) throws IOException { InetAddress normalAddr = anAddr.isAnyLocalAddress() ? InetAddress.getLocalHost() : anAddr; - try { - if (streaming && usingSocks()) { - socksConnect(anAddr, aPort, 0); - } else { - IoUtils.connect(fd, normalAddr, aPort, timeout); - } - } catch (ConnectException e) { - throw new ConnectException(anAddr + " (port " + aPort + "): " + e.getMessage()); + if (streaming && usingSocks()) { + socksConnect(anAddr, aPort, 0); + } else { + IoUtils.connect(fd, normalAddr, aPort, timeout); } super.address = normalAddr; super.port = aPort; diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java index 601cc11..2c5530c 100644 --- a/luni/src/main/java/libcore/io/BlockGuardOs.java +++ b/luni/src/main/java/libcore/io/BlockGuardOs.java @@ -18,6 +18,7 @@ package libcore.io; import dalvik.system.BlockGuard; import java.io.FileDescriptor; +import java.net.InetAddress; import java.nio.ByteBuffer; import static libcore.io.OsConstants.*; @@ -29,6 +30,11 @@ public class BlockGuardOs extends ForwardingOs { super(os); } + public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { + BlockGuard.getThreadPolicy().onNetwork(); + os.connect(fd, address, port); + } + public void fdatasync(FileDescriptor fd) throws ErrnoException { BlockGuard.getThreadPolicy().onWriteToDisk(); os.fdatasync(fd); diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java index 68dfcf3..832ffba 100644 --- a/luni/src/main/java/libcore/io/ForwardingOs.java +++ b/luni/src/main/java/libcore/io/ForwardingOs.java @@ -37,6 +37,7 @@ public class ForwardingOs implements Os { public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.bind(fd, address, port); } public void chmod(String path, int mode) throws ErrnoException { os.chmod(path, mode); } public void close(FileDescriptor fd) throws ErrnoException { os.close(fd); } + public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException { os.connect(fd, address, port); } public String[] environ() { return os.environ(); } public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException { return os.fcntlVoid(fd, cmd); } public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException { return os.fcntlLong(fd, cmd, arg); } diff --git a/luni/src/main/java/libcore/io/IoUtils.java b/luni/src/main/java/libcore/io/IoUtils.java index e2b31a7..1247ef6 100644 --- a/luni/src/main/java/libcore/io/IoUtils.java +++ b/luni/src/main/java/libcore/io/IoUtils.java @@ -22,6 +22,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.net.BindException; +import java.net.ConnectException; import java.net.InetAddress; import java.net.Inet4Address; import java.net.Inet6Address; @@ -238,6 +239,8 @@ public final class IoUtils { public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException { try { return connectErrno(fd, inetAddress, port, timeoutMs); + } catch (ErrnoException errnoException) { + throw new ConnectException(connectDetail(inetAddress, port, timeoutMs) + ": " + errnoException.getMessage(), errnoException); } catch (SocketException ex) { throw ex; // We don't want to doubly wrap these. } catch (SocketTimeoutException ex) { @@ -247,11 +250,11 @@ public final class IoUtils { } } - // TODO: this is the wrong name now, but when this gets rewritten without Platform.NETWORK... private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws IOException { // With no timeout, just call connect(2) directly. if (timeoutMs == 0) { - return Platform.NETWORK.connect(fd, inetAddress, port); + Libcore.os.connect(fd, inetAddress, port); + return true; } // With a timeout, we set the socket to non-blocking, connect(2), and then loop @@ -260,18 +263,20 @@ public final class IoUtils { long finishTimeMs = System.currentTimeMillis() + timeoutMs; IoUtils.setBlocking(fd, false); try { - if (Platform.NETWORK.connect(fd, inetAddress, port)) { - return true; + try { + Libcore.os.connect(fd, inetAddress, port); + return true; // We connected immediately. + } catch (ErrnoException errnoException) { + if (errnoException.errno != EINPROGRESS) { + throw errnoException; + } + // EINPROGRESS means we should keep trying... } int remainingTimeoutMs; do { remainingTimeoutMs = (int) (finishTimeMs - System.currentTimeMillis()); if (remainingTimeoutMs <= 0) { - String detail = "failed to connect to " + inetAddress + " (port " + port + ")"; - if (timeoutMs > 0) { - detail += " after " + timeoutMs + "ms"; - } - throw new SocketTimeoutException(detail); + throw new SocketTimeoutException(connectDetail(inetAddress, port, timeoutMs)); } } while (!Platform.NETWORK.isConnected(fd, remainingTimeoutMs)); return true; // Or we'd have thrown. @@ -280,6 +285,14 @@ public final class IoUtils { } } + private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs) { + String detail = "failed to connect to " + inetAddress + " (port " + port + ")"; + if (timeoutMs > 0) { + detail += " after " + timeoutMs + "ms"; + } + return detail; + } + /** * Sets 'fd' to be blocking or non-blocking, according to the state of 'blocking'. */ diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java index a864176..391a6fa 100644 --- a/luni/src/main/java/libcore/io/Os.java +++ b/luni/src/main/java/libcore/io/Os.java @@ -28,6 +28,7 @@ public interface Os { public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException; public void chmod(String path, int mode) throws ErrnoException; public void close(FileDescriptor fd) throws ErrnoException; + public void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException; public String[] environ(); public int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException; public int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException; diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java index 8640212..8451503 100644 --- a/luni/src/main/java/libcore/io/Posix.java +++ b/luni/src/main/java/libcore/io/Posix.java @@ -31,6 +31,7 @@ public final class Posix implements Os { public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException; public native void chmod(String path, int mode) throws ErrnoException; public native void close(FileDescriptor fd) throws ErrnoException; + public native void connect(FileDescriptor fd, InetAddress address, int port) throws ErrnoException; public native String[] environ(); public native int fcntlVoid(FileDescriptor fd, int cmd) throws ErrnoException; public native int fcntlLong(FileDescriptor fd, int cmd, long arg) throws ErrnoException; diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java index 6628005..23e3be3 100644 --- a/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java +++ b/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java @@ -39,7 +39,6 @@ public interface INetworkSystem { public int writeDirect(FileDescriptor fd, int address, int offset, int count) throws IOException; - public boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws IOException; public boolean isConnected(FileDescriptor fd, int timeout) throws IOException; public int send(FileDescriptor fd, byte[] data, int offset, int length, diff --git a/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java b/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java index 88b39f3..b369c6d 100644 --- a/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java +++ b/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java @@ -44,7 +44,6 @@ final class OSNetworkSystem implements INetworkSystem { public native void accept(FileDescriptor serverFd, SocketImpl newSocket, FileDescriptor clientFd) throws IOException; - public native boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws IOException; public native boolean isConnected(FileDescriptor fd, int timeout) throws IOException; public native void disconnectDatagram(FileDescriptor fd) throws SocketException; diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp index 7e2c8b6..9be8fd8 100644 --- a/luni/src/main/native/libcore_io_Posix.cpp +++ b/luni/src/main/native/libcore_io_Posix.cpp @@ -16,6 +16,7 @@ #define LOG_TAG "Posix" +#include "AsynchronousSocketCloseMonitor.h" #include "JNIHelp.h" #include "JniConstants.h" #include "JniException.h" @@ -57,6 +58,43 @@ struct addrinfo_deleter { } }; +/** + * Used to retry syscalls that can return EINTR. This differs from TEMP_FAILURE_RETRY in that + * it also considers the case where the reason for failure is that another thread called + * Socket.close. + * + * Assumes 'JNIEnv* env' and 'jobject javaFd' (which is a java.io.FileDescriptor) are in scope. + * + * Returns the result of 'exp', though a Java exception will be pending if the result is -1. + * + * Correct usage looks like this: + * + * void Posix_syscall(JNIEnv* env, jobject javaFd, ...) { + * ... + * int fd; + * NET_FAILURE_RETRY("syscall", syscall(fd, ...)); // Throws on error. + * } + */ +#define NET_FAILURE_RETRY(syscall_name, exp) ({ \ + typeof (exp) _rc = -1; \ + do { \ + { \ + fd = jniGetFDFromFileDescriptor(env, javaFd); \ + AsynchronousSocketCloseMonitor monitor(fd); \ + _rc = (exp); \ + } \ + if (_rc == -1) { \ + if (jniGetFDFromFileDescriptor(env, javaFd) == -1) { \ + jniThrowException(env, "java/net/SocketException", "Socket closed"); \ + break; \ + } else if (errno != EINTR) { \ + throwErrnoException(env, syscall_name); \ + break; \ + } \ + } \ + } while (_rc == -1); \ + _rc; }) + static void throwException(JNIEnv* env, jclass exceptionClass, jmethodID ctor3, jmethodID ctor2, const char* functionName, int error) { jthrowable cause = NULL; @@ -290,9 +328,9 @@ static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) { return; } - int fd = jniGetFDFromFileDescriptor(env, javaFd); + int fd; const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss); - throwIfMinusOne(env, "bind", TEMP_FAILURE_RETRY(bind(fd, sa, sizeof(sockaddr_storage)))); + NET_FAILURE_RETRY("bind", bind(fd, sa, sizeof(sockaddr_storage))); } static void Posix_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) { @@ -315,6 +353,16 @@ static void Posix_close(JNIEnv* env, jobject, jobject javaFd) { throwIfMinusOne(env, "close", close(fd)); } +static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) { + sockaddr_storage ss; + if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) { + return; + } + int fd; + const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss); + NET_FAILURE_RETRY("connect", connect(fd, sa, sizeof(sockaddr_storage))); +} + static jobjectArray Posix_environ(JNIEnv* env, jobject) { extern char** environ; // Standard, but not in any header file. return toStringArray(env, environ); @@ -932,6 +980,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"), NATIVE_METHOD(Posix, chmod, "(Ljava/lang/String;I)V"), NATIVE_METHOD(Posix, close, "(Ljava/io/FileDescriptor;)V"), + NATIVE_METHOD(Posix, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"), NATIVE_METHOD(Posix, environ, "()[Ljava/lang/String;"), NATIVE_METHOD(Posix, fcntlVoid, "(Ljava/io/FileDescriptor;I)I"), NATIVE_METHOD(Posix, fcntlLong, "(Ljava/io/FileDescriptor;IJ)I"), diff --git a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp index 73901a8..150f76a 100644 --- a/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp +++ b/luni/src/main/native/org_apache_harmony_luni_platform_OSNetworkSystem.cpp @@ -134,38 +134,6 @@ static jint OSNetworkSystem_write(JNIEnv* env, jobject, return result; } -static jboolean OSNetworkSystem_connect(JNIEnv* env, jobject, jobject fileDescriptor, jobject inetAddr, jint port) { - NetFd fd(env, fileDescriptor); - if (fd.isClosed()) { - return JNI_FALSE; - } - - sockaddr_storage ss; - if (!inetAddressToSockaddr(env, inetAddr, port, &ss)) { - return JNI_FALSE; - } - - // Initiate a connection attempt... - int rc = connect(fd.get(), reinterpret_cast<const sockaddr*>(&ss), sizeof(sockaddr_storage)); - int connectErrno = errno; - - // Did we get interrupted? - if (fd.isClosed()) { - return JNI_FALSE; - } - - // Did we fail to connect? - if (rc == -1) { - if (connectErrno != EINPROGRESS) { - throwConnectException(env, connectErrno); // Permanent failure, so throw. - } - return JNI_FALSE; - } - - // We connected straight away! - return JNI_TRUE; -} - static jboolean OSNetworkSystem_isConnected(JNIEnv* env, jobject, jobject fileDescriptor, jint timeout) { NetFd netFd(env, fileDescriptor); if (netFd.isClosed()) { @@ -585,7 +553,6 @@ static void OSNetworkSystem_close(JNIEnv* env, jobject, jobject fileDescriptor) static JNINativeMethod gMethods[] = { NATIVE_METHOD(OSNetworkSystem, accept, "(Ljava/io/FileDescriptor;Ljava/net/SocketImpl;Ljava/io/FileDescriptor;)V"), NATIVE_METHOD(OSNetworkSystem, close, "(Ljava/io/FileDescriptor;)V"), - NATIVE_METHOD(OSNetworkSystem, connect, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)Z"), NATIVE_METHOD(OSNetworkSystem, disconnectDatagram, "(Ljava/io/FileDescriptor;)V"), NATIVE_METHOD(OSNetworkSystem, isConnected, "(Ljava/io/FileDescriptor;I)Z"), NATIVE_METHOD(OSNetworkSystem, read, "(Ljava/io/FileDescriptor;[BII)I"), |