Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(440)

Side by Side Diff: sdk/lib/async/stream_controller.dart

Issue 16007003: Optimize internals of multiplex-streams. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Address comments Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « pkg/scheduled_test/lib/src/mock_clock.dart ('k') | sdk/lib/html/dart2js/html_dart2js.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « pkg/scheduled_test/lib/src/mock_clock.dart ('k') | sdk/lib/html/dart2js/html_dart2js.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698