Chromium Code Reviews| 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; |
| }); |
| } |