Index: runtime/bin/socket_patch.dart |
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart |
index 7dbc01ad4bac130ac2fa5df91ba11122161170df..a49895484054fc3427b3e85ad2b4c95c5189415f 100644 |
--- a/runtime/bin/socket_patch.dart |
+++ b/runtime/bin/socket_patch.dart |
@@ -211,6 +211,20 @@ class _NetworkInterface implements NetworkInterface { |
// implicit constructor. |
class _NativeSocketNativeWrapper extends NativeFieldWrapperClass1 {} |
+class _ConnectingSocket { |
+ final _NativeSocket socket; |
+ final Timer timer; |
+ |
+ _ConnectingSocket(this.socket, this.timer); |
+ |
+ void abort() { |
+ timer.cancel(); |
+ socket.close(); |
+ socket.setHandlers(); |
+ socket.setListening(read: false, write: false); |
+ } |
+} |
+ |
// The _NativeSocket class encapsulates an OS socket. |
class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { |
@@ -273,6 +287,10 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { |
static const int NORMAL_TOKEN_BATCH_SIZE = 8; |
static const int LISTENING_TOKEN_BATCH_SIZE = 2; |
+ static const Duration _RETRY_DURATION = const Duration(milliseconds: 250); |
+ static const Duration _RETRY_DURATION_LOOPBACK = |
+ const Duration(milliseconds: 25); |
+ |
// Use default Map so we keep order. |
static Map<int, _NativeSocket> _sockets = new Map<int, _NativeSocket>(); |
@@ -390,12 +408,17 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { |
}) |
.then((addresses) { |
assert(addresses is List); |
+ assert(addresses.isNotEmpty); |
Bill Hesse
2014/07/29 10:57:06
This assert is covered by the check in the above f
Anders Johnsen
2014/07/29 11:23:27
Done.
|
var completer = new Completer(); |
var it = addresses.iterator; |
- void run(error) { |
+ var error = null; |
+ var connecting = new HashSet(); |
+ void connectNext() { |
if (!it.moveNext()) { |
- assert(error != null); |
- completer.completeError(error); |
+ if (connecting.isEmpty) { |
+ assert(error != null); |
+ completer.completeError(error); |
+ } |
return; |
} |
var address = it.current; |
@@ -404,27 +427,42 @@ class _NativeSocket extends _NativeSocketNativeWrapper with _ServiceObject { |
var result = socket.nativeCreateConnect(address._in_addr, port); |
if (result is OSError) { |
// Keep first error, if present. |
- run(error != null ? error : |
- createError(result, "Connection failed", address, port)); |
+ if (error == null) { |
+ error = createError(result, "Connection failed", address, port); |
+ } |
+ connectNext(); |
} else { |
socket.port; // Query the local port, for error messages. |
+ // Set up timer for when we should retry the next address (if any). |
+ var duration = address.isLoopback ? |
+ _RETRY_DURATION_LOOPBACK : |
+ _RETRY_DURATION; |
+ var timer = new Timer(duration, connectNext); |
+ var connectingSocket = new _ConnectingSocket(socket, timer); |
+ connecting.add(connectingSocket); |
Bill Hesse
2014/07/29 10:57:06
If you used a Map, rather than a Set, then this wo
Anders Johnsen
2014/07/29 11:23:27
Acknowledged.
|
// Setup handlers for receiving the first write event which |
// indicate that the socket is fully connected. |
socket.setHandlers( |
write: () { |
+ timer.cancel(); |
socket.setListening(read: false, write: false); |
completer.complete(socket); |
+ connecting.remove(connectingSocket); |
+ for (var s in connecting) { |
+ s.abort(); |
+ } |
}, |
error: (e) { |
- socket.close(); |
+ connectingSocket.abort(); |
// Keep first error, if present. |
- run(error != null ? error : e); |
- } |
- ); |
+ if (error == null) error = e; |
+ connecting.remove(connectingSocket); |
+ if (connecting.isEmpty) connectNext(); |
+ }); |
socket.setListening(read: false, write: true); |
} |
} |
- run(null); |
+ connectNext(); |
return completer.future; |
}); |
} |