| 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 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 66 * If the stream is canceled before the controller needs new data the | 66 * If the stream is canceled before the controller needs new data the |
| 67 * [onResume] call might not be executed. | 67 * [onResume] call might not be executed. |
| 68 */ | 68 */ |
| 69 factory StreamController({void onListen(), | 69 factory StreamController({void onListen(), |
| 70 void onPause(), | 70 void onPause(), |
| 71 void onResume(), | 71 void onResume(), |
| 72 void onCancel()}) | 72 void onCancel()}) |
| 73 => new _StreamControllerImpl<T>(onListen, onPause, onResume, onCancel); | 73 => new _StreamControllerImpl<T>(onListen, onPause, onResume, onCancel); |
| 74 | 74 |
| 75 /** | 75 /** |
| 76 * A controller where [stream] creates new stream each time it is read. | 76 * A controller where [stream] can be listened to more than once. |
| 77 * | 77 * |
| 78 * The controller distributes any events to all currently subscribed streams. | 78 * The [Stream] returned by [stream] is a broadcast stream. It can be listened |
| 79 * to more than once. |
| 80 * |
| 81 * The controller distributes any events to all currently subscribed |
| 82 * listeners. |
| 83 * It is not allowed to call [add], [addError], or [close] before a previous |
| 84 * call has returned. |
| 85 * |
| 86 * Each listener is handled independently, and if they pause, only the pausing |
| 87 * listener is affected. A paused listener will buffer events internally until |
| 88 * unpaused or canceled. |
| 79 * | 89 * |
| 80 * The [onListen] callback is called when the first listener is subscribed, | 90 * The [onListen] callback is called when the first listener is subscribed, |
| 81 * and the [onCancel] is called when there is no longer any active listeners. | 91 * and the [onCancel] is called when there are no longer any active listeners. |
| 82 * If a listener is added again later, after the [onCancel] was called, | 92 * If a listener is added again later, after the [onCancel] was called, |
| 83 * the [onListen] will be called again. | 93 * the [onListen] will be called again. |
| 84 */ | 94 */ |
| 85 factory StreamController.multiplex({void onListen(), void onCancel()}) { | 95 factory StreamController.broadcast({void onListen(), void onCancel()}) { |
| 86 return new _MultiplexStreamController<T>(onListen, onCancel); | 96 return new _MultiplexStreamController<T>(onListen, onCancel); |
| 87 } | 97 } |
| 88 | 98 |
| 89 /** | 99 /** |
| 90 * Returns a view of this object that only exposes the [EventSink] interface. | 100 * Returns a view of this object that only exposes the [EventSink] interface. |
| 91 */ | 101 */ |
| 92 EventSink<T> get sink; | 102 EventSink<T> get sink; |
| 93 | 103 |
| 94 /** | 104 /** |
| 95 * Whether the stream is closed for adding more events. | 105 * Whether the stream is closed for adding more events. |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 311 | 321 |
| 312 void _onPause() { | 322 void _onPause() { |
| 313 _controller._recordPause(this); | 323 _controller._recordPause(this); |
| 314 } | 324 } |
| 315 | 325 |
| 316 void _onResume() { | 326 void _onResume() { |
| 317 _controller._recordResume(this); | 327 _controller._recordResume(this); |
| 318 } | 328 } |
| 319 } | 329 } |
| 320 | 330 |
| 331 class _MultiplexStream<T> extends _StreamImpl<T> { |
| 332 _MultiplexStreamController _controller; |
| 333 |
| 334 _MultiplexStream(this._controller); |
| 335 |
| 336 bool get isBroadcast => true; |
| 337 |
| 338 StreamSubscription<T> _createSubscription( |
| 339 void onData(T data), |
| 340 void onError(Object error), |
| 341 void onDone(), |
| 342 bool cancelOnError) { |
| 343 return new _MultiplexSubscription<T>( |
| 344 _controller, onData, onError, onDone, cancelOnError); |
| 345 } |
| 346 |
| 347 void _onListen(_BufferingStreamSubscription subscription) { |
| 348 _controller._recordListen(subscription); |
| 349 } |
| 350 } |
| 351 |
| 352 abstract class _MultiplexSubscriptionLink { |
| 353 _MultiplexSubscriptionLink _next; |
| 354 _MultiplexSubscriptionLink _previous; |
| 355 } |
| 356 |
| 357 class _MultiplexSubscription<T> extends _ControllerSubscription<T> |
| 358 implements _MultiplexSubscriptionLink { |
| 359 static const int _STATE_EVENT_ID = 1; |
| 360 static const int _STATE_FIRING = 2; |
| 361 static const int _STATE_REMOVE_AFTER_FIRING = 4; |
| 362 int _eventState; |
| 363 |
| 364 _MultiplexSubscriptionLink _next; |
| 365 _MultiplexSubscriptionLink _previous; |
| 366 |
| 367 _MultiplexSubscription(_StreamControllerLifecycle controller, |
| 368 void onData(T data), |
| 369 void onError(Object error), |
| 370 void onDone(), |
| 371 bool cancelOnError) |
| 372 : super(controller, onData, onError, onDone, cancelOnError) { |
| 373 _next = _previous = this; |
| 374 } |
| 375 |
| 376 _MultiplexStreamController get _controller => super._controller; |
| 377 |
| 378 bool _expectsEvent(int eventId) { |
| 379 return (_eventState & _STATE_EVENT_ID) == eventId; |
| 380 } |
| 381 |
| 382 void _toggleEventId() { |
| 383 _eventState ^= _STATE_EVENT_ID; |
| 384 } |
| 385 |
| 386 bool get _isFiring => (_eventState & _STATE_FIRING) != 0; |
| 387 |
| 388 bool _setRemoveAfterFiring() { |
| 389 assert(_isFiring); |
| 390 _eventState |= _STATE_REMOVE_AFTER_FIRING; |
| 391 } |
| 392 |
| 393 bool get _removeAfterFiring => |
| 394 (_eventState & _STATE_REMOVE_AFTER_FIRING) != 0; |
| 395 } |
| 396 |
| 397 |
| 321 class _MultiplexStreamController<T> implements StreamController<T>, | 398 class _MultiplexStreamController<T> implements StreamController<T>, |
| 322 _StreamControllerLifecycle<T> { | 399 _StreamControllerLifecycle<T>, |
| 400 _MultiplexSubscriptionLink { |
| 401 static const int _STATE_INITIAL = 0; |
| 402 static const int _STATE_EVENT_ID = 1; |
| 403 static const int _STATE_FIRING = 2; |
| 404 static const int _STATE_CLOSED = 4; |
| 405 |
| 323 final _NotificationHandler _onListen; | 406 final _NotificationHandler _onListen; |
| 324 final _NotificationHandler _onCancel; | 407 final _NotificationHandler _onCancel; |
| 325 /** Set when the [close] method is called. */ | |
| 326 bool _isClosed = false; | |
| 327 | 408 |
| 328 // TODO(lrn): Make a more efficient implementation of these subscriptions, | 409 // State of the controller. |
| 329 // e.g., the traditional double-linked list with concurrent add and remove | 410 int _state = _STATE_INITIAL; |
| 330 // while firing. | |
| 331 Set<_BufferingStreamSubscription<T>> _streams; | |
| 332 | 411 |
| 333 _MultiplexStreamController(this._onListen, this._onCancel) | 412 // Double-linked list of active listeners. |
| 334 : _streams = new Set<_BufferingStreamSubscription<T>>(); | 413 _MultiplexSubscriptionLink _next; |
| 414 _MultiplexSubscriptionLink _previous; |
| 415 |
| 416 _MultiplexStreamController(this._onListen, this._onCancel) { |
| 417 _next = _previous = this; |
| 418 } |
| 335 | 419 |
| 336 // StreamController interface. | 420 // StreamController interface. |
| 337 | 421 |
| 338 Stream<T> get stream => new _ControllerStream<T>(this); | 422 Stream<T> get stream => new _MultiplexStream<T>(this); |
| 339 | 423 |
| 340 EventSink<T> get sink => new _EventSinkView<T>(this); | 424 EventSink<T> get sink => new _EventSinkView<T>(this); |
| 341 | 425 |
| 342 bool get isClosed => _isClosed; | 426 bool get isClosed => (_state & _STATE_CLOSED) != 0; |
| 343 | 427 |
| 344 /** | 428 /** |
| 345 * A multiplex controller is never paused. | 429 * A multiplex controller is never paused. |
| 346 * | 430 * |
| 347 * Each receiving stream may be paused individually, and they handle their | 431 * Each receiving stream may be paused individually, and they handle their |
| 348 * own buffering. | 432 * own buffering. |
| 349 */ | 433 */ |
| 350 bool get isPaused => false; | 434 bool get isPaused => false; |
| 351 | 435 |
| 352 /** Whether there are currently a subscriber on the [Stream]. */ | 436 /** Whether there are currently a subscriber on the [Stream]. */ |
| 353 bool get hasListener => !_streams.isEmpty; | 437 bool get hasListener => !_isEmpty; |
| 438 |
| 439 // Linked list helpers |
| 440 |
| 441 bool get _isEmpty => identical(_next, this); |
| 442 |
| 443 /** Adds subscription to linked list of active listeners. */ |
| 444 void _addListener(_MultiplexSubscription<T> subscription) { |
| 445 _MultiplexSubscriptionLink previous = _previous; |
| 446 previous._next = subscription; |
| 447 _previous = subscription._previous; |
| 448 subscription._previous._next = this; |
| 449 subscription._previous = previous; |
| 450 subscription._eventState = (_state & _STATE_EVENT_ID); |
| 451 } |
| 452 |
| 453 void _removeListener(_MultiplexSubscription<T> subscription) { |
| 454 assert(identical(subscription._controller, this)); |
| 455 assert(!identical(subscription._next, subscription)); |
| 456 subscription._previous._next = subscription._next; |
| 457 subscription._next._previous = subscription._previous; |
| 458 subscription._next = subscription._previous = subscription; |
| 459 } |
| 354 | 460 |
| 355 // _StreamControllerLifecycle interface. | 461 // _StreamControllerLifecycle interface. |
| 356 | 462 |
| 357 void _recordListen(_BufferingStreamSubscription<T> subscription) { | 463 void _recordListen(_MultiplexSubscription<T> subscription) { |
| 358 bool isFirst = _streams.isEmpty; | 464 _addListener(subscription); |
| 359 _streams.add(subscription); | 465 if (identical(_next, _previous)) { |
| 360 if (isFirst) { | 466 // Only one listener, so it must be the first listener. |
| 361 _runGuarded(_onListen); | 467 _runGuarded(_onListen); |
| 362 } | 468 } |
| 363 } | 469 } |
| 364 | 470 |
| 365 void _recordCancel(_BufferingStreamSubscription<T> subscription) { | 471 void _recordCancel(_MultiplexSubscription<T> subscription) { |
| 366 _streams.remove(subscription); | 472 if (subscription._isFiring) { |
| 367 if (_streams.isEmpty) { | 473 subscription._setRemoveAfterFiring(); |
| 368 _runGuarded(_onCancel); | 474 } else { |
| 475 _removeListener(subscription); |
| 476 // If we are currently firing an event, the empty-check is performed at |
| 477 // the end of the listener loop instead of here. |
| 478 if ((_state & _STATE_FIRING) == 0 && _isEmpty) { |
| 479 _runGuarded(_onCancel); |
| 480 } |
| 369 } | 481 } |
| 370 } | 482 } |
| 371 | 483 |
| 372 void _recordPause(StreamSubscription<T> subscription) {} | 484 void _recordPause(StreamSubscription<T> subscription) {} |
| 373 void _recordResume(StreamSubscription<T> subscription) {} | 485 void _recordResume(StreamSubscription<T> subscription) {} |
| 374 | 486 |
| 375 // EventSink interface. | 487 // EventSink interface. |
| 376 | 488 |
| 377 void add(T data) { | 489 void add(T data) { |
| 378 if (_streams.isEmpty) return; | 490 assert(!isClosed); |
| 491 if (_isEmpty) return; |
| 379 _forEachListener((_BufferingStreamSubscription<T> subscription) { | 492 _forEachListener((_BufferingStreamSubscription<T> subscription) { |
| 380 subscription._add(data); | 493 subscription._add(data); |
| 381 }); | 494 }); |
| 382 } | 495 } |
| 383 | 496 |
| 384 void addError(Object error, [Object stackTrace]) { | 497 void addError(Object error, [Object stackTrace]) { |
| 385 if (_streams.isEmpty) return; | 498 assert(!isClosed); |
| 499 if (_isEmpty) return; |
| 386 _forEachListener((_BufferingStreamSubscription<T> subscription) { | 500 _forEachListener((_BufferingStreamSubscription<T> subscription) { |
| 387 subscription._addError(error); | 501 subscription._addError(error); |
| 388 }); | 502 }); |
| 389 } | 503 } |
| 390 | 504 |
| 391 void close() { | 505 void close() { |
| 392 _isClosed = true; | 506 assert(!isClosed); |
| 393 if (_streams.isEmpty) return; | 507 _state |= _STATE_CLOSED; |
| 394 _forEachListener((_BufferingStreamSubscription<T> subscription) { | 508 if (_isEmpty) return; |
| 395 _streams.remove(subscription); | 509 _forEachListener((_MultiplexSubscription<T> subscription) { |
| 396 subscription._close(); | 510 subscription._close(); |
| 511 subscription._eventState |= |
| 512 _MultiplexSubscription._STATE_REMOVE_AFTER_FIRING; |
| 397 }); | 513 }); |
| 398 } | 514 } |
| 399 | 515 |
| 400 void _forEachListener( | 516 void _forEachListener( |
| 401 void action(_BufferingStreamSubscription<T> subscription)) { | 517 void action(_BufferingStreamSubscription<T> subscription)) { |
| 402 List<_BufferingStreamSubscription<T>> subscriptions = _streams.toList(); | 518 if ((_state & _STATE_FIRING) != 0) { |
| 403 for (_BufferingStreamSubscription<T> subscription in subscriptions) { | 519 throw new StateError( |
| 404 if (_streams.contains(subscription)) { | 520 "Cannot fire new event. Controller is already firing an event"); |
| 521 } |
| 522 if (_isEmpty) return; |
| 523 |
| 524 // Get event id of this event. |
| 525 int id = (_state & _STATE_EVENT_ID); |
| 526 // Start firing (set the _STATE_FIRING bit). We don't do [_onCancel] |
| 527 // callbacks while firing, and we prevent reentrancy of this function. |
| 528 // |
| 529 // Set [_state]'s event id to the next event's id. |
| 530 // Any listeners added while firing this event will expect the next event, |
| 531 // not this one, and won't get notified. |
| 532 _state ^= _STATE_EVENT_ID | _STATE_FIRING; |
| 533 _MultiplexSubscriptionLink link = _next; |
| 534 while (!identical(link, this)) { |
| 535 _MultiplexSubscription<T> subscription = link; |
| 536 if (subscription._expectsEvent(id)) { |
| 537 subscription._eventState |= _MultiplexSubscription._STATE_FIRING; |
| 405 action(subscription); | 538 action(subscription); |
| 539 subscription._toggleEventId(); |
| 540 link = subscription._next; |
| 541 if (subscription._removeAfterFiring) { |
| 542 _removeListener(subscription); |
| 543 } |
| 544 subscription._eventState &= ~_MultiplexSubscription._STATE_FIRING; |
| 545 } else { |
| 546 link = subscription._next; |
| 406 } | 547 } |
| 407 } | 548 } |
| 549 _state &= ~_STATE_FIRING; |
| 550 |
| 551 if (_isEmpty) { |
| 552 _runGuarded(_onCancel); |
| 553 } |
| 408 } | 554 } |
| 409 } | 555 } |
| 410 | |
| OLD | NEW |