| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.isolate; | 5 part of dart.isolate; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * The initial [IsolateStream] available by default for this isolate. This | 8 * The initial [IsolateStream] available by default for this isolate. This |
| 9 * [IsolateStream] is created automatically and it is commonly used to establish | 9 * [IsolateStream] is created automatically and it is commonly used to establish |
| 10 * the first communication between isolates (see [streamSpawnFunction] and | 10 * the first communication between isolates (see [streamSpawnFunction] and |
| 11 * [streamSpawnUri]). | 11 * [streamSpawnUri]). |
| 12 */ | 12 */ |
| 13 final IsolateStream stream = new IsolateStream._fromOriginalReceivePort(port); | 13 final IsolateStream stream = new IsolateStream._fromOriginalReceivePort(port); |
| 14 | 14 |
| 15 /** | 15 /** |
| 16 * A [MessageBox] creates an [IsolateStream], [stream], and an [IsolateSink], | 16 * A [MessageBox] creates an [IsolateStream], [stream], and an [IsolateSink], |
| 17 * [sink]. | 17 * [sink]. |
| 18 * | 18 * |
| 19 * Any message that is written into the [sink] (independent of the isolate) is | 19 * Any message that is written into the [sink] (independent of the isolate) is |
| 20 * sent to the [stream] where its subscribers can react to the messages. | 20 * sent to the [stream] where its subscribers can react to the messages. |
| 21 */ | 21 */ |
| 22 class MessageBox { | 22 class MessageBox { |
| 23 final IsolateStream stream; | 23 final IsolateStream stream; |
| 24 final IsolateSink sink; | 24 final IsolateSink sink; |
| 25 | 25 |
| 26 MessageBox.oneShot() : this._oneShot(new ReceivePort()); | 26 external MessageBox.oneShot(); |
| 27 MessageBox._oneShot(ReceivePort receivePort) | 27 external MessageBox(); |
| 28 : stream = new IsolateStream._fromOriginalReceivePortOneShot(receivePort), | |
| 29 sink = new IsolateSink._fromPort(receivePort.toSendPort()); | |
| 30 | |
| 31 MessageBox() : this._(new ReceivePort()); | |
| 32 MessageBox._(ReceivePort receivePort) | |
| 33 : stream = new IsolateStream._fromOriginalReceivePort(receivePort), | |
| 34 sink = new IsolateSink._fromPort(receivePort.toSendPort()); | |
| 35 } | 28 } |
| 36 | 29 |
| 37 // Used for mangling. | 30 external bool _isCloseToken(var object); |
| 38 const int _ISOLATE_STREAM_TOKEN = 132421119; | |
| 39 | |
| 40 class _CloseToken { | |
| 41 /// This token is sent from [IsolateSink]s to [IsolateStream]s to ask them to | |
| 42 /// close themselves. | |
| 43 const _CloseToken(); | |
| 44 } | |
| 45 | 31 |
| 46 /** | 32 /** |
| 47 * [IsolateStream]s, together with [IsolateSink]s, are the only means of | 33 * [IsolateStream]s, together with [IsolateSink]s, are the only means of |
| 48 * communication between isolates. Each IsolateStream has a corresponding | 34 * communication between isolates. Each IsolateStream has a corresponding |
| 49 * [IsolateSink]. Any message written into that sink will be delivered to | 35 * [IsolateSink]. Any message written into that sink will be delivered to |
| 50 * the stream and then dispatched to the stream's subscribers. | 36 * the stream and then dispatched to the stream's subscribers. |
| 51 */ | 37 */ |
| 52 class IsolateStream extends Stream<dynamic> { | 38 class IsolateStream extends Stream<dynamic> { |
| 53 bool _isClosed = false; | 39 bool _isClosed = false; |
| 54 final ReceivePort _port; | 40 final ReceivePort _port; |
| 55 StreamController _controller = new StreamController.broadcast(); | 41 StreamController _controller = new StreamController.broadcast(); |
| 56 | 42 |
| 57 IsolateStream._fromOriginalReceivePort(this._port) { | 43 IsolateStream._fromOriginalReceivePort(this._port) { |
| 58 _port.receive((message, replyTo) { | 44 _port.receive((message, replyTo) { |
| 59 assert(replyTo == null); | 45 assert(replyTo == null); |
| 60 _add(message); | 46 _add(message); |
| 61 }); | 47 }); |
| 62 } | 48 } |
| 63 | 49 |
| 64 IsolateStream._fromOriginalReceivePortOneShot(this._port) { | 50 IsolateStream._fromOriginalReceivePortOneShot(this._port) { |
| 65 _port.receive((message, replyTo) { | 51 _port.receive((message, replyTo) { |
| 66 assert(replyTo == null); | 52 assert(replyTo == null); |
| 67 _add(message); | 53 _add(message); |
| 68 close(); | 54 close(); |
| 69 }); | 55 }); |
| 70 } | 56 } |
| 71 | 57 |
| 72 void _add(var message) { | 58 void _add(var message) { |
| 73 message = _unmangleMessage(message); | 59 if (_isCloseToken(message)) { |
| 74 if (identical(message, const _CloseToken())) { | |
| 75 close(); | 60 close(); |
| 76 } else { | 61 } else { |
| 77 _controller.sink.add(message); | 62 _controller.sink.add(message); |
| 78 } | 63 } |
| 79 } | 64 } |
| 80 | 65 |
| 81 /** | 66 /** |
| 82 * Close the stream from the receiving end. | 67 * Close the stream from the receiving end. |
| 83 * | 68 * |
| 84 * Closing an already closed port has no effect. | 69 * Closing an already closed port has no effect. |
| 85 */ | 70 */ |
| 86 void close() { | 71 void close() { |
| 87 if (!_isClosed) { | 72 if (!_isClosed) { |
| 88 _isClosed = true; | 73 _isClosed = true; |
| 89 _port.close(); | 74 _port.close(); |
| 90 _controller.close(); | 75 _controller.close(); |
| 91 } | 76 } |
| 92 } | 77 } |
| 93 | 78 |
| 94 StreamSubscription listen(void onData(event), | 79 StreamSubscription listen(void onData(event), |
| 95 { void onError(AsyncError error), | 80 { void onError(AsyncError error), |
| 96 void onDone(), | 81 void onDone(), |
| 97 bool unsubscribeOnError}) { | 82 bool unsubscribeOnError}) { |
| 98 return _controller.stream.listen(onData, | 83 return _controller.stream.listen(onData, |
| 99 onError: onError, | 84 onError: onError, |
| 100 onDone: onDone, | 85 onDone: onDone, |
| 101 unsubscribeOnError: unsubscribeOnError); | 86 unsubscribeOnError: unsubscribeOnError); |
| 102 } | 87 } |
| 103 | |
| 104 dynamic _unmangleMessage(var message) { | |
| 105 _IsolateDecoder decoder = new _IsolateDecoder( | |
| 106 _ISOLATE_STREAM_TOKEN, | |
| 107 (data) { | |
| 108 if (data is! List) return data; | |
| 109 if (data.length == 2 && data[0] == "Sink" && data[1] is SendPort) { | |
| 110 return new IsolateSink._fromPort(data[1]); | |
| 111 } | |
| 112 if (data.length == 1 && data[0] == "Close") { | |
| 113 return const _CloseToken(); | |
| 114 } | |
| 115 return data; | |
| 116 }); | |
| 117 return decoder.decode(message); | |
| 118 } | |
| 119 } | 88 } |
| 120 | 89 |
| 121 /** | 90 /** |
| 122 * [IsolateSink]s represent the feed for [IsolateStream]s. Any message written | 91 * [IsolateSink]s represent the feed for [IsolateStream]s. Any message written |
| 123 * to [this] is delivered to its respective [IsolateStream]. [IsolateSink]s are | 92 * to [this] is delivered to its respective [IsolateStream]. [IsolateSink]s are |
| 124 * created by [MessageBox]es. | 93 * created by [MessageBox]es. |
| 125 * | 94 * |
| 126 * [IsolateSink]s can be transmitted to other isolates. | 95 * [IsolateSink]s can be transmitted to other isolates. |
| 127 */ | 96 */ |
| 128 class IsolateSink extends StreamSink<dynamic> { | 97 abstract class IsolateSink extends StreamSink<dynamic> { |
| 129 // TODO(8997): Implement EventSink instead. | 98 // TODO(8997): Implement EventSink instead. |
| 130 bool _isClosed = false; | 99 // TODO(floitsch): Actually it should be a StreamSink (being able to flow- |
| 131 final SendPort _port; | 100 // control). |
| 132 IsolateSink._fromPort(this._port); | |
| 133 | 101 |
| 134 /** | 102 /** |
| 135 * Sends an asynchronous [message] to the linked [IsolateStream]. The message | 103 * Sends an asynchronous [message] to the linked [IsolateStream]. The message |
| 136 * is copied to the receiving isolate. | 104 * is copied to the receiving isolate. |
| 137 * | 105 * |
| 138 * The content of [message] can be: primitive values (null, num, bool, double, | 106 * The content of [message] can be: primitive values (null, num, bool, double, |
| 139 * String), instances of [IsolateSink]s, and lists and maps whose elements are | 107 * String), instances of [IsolateSink]s, and lists and maps whose elements are |
| 140 * any of these. List and maps are also allowed to be cyclic. | 108 * any of these. List and maps are also allowed to be cyclic. |
| 141 * | 109 * |
| 142 * In the special circumstances when two isolates share the same code and are | 110 * In the special circumstances when two isolates share the same code and are |
| 143 * running in the same process (e.g. isolates created via [spawnFunction]), it | 111 * running in the same process (e.g. isolates created via [spawnFunction]), it |
| 144 * is also possible to send object instances (which would be copied in the | 112 * is also possible to send object instances (which would be copied in the |
| 145 * process). This is currently only supported by the dartvm. For now, the | 113 * process). This is currently only supported by the dartvm. For now, the |
| 146 * dart2js compiler only supports the restricted messages described above. | 114 * dart2js compiler only supports the restricted messages described above. |
| 147 */ | 115 */ |
| 148 void add(dynamic message) { | 116 void add(dynamic message); |
| 149 var mangled = _mangleMessage(message); | |
| 150 _port.send(mangled); | |
| 151 } | |
| 152 | 117 |
| 153 void addError(AsyncError errorEvent) { | 118 void addError(AsyncError errorEvent); |
| 154 throw new UnimplementedError("signalError on isolate streams"); | |
| 155 } | |
| 156 | 119 |
| 157 dynamic _mangleMessage(var message) { | 120 /** Closing multiple times is allowed. */ |
| 158 _IsolateEncoder encoder = new _IsolateEncoder( | 121 void close(); |
| 159 _ISOLATE_STREAM_TOKEN, | |
| 160 (data) { | |
| 161 if (data is IsolateSink) return ["Sink", data._port]; | |
| 162 if (identical(data, const _CloseToken())) return ["Close"]; | |
| 163 return data; | |
| 164 }); | |
| 165 return encoder.encode(message); | |
| 166 } | |
| 167 | |
| 168 void close() { | |
| 169 if (_isClosed) throw new StateError("Sending on closed stream"); | |
| 170 add(const _CloseToken()); | |
| 171 _isClosed = true; | |
| 172 } | |
| 173 | 122 |
| 174 /** | 123 /** |
| 175 * Tests whether [other] is an [IsolateSink] feeding into the same | 124 * Tests whether [other] is an [IsolateSink] feeding into the same |
| 176 * [IsolateStream] as this one. | 125 * [IsolateStream] as this one. |
| 177 */ | 126 */ |
| 178 bool operator==(var other) { | 127 bool operator==(var other); |
| 179 return other is IsolateSink && _port == other._port; | |
| 180 } | |
| 181 | |
| 182 int get hashCode => _port.hashCode + 499; | |
| 183 } | 128 } |
| 184 | 129 |
| 185 | 130 |
| 186 /** | 131 /** |
| 187 * Creates and spawns an isolate that shares the same code as the current | 132 * Creates and spawns an isolate that shares the same code as the current |
| 188 * isolate, but that starts from [topLevelFunction]. The [topLevelFunction] | 133 * isolate, but that starts from [topLevelFunction]. The [topLevelFunction] |
| 189 * argument must be a static top-level function or a static method that takes no | 134 * argument must be a static top-level function or a static method that takes no |
| 190 * arguments. | 135 * arguments. |
| 191 * | 136 * |
| 192 * When any isolate starts (even the main script of the application), a default | 137 * When any isolate starts (even the main script of the application), a default |
| 193 * [IsolateStream] is created for it. This sink is available from the top-level | 138 * [IsolateStream] is created for it. This sink is available from the top-level |
| 194 * getter [stream] defined in this library. | 139 * getter [stream] defined in this library. |
| 195 * | 140 * |
| 196 * [spawnFunction] returns an [IsolateSink] feeding into the child isolate's | 141 * [spawnFunction] returns an [IsolateSink] feeding into the child isolate's |
| 197 * default stream. | 142 * default stream. |
| 198 * | 143 * |
| 199 * The optional [unhandledExceptionCallback] argument is invoked whenever an | 144 * The optional [unhandledExceptionCallback] argument is invoked whenever an |
| 200 * exception inside the isolate is unhandled. It can be seen as a big | 145 * exception inside the isolate is unhandled. It can be seen as a big |
| 201 * `try/catch` around everything that is executed inside the isolate. The | 146 * `try/catch` around everything that is executed inside the isolate. The |
| 202 * callback should return `true` when it was able to handled the exception. | 147 * callback should return `true` when it was able to handled the exception. |
| 203 * | 148 * |
| 204 * See comments at the top of this library for more details. | 149 * See comments at the top of this library for more details. |
| 205 */ | 150 */ |
| 206 IsolateSink streamSpawnFunction( | 151 external IsolateSink streamSpawnFunction( |
| 207 void topLevelFunction(), | 152 void topLevelFunction(), |
| 208 [bool unhandledExceptionCallback(IsolateUnhandledException e)]) { | 153 [bool unhandledExceptionCallback(IsolateUnhandledException e)]); |
| 209 SendPort sendPort = spawnFunction(topLevelFunction, | |
| 210 unhandledExceptionCallback); | |
| 211 return new IsolateSink._fromPort(sendPort); | |
| 212 } | |
| 213 | |
| 214 /** | |
| 215 * Creates and spawns an isolate whose code is available at [uri]. Like with | |
| 216 * [streamSpawnFunction], the child isolate will have a default [IsolateStream], | |
| 217 * and a this function returns an [IsolateSink] feeding into it. | |
| 218 * | |
| 219 * See comments at the top of this library for more details. | |
| 220 */ | |
| 221 IsolateSink streamSpawnUri(String uri) { | |
| 222 SendPort sendPort = spawnUri(uri); | |
| 223 return new IsolateSink._fromPort(sendPort); | |
| 224 } | |
| OLD | NEW |