| 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.async; | 5 part of dart.async; |
| 6 | 6 |
| 7 // ------------------------------------------------------------------- | 7 // ------------------------------------------------------------------- |
| 8 // Controller for creating and adding events to a stream. | 8 // Controller for creating and adding events to a stream. |
| 9 // ------------------------------------------------------------------- | 9 // ------------------------------------------------------------------- |
| 10 | 10 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 39 * Whether to invoke a callback depends only on the state before and after | 39 * Whether to invoke a callback depends only on the state before and after |
| 40 * a stream action, for example firing an event. If the state changes multiple | 40 * a stream action, for example firing an event. If the state changes multiple |
| 41 * times during the action, and then ends up in the same state as before, no | 41 * times during the action, and then ends up in the same state as before, no |
| 42 * callback is performed. | 42 * callback is performed. |
| 43 * | 43 * |
| 44 * If listeners are added after the stream has completed (sent a "done" event), | 44 * If listeners are added after the stream has completed (sent a "done" event), |
| 45 * the listeners will be sent a "done" event eventually, but they won't affect | 45 * the listeners will be sent a "done" event eventually, but they won't affect |
| 46 * the stream at all, and won't trigger callbacks. From the controller's point | 46 * the stream at all, and won't trigger callbacks. From the controller's point |
| 47 * of view, the stream is completely inert when has completed. | 47 * of view, the stream is completely inert when has completed. |
| 48 */ | 48 */ |
| 49 abstract class StreamController<T> implements StreamSink<T> { | 49 abstract class StreamController<T> implements EventSink<T> { |
| 50 /** The stream that this controller is controlling. */ | 50 /** The stream that this controller is controlling. */ |
| 51 Stream<T> get stream; | 51 Stream<T> get stream; |
| 52 | 52 |
| 53 /** | 53 /** |
| 54 * A controller with a [stream] that supports only one single subscriber. | 54 * A controller with a [stream] that supports only one single subscriber. |
| 55 * | 55 * |
| 56 * If [sync] is true, events may be passed directly to the stream's listener | 56 * If [sync] is true, events may be passed directly to the stream's listener |
| 57 * during an [add], [addError] or [close] call. If [sync] is false, the event | 57 * during an [add], [addError] or [close] call. If [sync] is false, the event |
| 58 * will be passed to the listener at a later time, after the code creating | 58 * will be passed to the listener at a later time, after the code creating |
| 59 * the event has returned. | 59 * the event has returned. |
| 60 * | 60 * |
| 61 * The controller will buffer all incoming events until the subscriber is | 61 * The controller will buffer all incoming events until the subscriber is |
| 62 * registered. | 62 * registered. |
| 63 * | 63 * |
| 64 * The [onPause] function is called when the stream becomes | 64 * The [onPause] function is called when the stream becomes |
| 65 * paused. [onResume] is called when the stream resumed. | 65 * paused. [onResume] is called when the stream resumed. |
| 66 * | 66 * |
| 67 * The [onListen] callback is called when the stream | 67 * The [onListen] callback is called when the stream |
| 68 * receives its listener and [onCancel] when the listener ends | 68 * receives its listener and [onCancel] when the listener ends |
| 69 * its subscription. | 69 * its subscription. |
| 70 * | 70 * |
| 71 * If the stream is canceled before the controller needs new data the | 71 * If the stream is canceled before the controller needs new data the |
| 72 * [onResume] call might not be executed. | 72 * [onResume] call might not be executed. |
| 73 */ | 73 */ |
| 74 factory StreamController({void onListen(), | 74 factory StreamController({void onListen(), |
| 75 void onPause(), | 75 void onPause(), |
| 76 void onResume(), | 76 void onResume(), |
| 77 void onCancel(), | 77 void onCancel(), |
| 78 bool sync: false}) { | 78 bool sync: false}) |
| 79 if (onListen == null && onPause == null && | 79 => sync |
| 80 onResume == null && onCancel == null) { | |
| 81 return sync | |
| 82 ? new _NoCallbackSyncStreamController/*<T>*/() | |
| 83 : new _NoCallbackAsyncStreamController/*<T>*/(); | |
| 84 } | |
| 85 return sync | |
| 86 ? new _SyncStreamController<T>(onListen, onPause, onResume, onCancel) | 80 ? new _SyncStreamController<T>(onListen, onPause, onResume, onCancel) |
| 87 : new _AsyncStreamController<T>(onListen, onPause, onResume, onCancel); | 81 : new _AsyncStreamController<T>(onListen, onPause, onResume, onCancel); |
| 88 } | |
| 89 | 82 |
| 90 /** | 83 /** |
| 91 * A controller where [stream] can be listened to more than once. | 84 * A controller where [stream] can be listened to more than once. |
| 92 * | 85 * |
| 93 * The [Stream] returned by [stream] is a broadcast stream. It can be listened | 86 * The [Stream] returned by [stream] is a broadcast stream. It can be listened |
| 94 * to more than once. | 87 * to more than once. |
| 95 * | 88 * |
| 96 * The controller distributes any events to all currently subscribed | 89 * The controller distributes any events to all currently subscribed |
| 97 * listeners. | 90 * listeners. |
| 98 * It is not allowed to call [add], [addError], or [close] before a previous | 91 * It is not allowed to call [add], [addError], or [close] before a previous |
| (...skipping 24 matching lines...) Expand all Loading... |
| 123 */ | 116 */ |
| 124 factory StreamController.broadcast({void onListen(), | 117 factory StreamController.broadcast({void onListen(), |
| 125 void onCancel(), | 118 void onCancel(), |
| 126 bool sync: false}) { | 119 bool sync: false}) { |
| 127 return sync | 120 return sync |
| 128 ? new _SyncBroadcastStreamController<T>(onListen, onCancel) | 121 ? new _SyncBroadcastStreamController<T>(onListen, onCancel) |
| 129 : new _AsyncBroadcastStreamController<T>(onListen, onCancel); | 122 : new _AsyncBroadcastStreamController<T>(onListen, onCancel); |
| 130 } | 123 } |
| 131 | 124 |
| 132 /** | 125 /** |
| 133 * Returns a view of this object that only exposes the [StreamSink] interface. | 126 * Returns a view of this object that only exposes the [EventSink] interface. |
| 134 */ | 127 */ |
| 135 StreamSink<T> get sink; | 128 EventSink<T> get sink; |
| 136 | 129 |
| 137 /** | 130 /** |
| 138 * Whether the stream is closed for adding more events. | 131 * Whether the stream is closed for adding more events. |
| 139 * | 132 * |
| 140 * If true, the "done" event might not have fired yet, but it has been | 133 * If true, the "done" event might not have fired yet, but it has been |
| 141 * scheduled, and it is too late to add more events. | 134 * scheduled, and it is too late to add more events. |
| 142 */ | 135 */ |
| 143 bool get isClosed; | 136 bool get isClosed; |
| 144 | 137 |
| 145 /** | 138 /** |
| (...skipping 16 matching lines...) Expand all Loading... |
| 162 * Send or enqueue an error event. | 155 * Send or enqueue an error event. |
| 163 * | 156 * |
| 164 * Also allows an objection stack trace object, on top of what [EventSink] | 157 * Also allows an objection stack trace object, on top of what [EventSink] |
| 165 * allows. | 158 * allows. |
| 166 */ | 159 */ |
| 167 void addError(Object error, [Object stackTrace]); | 160 void addError(Object error, [Object stackTrace]); |
| 168 } | 161 } |
| 169 | 162 |
| 170 | 163 |
| 171 abstract class _StreamControllerLifecycle<T> { | 164 abstract class _StreamControllerLifecycle<T> { |
| 172 StreamSubscription<T> _subscribe(void onData(T data), | 165 void _recordListen(StreamSubscription<T> subscription) {} |
| 173 void onError(Object error), | |
| 174 void onDone(), | |
| 175 bool cancelOnError); | |
| 176 void _recordPause(StreamSubscription<T> subscription) {} | 166 void _recordPause(StreamSubscription<T> subscription) {} |
| 177 void _recordResume(StreamSubscription<T> subscription) {} | 167 void _recordResume(StreamSubscription<T> subscription) {} |
| 178 void _recordCancel(StreamSubscription<T> subscription) {} | 168 void _recordCancel(StreamSubscription<T> subscription) {} |
| 179 } | 169 } |
| 180 | 170 |
| 181 /** | 171 /** |
| 182 * Default implementation of [StreamController]. | 172 * Default implementation of [StreamController]. |
| 183 * | 173 * |
| 184 * Controls a stream that only supports a single controller. | 174 * Controls a stream that only supports a single controller. |
| 185 */ | 175 */ |
| 186 abstract class _StreamController<T> implements StreamController<T>, | 176 abstract class _StreamController<T> implements StreamController<T>, |
| 187 _StreamControllerLifecycle<T>, | 177 _StreamControllerLifecycle<T>, |
| 188 _EventSink<T>, | |
| 189 _EventDispatch<T> { | 178 _EventDispatch<T> { |
| 190 // The states are bit-flags. More than one can be set at a time. | 179 static const int _STATE_OPEN = 0; |
| 191 // | 180 static const int _STATE_CANCELLED = 1; |
| 192 // The "subscription state" goes through the states: | 181 static const int _STATE_CLOSED = 2; |
| 193 // initial -> subscribed -> canceled. | |
| 194 // These are mutually exclusive. | |
| 195 // The "closed" state records whether the [close] method has been called | |
| 196 // on the controller. This can be done at any time. If done before | |
| 197 // subscription, the done event is queued. If done after cancel, the done | |
| 198 // event is ignored (just as any other event after a cancel). | |
| 199 | 182 |
| 200 /** The controller is in its initial state with no subscription. */ | 183 final _NotificationHandler _onListen; |
| 201 static const int _STATE_INITIAL = 0; | 184 final _NotificationHandler _onPause; |
| 202 /** The controller has a subscription, but hasn't been closed or canceled. */ | 185 final _NotificationHandler _onResume; |
| 203 static const int _STATE_SUBSCRIBED = 1; | 186 final _NotificationHandler _onCancel; |
| 204 /** The subscription is canceled. */ | 187 _StreamImpl<T> _stream; |
| 205 static const int _STATE_CANCELED = 2; | |
| 206 /** Mask for the subscription state. */ | |
| 207 static const int _STATE_SUBSCRIPTION_MASK = 3; | |
| 208 | 188 |
| 209 // The following state relate to the controller, not the subscription. | 189 // An active subscription on the stream, or null if no subscripton is active. |
| 210 // If closed, adding more events is not allowed. | 190 _ControllerSubscription<T> _subscription; |
| 211 // If executing an [addStream], new events are not allowed either, but will | 191 |
| 212 // be added by the stream. | 192 // Whether we have sent a "done" event. |
| 193 int _state = _STATE_OPEN; |
| 194 |
| 195 // Events added to the stream before it has an active subscription. |
| 196 _PendingEvents _pendingEvents = null; |
| 197 |
| 198 _StreamController(this._onListen, |
| 199 this._onPause, |
| 200 this._onResume, |
| 201 this._onCancel) { |
| 202 _stream = new _ControllerStream<T>(this); |
| 203 } |
| 204 |
| 205 Stream<T> get stream => _stream; |
| 213 | 206 |
| 214 /** | 207 /** |
| 215 * The controller is closed due to calling [close]. | 208 * Returns a view of this object that only exposes the [EventSink] interface. |
| 216 * | |
| 217 * When the stream is closed, you can neither add new events nor add new | |
| 218 * listeners. | |
| 219 */ | 209 */ |
| 220 static const int _STATE_CLOSED = 4; | 210 EventSink<T> get sink => new _EventSinkView<T>(this); |
| 221 /** | |
| 222 * The controller is in the middle of an [addStream] operation. | |
| 223 * | |
| 224 * While adding events from a stream, no new events can be added directly | |
| 225 * on the controller. | |
| 226 */ | |
| 227 static const int _STATE_ADDSTREAM = 8; | |
| 228 | 211 |
| 229 /** | 212 /** |
| 230 * Field containing different data depending on the current subscription | 213 * Whether a listener has existed and been cancelled. |
| 231 * state. | |
| 232 * | |
| 233 * If [_state] is [_STATE_INITIAL], the field may contain a [_PendingEvents] | |
| 234 * for events added to the controller before a subscription. | |
| 235 * | |
| 236 * While [_state] is [_STATE_SUBSCRIBED], the field contains the subscription. | |
| 237 * | |
| 238 * When [_state] is [_STATE_CANCELED] the field is currently not used. | |
| 239 */ | |
| 240 var _varData; | |
| 241 | |
| 242 /** Current state of the controller. */ | |
| 243 int _state = _STATE_INITIAL; | |
| 244 | |
| 245 /** | |
| 246 * Future completed when the stream sends its last event. | |
| 247 * | |
| 248 * This is also the future returned by [close]. | |
| 249 */ | |
| 250 // TODO(lrn): Could this be stored in the varData field too, if it's not | |
| 251 // accessed until the call to "close"? Then we need to special case if it's | |
| 252 // accessed earlier, or if close is called before subscribing. | |
| 253 _FutureImpl _doneFuture; | |
| 254 | |
| 255 _StreamController(); | |
| 256 | |
| 257 _NotificationHandler get _onListen; | |
| 258 _NotificationHandler get _onPause; | |
| 259 _NotificationHandler get _onResume; | |
| 260 _NotificationHandler get _onCancel; | |
| 261 | |
| 262 // Return a new stream every time. The streams are equal, but not identical. | |
| 263 Stream<T> get stream => new _ControllerStream(this); | |
| 264 | |
| 265 /** | |
| 266 * Returns a view of this object that only exposes the [StreamSink] interface. | |
| 267 */ | |
| 268 StreamSink<T> get sink => new _StreamSinkWrapper<T>(this); | |
| 269 | |
| 270 /** | |
| 271 * Whether a listener has existed and been canceled. | |
| 272 * | 214 * |
| 273 * After this, adding more events will be ignored. | 215 * After this, adding more events will be ignored. |
| 274 */ | 216 */ |
| 275 bool get _isCanceled => (_state & _STATE_CANCELED) != 0; | 217 bool get _isCancelled => (_state & _STATE_CANCELLED) != 0; |
| 276 | |
| 277 /** Whether there is an active listener. */ | |
| 278 bool get hasListener => (_state & _STATE_SUBSCRIBED) != 0; | |
| 279 | |
| 280 /** Whether there has not been a listener yet. */ | |
| 281 bool get _isInitialState => | |
| 282 (_state & _STATE_SUBSCRIPTION_MASK) == _STATE_INITIAL; | |
| 283 | 218 |
| 284 bool get isClosed => (_state & _STATE_CLOSED) != 0; | 219 bool get isClosed => (_state & _STATE_CLOSED) != 0; |
| 285 | 220 |
| 286 bool get isPaused => hasListener ? _subscription._isInputPaused | 221 bool get isPaused => hasListener ? _subscription._isInputPaused |
| 287 : !_isCanceled; | 222 : !_isCancelled; |
| 288 | 223 |
| 289 bool get _isAddingStream => (_state & _STATE_ADDSTREAM) != 0; | 224 bool get hasListener => _subscription != null; |
| 290 | |
| 291 /** New events may not be added after close, or during addStream. */ | |
| 292 bool get _mayAddEvent => (_state < _STATE_CLOSED); | |
| 293 | |
| 294 // Returns the pending events. | |
| 295 // Pending events are events added before a subscription exists. | |
| 296 // They are added to the subscription when it is created. | |
| 297 // Pending events, if any, are kept in the _varData field until the | |
| 298 // stream is listened to. | |
| 299 // While adding a stream, pending events are moved into the | |
| 300 // state object to allow the state object to use the _varData field. | |
| 301 _PendingEvents get _pendingEvents { | |
| 302 assert(_isInitialState); | |
| 303 if (!_isAddingStream) { | |
| 304 return _varData; | |
| 305 } | |
| 306 _StreamControllerAddStreamState state = _varData; | |
| 307 return state.varData; | |
| 308 } | |
| 309 | |
| 310 // Returns the pending events, and creates the object if necessary. | |
| 311 _StreamImplEvents _ensurePendingEvents() { | |
| 312 assert(_isInitialState); | |
| 313 if (!_isAddingStream) { | |
| 314 if (_varData == null) _varData = new _StreamImplEvents(); | |
| 315 return _varData; | |
| 316 } | |
| 317 _StreamControllerAddStreamState state = _varData; | |
| 318 if (state.varData == null) state.varData = new _StreamImplEvents(); | |
| 319 return state.varData; | |
| 320 } | |
| 321 | |
| 322 // Get the current subscription. | |
| 323 // If we are adding a stream, the subscription is moved into the state | |
| 324 // object to allow the state object to use the _varData field. | |
| 325 _ControllerSubscription get _subscription { | |
| 326 assert(hasListener); | |
| 327 if (_isAddingStream) { | |
| 328 _StreamControllerAddStreamState addState = _varData; | |
| 329 return addState.varData; | |
| 330 } | |
| 331 return _varData; | |
| 332 } | |
| 333 | 225 |
| 334 /** | 226 /** |
| 335 * Creates an error describing why an event cannot be added. | 227 * Send or queue a data event. |
| 336 * | |
| 337 * The reason, and therefore the error message, depends on the current state. | |
| 338 */ | |
| 339 Error _badEventState() { | |
| 340 if (isClosed) { | |
| 341 return new StateError("Cannot add event after closing"); | |
| 342 } | |
| 343 assert(_isAddingStream); | |
| 344 return new StateError("Cannot add event while adding a stream"); | |
| 345 } | |
| 346 | |
| 347 // StreamSink interface. | |
| 348 Future addStream(Stream<T> source) { | |
| 349 if (!_mayAddEvent) throw _badEventState(); | |
| 350 if (_isCanceled) return new _FutureImpl.immediate(null); | |
| 351 _StreamControllerAddStreamState addState = | |
| 352 new _StreamControllerAddStreamState(this, _varData, source); | |
| 353 _varData = addState; | |
| 354 _state |= _STATE_ADDSTREAM; | |
| 355 return addState.addStreamFuture; | |
| 356 } | |
| 357 | |
| 358 Future get done => _ensureDoneFuture(); | |
| 359 | |
| 360 Future _ensureDoneFuture() { | |
| 361 if (_doneFuture == null) { | |
| 362 _doneFuture = new _FutureImpl(); | |
| 363 if (_isCanceled) _doneFuture._setValue(null); | |
| 364 } | |
| 365 return _doneFuture; | |
| 366 } | |
| 367 | |
| 368 /** | |
| 369 * Send or enqueue a data event. | |
| 370 */ | 228 */ |
| 371 void add(T value) { | 229 void add(T value) { |
| 372 if (!_mayAddEvent) throw _badEventState(); | 230 if (isClosed) throw new StateError("Adding event after close"); |
| 373 _add(value); | 231 if (_subscription != null) { |
| 232 _sendData(value); |
| 233 } else if (!_isCancelled) { |
| 234 _addPendingEvent(new _DelayedData<T>(value)); |
| 235 } |
| 374 } | 236 } |
| 375 | 237 |
| 376 /** | 238 /** |
| 377 * Send or enqueue an error event. | 239 * Send or enqueue an error event. |
| 378 */ | 240 */ |
| 379 void addError(Object error, [Object stackTrace]) { | 241 void addError(Object error, [Object stackTrace]) { |
| 380 if (!_mayAddEvent) throw _badEventState(); | 242 if (isClosed) throw new StateError("Adding event after close"); |
| 381 if (stackTrace != null) { | 243 if (stackTrace != null) { |
| 382 // Force stack trace overwrite. Even if the error already contained | 244 // Force stack trace overwrite. Even if the error already contained |
| 383 // a stack trace. | 245 // a stack trace. |
| 384 _attachStackTrace(error, stackTrace); | 246 _attachStackTrace(error, stackTrace); |
| 385 } | 247 } |
| 386 _addError(error); | 248 if (_subscription != null) { |
| 249 _sendError(error); |
| 250 } else if (!_isCancelled) { |
| 251 _addPendingEvent(new _DelayedError(error)); |
| 252 } |
| 387 } | 253 } |
| 388 | 254 |
| 389 /** | 255 /** |
| 390 * Closes this controller. | 256 * Closes this controller. |
| 391 * | 257 * |
| 392 * After closing, no further events may be added using [add] or [addError]. | 258 * After closing, no further events may be added using [add] or [addError]. |
| 393 * | 259 * |
| 394 * You are allowed to close the controller more than once, but only the first | 260 * You are allowed to close the controller more than once, but only the first |
| 395 * call has any effect. | 261 * call has any effect. |
| 396 * | 262 * |
| 397 * The first time a controller is closed, a "done" event is sent to its | 263 * The first time a controller is closed, a "done" event is sent to its |
| 398 * stream. | 264 * stream. |
| 399 */ | 265 */ |
| 400 Future close() { | 266 void close() { |
| 401 if (isClosed) { | 267 if (isClosed) return; |
| 402 assert(_doneFuture != null); // Was set when close was first called. | |
| 403 return _doneFuture; | |
| 404 } | |
| 405 if (!_mayAddEvent) throw _badEventState(); | |
| 406 _state |= _STATE_CLOSED; | 268 _state |= _STATE_CLOSED; |
| 407 _ensureDoneFuture(); | 269 if (_subscription != null) { |
| 408 if (hasListener) { | |
| 409 _sendDone(); | 270 _sendDone(); |
| 410 } else if (_isInitialState) { | 271 } else if (!_isCancelled) { |
| 411 _ensurePendingEvents().add(const _DelayedDone()); | 272 _addPendingEvent(const _DelayedDone()); |
| 412 } | |
| 413 return _doneFuture; | |
| 414 } | |
| 415 | |
| 416 // EventSink interface. Used by the [addStream] events. | |
| 417 | |
| 418 // Add data event, used both by the [addStream] events and by [add]. | |
| 419 void _add(T value) { | |
| 420 if (hasListener) { | |
| 421 _sendData(value); | |
| 422 } else if (_isInitialState) { | |
| 423 _ensurePendingEvents().add(new _DelayedData<T>(value)); | |
| 424 } | 273 } |
| 425 } | 274 } |
| 426 | 275 |
| 427 void _addError(Object error) { | 276 // EventDispatch interface |
| 428 if (hasListener) { | 277 |
| 429 _sendError(error); | 278 void _addPendingEvent(_DelayedEvent event) { |
| 430 } else if (_isInitialState) { | 279 if (_isCancelled) return; |
| 431 _ensurePendingEvents().add(new _DelayedError(error)); | 280 _StreamImplEvents events = _pendingEvents; |
| 281 if (events == null) { |
| 282 events = _pendingEvents = new _StreamImplEvents(); |
| 432 } | 283 } |
| 284 events.add(event); |
| 433 } | 285 } |
| 434 | 286 |
| 435 void _close() { | 287 void _recordListen(_BufferingStreamSubscription<T> subscription) { |
| 436 // End of addStream stream. | 288 assert(_subscription == null); |
| 437 assert(_isAddingStream); | 289 _subscription = subscription; |
| 438 _StreamControllerAddStreamState addState = _varData; | 290 subscription._setPendingEvents(_pendingEvents); |
| 439 _varData = addState.varData; | 291 _pendingEvents = null; |
| 440 _state &= ~_STATE_ADDSTREAM; | |
| 441 addState.complete(); | |
| 442 } | |
| 443 | |
| 444 // _StreamControllerLifeCycle interface | |
| 445 | |
| 446 StreamSubscription<T> _subscribe(void onData(T data), | |
| 447 void onError(Object error), | |
| 448 void onDone(), | |
| 449 bool cancelOnError) { | |
| 450 if (!_isInitialState) { | |
| 451 throw new StateError("Stream has already been listened to."); | |
| 452 } | |
| 453 _ControllerSubscription subscription = new _ControllerSubscription( | |
| 454 this, onData, onError, onDone, cancelOnError); | |
| 455 | |
| 456 _PendingEvents pendingEvents = _pendingEvents; | |
| 457 _state |= _STATE_SUBSCRIBED; | |
| 458 if (_isAddingStream) { | |
| 459 _StreamControllerAddStreamState addState = _varData; | |
| 460 addState.varData = subscription; | |
| 461 } else { | |
| 462 _varData = subscription; | |
| 463 } | |
| 464 subscription._setPendingEvents(pendingEvents); | |
| 465 subscription._guardCallback(() { | 292 subscription._guardCallback(() { |
| 466 _runGuarded(_onListen); | 293 _runGuarded(_onListen); |
| 467 }); | 294 }); |
| 468 | |
| 469 return subscription; | |
| 470 } | 295 } |
| 471 | 296 |
| 472 void _recordCancel(StreamSubscription<T> subscription) { | 297 void _recordCancel(StreamSubscription<T> subscription) { |
| 473 if (_isAddingStream) { | 298 assert(identical(_subscription, subscription)); |
| 474 _StreamControllerAddStreamState addState = _varData; | 299 _subscription = null; |
| 475 addState.cancel(); | 300 _state |= _STATE_CANCELLED; |
| 476 } | |
| 477 _varData = null; | |
| 478 _state = | |
| 479 (_state & ~(_STATE_SUBSCRIBED | _STATE_ADDSTREAM)) | _STATE_CANCELED; | |
| 480 _runGuarded(_onCancel); | 301 _runGuarded(_onCancel); |
| 481 if (_doneFuture != null && _doneFuture._mayComplete) { | |
| 482 _doneFuture._asyncSetValue(null); | |
| 483 } | |
| 484 } | 302 } |
| 485 | 303 |
| 486 void _recordPause(StreamSubscription<T> subscription) { | 304 void _recordPause(StreamSubscription<T> subscription) { |
| 487 if (_isAddingStream) { | |
| 488 _StreamControllerAddStreamState addState = _varData; | |
| 489 addState.pause(); | |
| 490 } | |
| 491 _runGuarded(_onPause); | 305 _runGuarded(_onPause); |
| 492 } | 306 } |
| 493 | 307 |
| 494 void _recordResume(StreamSubscription<T> subscription) { | 308 void _recordResume(StreamSubscription<T> subscription) { |
| 495 if (_isAddingStream) { | |
| 496 _StreamControllerAddStreamState addState = _varData; | |
| 497 addState.resume(); | |
| 498 } | |
| 499 _runGuarded(_onResume); | 309 _runGuarded(_onResume); |
| 500 } | 310 } |
| 501 } | 311 } |
| 502 | 312 |
| 503 abstract class _SyncStreamControllerDispatch<T> | 313 class _SyncStreamController<T> extends _StreamController<T> { |
| 504 implements _StreamController<T> { | 314 _SyncStreamController(void onListen(), |
| 315 void onPause(), |
| 316 void onResume(), |
| 317 void onCancel()) |
| 318 : super(onListen, onPause, onResume, onCancel); |
| 319 |
| 505 void _sendData(T data) { | 320 void _sendData(T data) { |
| 506 _subscription._add(data); | 321 _subscription._add(data); |
| 507 } | 322 } |
| 508 | 323 |
| 509 void _sendError(Object error) { | 324 void _sendError(Object error) { |
| 510 _subscription._addError(error); | 325 _subscription._addError(error); |
| 511 } | 326 } |
| 512 | 327 |
| 513 void _sendDone() { | 328 void _sendDone() { |
| 514 _subscription._close(); | 329 _subscription._close(); |
| 515 } | 330 } |
| 516 } | 331 } |
| 517 | 332 |
| 518 abstract class _AsyncStreamControllerDispatch<T> | 333 class _AsyncStreamController<T> extends _StreamController<T> { |
| 519 implements _StreamController<T> { | 334 _AsyncStreamController(void onListen(), |
| 335 void onPause(), |
| 336 void onResume(), |
| 337 void onCancel()) |
| 338 : super(onListen, onPause, onResume, onCancel); |
| 339 |
| 520 void _sendData(T data) { | 340 void _sendData(T data) { |
| 521 _subscription._addPending(new _DelayedData(data)); | 341 _subscription._addPending(new _DelayedData(data)); |
| 522 } | 342 } |
| 523 | 343 |
| 524 void _sendError(Object error) { | 344 void _sendError(Object error) { |
| 525 _subscription._addPending(new _DelayedError(error)); | 345 _subscription._addPending(new _DelayedError(error)); |
| 526 } | 346 } |
| 527 | 347 |
| 528 void _sendDone() { | 348 void _sendDone() { |
| 529 _subscription._addPending(const _DelayedDone()); | 349 _subscription._addPending(const _DelayedDone()); |
| 530 } | 350 } |
| 531 } | 351 } |
| 532 | 352 |
| 533 // TODO(lrn): Use common superclass for callback-controllers when VM supports | |
| 534 // constructors in mixin superclasses. | |
| 535 | |
| 536 class _AsyncStreamController<T> extends _StreamController<T> | |
| 537 with _AsyncStreamControllerDispatch<T> { | |
| 538 final _NotificationHandler _onListen; | |
| 539 final _NotificationHandler _onPause; | |
| 540 final _NotificationHandler _onResume; | |
| 541 final _NotificationHandler _onCancel; | |
| 542 | |
| 543 _AsyncStreamController(void this._onListen(), | |
| 544 void this._onPause(), | |
| 545 void this._onResume(), | |
| 546 void this._onCancel()); | |
| 547 } | |
| 548 | |
| 549 class _SyncStreamController<T> extends _StreamController<T> | |
| 550 with _SyncStreamControllerDispatch<T> { | |
| 551 final _NotificationHandler _onListen; | |
| 552 final _NotificationHandler _onPause; | |
| 553 final _NotificationHandler _onResume; | |
| 554 final _NotificationHandler _onCancel; | |
| 555 | |
| 556 _SyncStreamController(void this._onListen(), | |
| 557 void this._onPause(), | |
| 558 void this._onResume(), | |
| 559 void this._onCancel()); | |
| 560 } | |
| 561 | |
| 562 abstract class _NoCallbacks { | |
| 563 _NotificationHandler get _onListen => null; | |
| 564 _NotificationHandler get _onPause => null; | |
| 565 _NotificationHandler get _onResume => null; | |
| 566 _NotificationHandler get _onCancel => null; | |
| 567 } | |
| 568 | |
| 569 typedef _NoCallbackAsyncStreamController/*<T>*/ = _StreamController/*<T>*/ | |
| 570 with _AsyncStreamControllerDispatch/*<T>*/, _NoCallbacks; | |
| 571 | |
| 572 typedef _NoCallbackSyncStreamController/*<T>*/ = _StreamController/*<T>*/ | |
| 573 with _SyncStreamControllerDispatch/*<T>*/, _NoCallbacks; | |
| 574 | |
| 575 typedef void _NotificationHandler(); | 353 typedef void _NotificationHandler(); |
| 576 | 354 |
| 577 void _runGuarded(_NotificationHandler notificationHandler) { | 355 void _runGuarded(_NotificationHandler notificationHandler) { |
| 578 if (notificationHandler == null) return; | 356 if (notificationHandler == null) return; |
| 579 try { | 357 try { |
| 580 notificationHandler(); | 358 notificationHandler(); |
| 581 } catch (e, s) { | 359 } catch (e, s) { |
| 582 _Zone.current.handleUncaughtError(_asyncError(e, s)); | 360 _Zone.current.handleUncaughtError(_asyncError(e, s)); |
| 583 } | 361 } |
| 584 } | 362 } |
| 585 | 363 |
| 586 class _ControllerStream<T> extends _StreamImpl<T> { | 364 class _ControllerStream<T> extends _StreamImpl<T> { |
| 587 _StreamControllerLifecycle<T> _controller; | 365 _StreamControllerLifecycle<T> _controller; |
| 366 bool _hasListener = false; |
| 588 | 367 |
| 589 _ControllerStream(this._controller); | 368 _ControllerStream(this._controller); |
| 590 | 369 |
| 591 StreamSubscription<T> _createSubscription( | 370 StreamSubscription<T> _createSubscription( |
| 592 void onData(T data), | 371 void onData(T data), |
| 593 void onError(Object error), | 372 void onError(Object error), |
| 594 void onDone(), | 373 void onDone(), |
| 595 bool cancelOnError) => | 374 bool cancelOnError) { |
| 596 _controller._subscribe(onData, onError, onDone, cancelOnError); | 375 if (_hasListener) { |
| 376 throw new StateError("The stream has already been listened to."); |
| 377 } |
| 378 _hasListener = true; |
| 379 return new _ControllerSubscription<T>( |
| 380 _controller, onData, onError, onDone, cancelOnError); |
| 381 } |
| 597 | 382 |
| 598 // Override == and hashCode so that new streams returned by the same | 383 void _onListen(_BufferingStreamSubscription subscription) { |
| 599 // controller are considered equal. The controller returns a new stream | 384 _controller._recordListen(subscription); |
| 600 // each time it's queried, but doesn't have to cache the result. | |
| 601 | |
| 602 int get hashCode => _controller.hashCode ^ 0x35323532; | |
| 603 | |
| 604 bool operator==(Object other) { | |
| 605 if (other is! _ControllerStream) return false; | |
| 606 _ControllerStream otherStream = other; | |
| 607 return identical(otherStream._controller, this); | |
| 608 } | 385 } |
| 609 } | 386 } |
| 610 | 387 |
| 611 class _ControllerSubscription<T> extends _BufferingStreamSubscription<T> { | 388 class _ControllerSubscription<T> extends _BufferingStreamSubscription<T> { |
| 612 final _StreamControllerLifecycle<T> _controller; | 389 final _StreamControllerLifecycle<T> _controller; |
| 613 | 390 |
| 614 _ControllerSubscription(this._controller, | 391 _ControllerSubscription(this._controller, |
| 615 void onData(T data), | 392 void onData(T data), |
| 616 void onError(Object error), | 393 void onError(Object error), |
| 617 void onDone(), | 394 void onDone(), |
| 618 bool cancelOnError) | 395 bool cancelOnError) |
| 619 : super(onData, onError, onDone, cancelOnError); | 396 : super(onData, onError, onDone, cancelOnError); |
| 620 | 397 |
| 621 void _onCancel() { | 398 void _onCancel() { |
| 622 _controller._recordCancel(this); | 399 _controller._recordCancel(this); |
| 623 } | 400 } |
| 624 | 401 |
| 625 void _onPause() { | 402 void _onPause() { |
| 626 _controller._recordPause(this); | 403 _controller._recordPause(this); |
| 627 } | 404 } |
| 628 | 405 |
| 629 void _onResume() { | 406 void _onResume() { |
| 630 _controller._recordResume(this); | 407 _controller._recordResume(this); |
| 631 } | 408 } |
| 632 } | 409 } |
| 633 | 410 |
| 634 | 411 class _BroadcastStream<T> extends _StreamImpl<T> { |
| 635 /** A class that exposes only the [StreamSink] interface of an object. */ | 412 _BroadcastStreamController _controller; |
| 636 class _StreamSinkWrapper<T> implements StreamSink<T> { | 413 |
| 637 final StreamSink _target; | 414 _BroadcastStream(this._controller); |
| 638 _StreamSinkWrapper(this._target); | 415 |
| 639 void add(T data) { _target.add(data); } | 416 bool get isBroadcast => true; |
| 640 void addError(Object error) { _target.addError(error); } | 417 |
| 641 Future close() => _target.close(); | 418 StreamSubscription<T> _createSubscription( |
| 642 Future addStream(Stream<T> source) => _target.addStream(source); | 419 void onData(T data), |
| 643 Future get done => _target.done; | 420 void onError(Object error), |
| 421 void onDone(), |
| 422 bool cancelOnError) { |
| 423 return new _BroadcastSubscription<T>( |
| 424 _controller, onData, onError, onDone, cancelOnError); |
| 425 } |
| 426 |
| 427 void _onListen(_BufferingStreamSubscription subscription) { |
| 428 _controller._recordListen(subscription); |
| 429 } |
| 430 } |
| 431 |
| 432 abstract class _BroadcastSubscriptionLink { |
| 433 _BroadcastSubscriptionLink _next; |
| 434 _BroadcastSubscriptionLink _previous; |
| 435 } |
| 436 |
| 437 class _BroadcastSubscription<T> extends _ControllerSubscription<T> |
| 438 implements _BroadcastSubscriptionLink { |
| 439 static const int _STATE_EVENT_ID = 1; |
| 440 static const int _STATE_FIRING = 2; |
| 441 static const int _STATE_REMOVE_AFTER_FIRING = 4; |
| 442 int _eventState; |
| 443 |
| 444 _BroadcastSubscriptionLink _next; |
| 445 _BroadcastSubscriptionLink _previous; |
| 446 |
| 447 _BroadcastSubscription(_StreamControllerLifecycle controller, |
| 448 void onData(T data), |
| 449 void onError(Object error), |
| 450 void onDone(), |
| 451 bool cancelOnError) |
| 452 : super(controller, onData, onError, onDone, cancelOnError) { |
| 453 _next = _previous = this; |
| 454 } |
| 455 |
| 456 _BroadcastStreamController get _controller => super._controller; |
| 457 |
| 458 bool _expectsEvent(int eventId) { |
| 459 return (_eventState & _STATE_EVENT_ID) == eventId; |
| 460 } |
| 461 |
| 462 void _toggleEventId() { |
| 463 _eventState ^= _STATE_EVENT_ID; |
| 464 } |
| 465 |
| 466 bool get _isFiring => (_eventState & _STATE_FIRING) != 0; |
| 467 |
| 468 bool _setRemoveAfterFiring() { |
| 469 assert(_isFiring); |
| 470 _eventState |= _STATE_REMOVE_AFTER_FIRING; |
| 471 } |
| 472 |
| 473 bool get _removeAfterFiring => |
| 474 (_eventState & _STATE_REMOVE_AFTER_FIRING) != 0; |
| 475 } |
| 476 |
| 477 |
| 478 abstract class _BroadcastStreamController<T> |
| 479 implements StreamController<T>, |
| 480 _StreamControllerLifecycle<T>, |
| 481 _BroadcastSubscriptionLink, |
| 482 _EventDispatch<T> { |
| 483 static const int _STATE_INITIAL = 0; |
| 484 static const int _STATE_EVENT_ID = 1; |
| 485 static const int _STATE_FIRING = 2; |
| 486 static const int _STATE_CLOSED = 4; |
| 487 |
| 488 final _NotificationHandler _onListen; |
| 489 final _NotificationHandler _onCancel; |
| 490 |
| 491 // State of the controller. |
| 492 int _state; |
| 493 |
| 494 // Double-linked list of active listeners. |
| 495 _BroadcastSubscriptionLink _next; |
| 496 _BroadcastSubscriptionLink _previous; |
| 497 |
| 498 _BroadcastStreamController(this._onListen, this._onCancel) |
| 499 : _state = _STATE_INITIAL { |
| 500 _next = _previous = this; |
| 501 } |
| 502 |
| 503 // StreamController interface. |
| 504 |
| 505 Stream<T> get stream => new _BroadcastStream<T>(this); |
| 506 |
| 507 EventSink<T> get sink => new _EventSinkView<T>(this); |
| 508 |
| 509 bool get isClosed => (_state & _STATE_CLOSED) != 0; |
| 510 |
| 511 /** |
| 512 * A broadcast controller is never paused. |
| 513 * |
| 514 * Each receiving stream may be paused individually, and they handle their |
| 515 * own buffering. |
| 516 */ |
| 517 bool get isPaused => false; |
| 518 |
| 519 /** Whether there are currently a subscriber on the [Stream]. */ |
| 520 bool get hasListener => !_isEmpty; |
| 521 |
| 522 /** Whether an event is being fired (sent to some, but not all, listeners). */ |
| 523 bool get _isFiring => (_state & _STATE_FIRING) != 0; |
| 524 |
| 525 // Linked list helpers |
| 526 |
| 527 bool get _isEmpty => identical(_next, this); |
| 528 |
| 529 /** Adds subscription to linked list of active listeners. */ |
| 530 void _addListener(_BroadcastSubscription<T> subscription) { |
| 531 _BroadcastSubscriptionLink previous = _previous; |
| 532 previous._next = subscription; |
| 533 _previous = subscription._previous; |
| 534 subscription._previous._next = this; |
| 535 subscription._previous = previous; |
| 536 subscription._eventState = (_state & _STATE_EVENT_ID); |
| 537 } |
| 538 |
| 539 void _removeListener(_BroadcastSubscription<T> subscription) { |
| 540 assert(identical(subscription._controller, this)); |
| 541 assert(!identical(subscription._next, subscription)); |
| 542 subscription._previous._next = subscription._next; |
| 543 subscription._next._previous = subscription._previous; |
| 544 subscription._next = subscription._previous = subscription; |
| 545 } |
| 546 |
| 547 // _StreamControllerLifecycle interface. |
| 548 |
| 549 void _recordListen(_BroadcastSubscription<T> subscription) { |
| 550 _addListener(subscription); |
| 551 if (identical(_next, _previous)) { |
| 552 // Only one listener, so it must be the first listener. |
| 553 _runGuarded(_onListen); |
| 554 } |
| 555 } |
| 556 |
| 557 void _recordCancel(_BroadcastSubscription<T> subscription) { |
| 558 if (subscription._isFiring) { |
| 559 subscription._setRemoveAfterFiring(); |
| 560 } else { |
| 561 _removeListener(subscription); |
| 562 // If we are currently firing an event, the empty-check is performed at |
| 563 // the end of the listener loop instead of here. |
| 564 if ((_state & _STATE_FIRING) == 0 && _isEmpty) { |
| 565 _callOnCancel(); |
| 566 } |
| 567 } |
| 568 } |
| 569 |
| 570 void _recordPause(StreamSubscription<T> subscription) {} |
| 571 void _recordResume(StreamSubscription<T> subscription) {} |
| 572 |
| 573 // EventSink interface. |
| 574 |
| 575 void add(T data) { |
| 576 if (isClosed) { |
| 577 throw new StateError("Cannot add new events after calling close()"); |
| 578 } |
| 579 _sendData(data); |
| 580 } |
| 581 |
| 582 void addError(Object error, [Object stackTrace]) { |
| 583 if (isClosed) { |
| 584 throw new StateError("Cannot add new events after calling close()"); |
| 585 } |
| 586 if (stackTrace != null) _attachStackTrace(error, stackTrace); |
| 587 _sendError(error); |
| 588 } |
| 589 |
| 590 void close() { |
| 591 if (isClosed) { |
| 592 throw new StateError("Cannot add new events after calling close()"); |
| 593 } |
| 594 _state |= _STATE_CLOSED; |
| 595 _sendDone(); |
| 596 } |
| 597 |
| 598 void _forEachListener( |
| 599 void action(_BufferingStreamSubscription<T> subscription)) { |
| 600 if (_isFiring) { |
| 601 throw new StateError( |
| 602 "Cannot fire new event. Controller is already firing an event"); |
| 603 } |
| 604 if (_isEmpty) return; |
| 605 |
| 606 // Get event id of this event. |
| 607 int id = (_state & _STATE_EVENT_ID); |
| 608 // Start firing (set the _STATE_FIRING bit). We don't do [_onCancel] |
| 609 // callbacks while firing, and we prevent reentrancy of this function. |
| 610 // |
| 611 // Set [_state]'s event id to the next event's id. |
| 612 // Any listeners added while firing this event will expect the next event, |
| 613 // not this one, and won't get notified. |
| 614 _state ^= _STATE_EVENT_ID | _STATE_FIRING; |
| 615 _BroadcastSubscriptionLink link = _next; |
| 616 while (!identical(link, this)) { |
| 617 _BroadcastSubscription<T> subscription = link; |
| 618 if (subscription._expectsEvent(id)) { |
| 619 subscription._eventState |= _BroadcastSubscription._STATE_FIRING; |
| 620 action(subscription); |
| 621 subscription._toggleEventId(); |
| 622 link = subscription._next; |
| 623 if (subscription._removeAfterFiring) { |
| 624 _removeListener(subscription); |
| 625 } |
| 626 subscription._eventState &= ~_BroadcastSubscription._STATE_FIRING; |
| 627 } else { |
| 628 link = subscription._next; |
| 629 } |
| 630 } |
| 631 _state &= ~_STATE_FIRING; |
| 632 |
| 633 if (_isEmpty) { |
| 634 _callOnCancel(); |
| 635 } |
| 636 } |
| 637 |
| 638 void _callOnCancel() { |
| 639 _runGuarded(_onCancel); |
| 640 } |
| 641 } |
| 642 |
| 643 class _SyncBroadcastStreamController<T> extends _BroadcastStreamController<T> { |
| 644 _SyncBroadcastStreamController(void onListen(), void onCancel()) |
| 645 : super(onListen, onCancel); |
| 646 |
| 647 // EventDispatch interface. |
| 648 |
| 649 void _sendData(T data) { |
| 650 if (_isEmpty) return; |
| 651 _forEachListener((_BufferingStreamSubscription<T> subscription) { |
| 652 subscription._add(data); |
| 653 }); |
| 654 } |
| 655 |
| 656 void _sendError(Object error) { |
| 657 if (_isEmpty) return; |
| 658 _forEachListener((_BufferingStreamSubscription<T> subscription) { |
| 659 subscription._addError(error); |
| 660 }); |
| 661 } |
| 662 |
| 663 void _sendDone() { |
| 664 if (_isEmpty) return; |
| 665 _forEachListener((_BroadcastSubscription<T> subscription) { |
| 666 subscription._close(); |
| 667 subscription._eventState |= |
| 668 _BroadcastSubscription._STATE_REMOVE_AFTER_FIRING; |
| 669 }); |
| 670 } |
| 671 } |
| 672 |
| 673 class _AsyncBroadcastStreamController<T> extends _BroadcastStreamController<T> { |
| 674 _AsyncBroadcastStreamController(void onListen(), void onCancel()) |
| 675 : super(onListen, onCancel); |
| 676 |
| 677 // EventDispatch interface. |
| 678 |
| 679 void _sendData(T data) { |
| 680 for (_BroadcastSubscriptionLink link = _next; |
| 681 !identical(link, this); |
| 682 link = link._next) { |
| 683 _BroadcastSubscription<T> subscription = link; |
| 684 subscription._addPending(new _DelayedData(data)); |
| 685 } |
| 686 } |
| 687 |
| 688 void _sendError(Object error) { |
| 689 for (_BroadcastSubscriptionLink link = _next; |
| 690 !identical(link, this); |
| 691 link = link._next) { |
| 692 _BroadcastSubscription<T> subscription = link; |
| 693 subscription._addPending(new _DelayedError(error)); |
| 694 } |
| 695 } |
| 696 |
| 697 void _sendDone() { |
| 698 for (_BroadcastSubscriptionLink link = _next; |
| 699 !identical(link, this); |
| 700 link = link._next) { |
| 701 _BroadcastSubscription<T> subscription = link; |
| 702 subscription._addPending(const _DelayedDone()); |
| 703 } |
| 704 } |
| 644 } | 705 } |
| 645 | 706 |
| 646 /** | 707 /** |
| 647 * Object containing the state used to handle [StreamController.addStream]. | 708 * Stream controller that is used by [Stream.asBroadcastStream]. |
| 709 * |
| 710 * This stream controller allows incoming events while it is firing |
| 711 * other events. This is handled by delaying the events until the |
| 712 * current event is done firing, and then fire the pending events. |
| 713 * |
| 714 * This class extends [_SyncBroadcastStreamController]. Events of |
| 715 * an "asBroadcastStream" stream are always initiated by events |
| 716 * on another stream, and it is fine to forward them synchronously. |
| 648 */ | 717 */ |
| 649 class _AddStreamState<T> { | 718 class _AsBroadcastStreamController<T> |
| 650 // [_FutureImpl] returned by call to addStream. | 719 extends _SyncBroadcastStreamController<T> |
| 651 _FutureImpl addStreamFuture; | 720 implements _EventDispatch<T> { |
| 652 | 721 _StreamImplEvents _pending; |
| 653 // Subscription on stream argument to addStream. | 722 |
| 654 StreamSubscription addSubscription; | 723 _AsBroadcastStreamController(void onListen(), void onCancel()) |
| 655 | 724 : super(onListen, onCancel); |
| 656 _AddStreamState(StreamSink controller, Stream source) | 725 |
| 657 : addStreamFuture = new _FutureImpl(), | 726 bool get _hasPending => _pending != null && ! _pending.isEmpty; |
| 658 addSubscription = source.listen(controller._add, | 727 |
| 659 onError: controller._addError, | 728 void _addPendingEvent(_DelayedEvent event) { |
| 660 onDone: controller._close, | 729 if (_pending == null) { |
| 661 cancelOnError: true); | 730 _pending = new _StreamImplEvents(); |
| 662 | 731 } |
| 663 void pause() { | 732 _pending.add(event); |
| 664 addSubscription.pause(); | 733 } |
| 665 } | 734 |
| 666 | 735 void add(T data) { |
| 667 void resume() { | 736 if (_isFiring) { |
| 668 addSubscription.resume(); | 737 _addPendingEvent(new _DelayedData<T>(data)); |
| 669 } | 738 return; |
| 670 | 739 } |
| 671 void cancel() { | 740 super.add(data); |
| 672 addSubscription.cancel(); | 741 while (_hasPending) { |
| 673 complete(); | 742 _pending.handleNext(this); |
| 674 } | 743 } |
| 675 | 744 } |
| 676 void complete() { | 745 |
| 677 addStreamFuture._asyncSetValue(null); | 746 void addError(Object error, [StackTrace stackTrace]) { |
| 678 } | 747 if (_isFiring) { |
| 679 } | 748 _addPendingEvent(new _DelayedError(error)); |
| 680 | 749 return; |
| 681 class _StreamControllerAddStreamState<T> extends _AddStreamState<T> { | 750 } |
| 682 // The subscription or pending data of a _StreamController. | 751 super.addError(error, stackTrace); |
| 683 // Stored here because we reuse the `_varData` field in the _StreamController | 752 while (_hasPending) { |
| 684 // to store this state object. | 753 _pending.handleNext(this); |
| 685 var varData; | 754 } |
| 686 | 755 } |
| 687 _StreamControllerAddStreamState(_StreamController controller, | 756 |
| 688 this.varData, | 757 void close() { |
| 689 Stream source) : super(controller, source) { | 758 if (_isFiring) { |
| 690 if (controller.isPaused) { | 759 _addPendingEvent(const _DelayedDone()); |
| 691 addSubscription.pause(); | 760 _state |= _STATE_CLOSED; |
| 692 } | 761 return; |
| 693 } | 762 } |
| 694 } | 763 super.close(); |
| 764 assert(!_hasPending); |
| 765 } |
| 766 |
| 767 void _callOnCancel() { |
| 768 if (_hasPending) { |
| 769 _pending.clear(); |
| 770 _pending = null; |
| 771 } |
| 772 super._callOnCancel(); |
| 773 } |
| 774 } |
| OLD | NEW |