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

Unified Diff: sdk/lib/async/stream_controller.dart

Issue 15989006: Revert until Windows crash is debugged. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sdk/lib/async/stream.dart ('k') | sdk/lib/async/stream_impl.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/async/stream_controller.dart
diff --git a/sdk/lib/async/stream_controller.dart b/sdk/lib/async/stream_controller.dart
index 3412e48f459536d843eafefe0d3a1b02d36147d9..6ff3ee2417b7d41939bab032546af7abf7140b41 100644
--- a/sdk/lib/async/stream_controller.dart
+++ b/sdk/lib/async/stream_controller.dart
@@ -46,9 +46,8 @@ part of dart.async;
* the stream at all, and won't trigger callbacks. From the controller's point
* of view, the stream is completely inert when has completed.
*/
-abstract class StreamController<T> implements EventSink<T> {
- /** The stream that this controller is controlling. */
- Stream<T> get stream;
+class StreamController<T> extends EventSink<T> {
+ final _StreamImpl<T> stream;
/**
* A controller with a [stream] that supports only one single subscriber.
@@ -60,36 +59,23 @@ abstract class StreamController<T> implements EventSink<T> {
* paused. [onResume] is called when the stream resumed.
*
* The [onListen] callback is called when the stream
- * receives its listener and [onCancel] when the listener ends
+ * receives its listener. [onCancel] when the listener cancels
* its subscription.
*
* If the stream is canceled before the controller needs new data the
* [onResume] call might not be executed.
*/
- factory StreamController({void onListen(),
- void onPause(),
- void onResume(),
- void onCancel()})
- => new _StreamControllerImpl<T>(onListen, onPause, onResume, onCancel);
-
- /**
- * A controller where [stream] creates new stream each time it is read.
- *
- * The controller distributes any events to all currently subscribed streams.
- *
- * The [onListen] callback is called when the first listener is subscribed,
- * and the [onCancel] is called when there is no longer any active listeners.
- * If a listener is added again later, after the [onCancel] was called,
- * the [onListen] will be called again.
- */
- factory StreamController.multiplex({void onListen(), void onCancel()}) {
- return new _MultiplexStreamController<T>(onListen, onCancel);
- }
+ StreamController({void onListen(),
+ void onPause(),
+ void onResume(),
+ void onCancel()})
+ : stream = new _SingleControllerStream<T>(
+ onListen, onPause, onResume, onCancel);
/**
* Returns a view of this object that only exposes the [EventSink] interface.
*/
- EventSink<T> get sink;
+ EventSink<T> get sink => new _EventSinkView<T>(this);
/**
* Whether the stream is closed for adding more events.
@@ -97,314 +83,71 @@ abstract class StreamController<T> implements EventSink<T> {
* If true, the "done" event might not have fired yet, but it has been
* scheduled, and it is too late to add more events.
*/
- bool get isClosed;
-
- /** Whether the subscription is active and paused. */
- bool get isPaused;
-
- /** Whether there is a subscriber on the [Stream]. */
- bool get hasListener;
-
- /**
- * Send or enqueue an error event.
- *
- * Also allows an objection stack trace object, on top of what [EventSink]
- * allows.
- */
- void addError(Object error, [Object stackTrace]);
-}
-
-
-abstract class _StreamControllerLifecycle<T> {
- void _recordListen(StreamSubscription<T> subscription) {}
- void _recordPause(StreamSubscription<T> subscription) {}
- void _recordResume(StreamSubscription<T> subscription) {}
- void _recordCancel(StreamSubscription<T> subscription) {}
-}
-
-/**
- * Default implementation of [StreamController].
- *
- * Controls a stream that only supports a single controller.
- */
-class _StreamControllerImpl<T> implements StreamController<T>,
- _StreamControllerLifecycle<T> {
- static const int _STATE_OPEN = 0;
- static const int _STATE_CANCELLED = 1;
- static const int _STATE_CLOSED = 2;
-
- final _NotificationHandler _onListen;
- final _NotificationHandler _onPause;
- final _NotificationHandler _onResume;
- final _NotificationHandler _onCancel;
- _StreamImpl<T> _stream;
-
- // An active subscription on the stream, or null if no subscripton is active.
- _ControllerSubscription<T> _subscription;
-
- // Whether we have sent a "done" event.
- int _state = _STATE_OPEN;
-
- // Events added to the stream before it has an active subscription.
- _PendingEvents _pendingEvents = null;
-
- _StreamControllerImpl(this._onListen,
- this._onPause,
- this._onResume,
- this._onCancel) {
- _stream = new _ControllerStream<T>(this);
- }
-
- Stream<T> get stream => _stream;
-
- /**
- * Returns a view of this object that only exposes the [EventSink] interface.
- */
- EventSink<T> get sink => new _EventSinkView<T>(this);
-
- /**
- * Whether a listener has existed and been cancelled.
- *
- * After this, adding more events will be ignored.
- */
- bool get _isCancelled => (_state & _STATE_CANCELLED) != 0;
-
- bool get isClosed => (_state & _STATE_CLOSED) != 0;
+ bool get isClosed => stream._isClosed;
- bool get isPaused => _subscription != null && _subscription._isInputPaused;
+ /** Whether one or more active subscribers have requested a pause. */
+ bool get isPaused => stream._isInputPaused;
- bool get hasListener => _subscription != null;
+ /** Whether there are currently any subscribers on this [Stream]. */
+ bool get hasListener => stream._hasListener;
/**
* Send or queue a data event.
*/
- void add(T value) {
- if (isClosed) throw new StateError("Adding event after close");
- if (_subscription != null) {
- _subscription._add(value);
- } else if (!_isCancelled) {
- _addPendingEvent(new _DelayedData<T>(value));
- }
- }
+ void add(T value) => stream._add(value);
/**
* Send or enqueue an error event.
+ *
+ * If a subscription has requested to be unsubscribed on errors,
+ * it will be unsubscribed after receiving this event.
*/
void addError(Object error, [Object stackTrace]) {
- if (isClosed) throw new StateError("Adding event after close");
if (stackTrace != null) {
// Force stack trace overwrite. Even if the error already contained
// a stack trace.
_attachStackTrace(error, stackTrace);
}
- if (_subscription != null) {
- _subscription._addError(error);
- } else if (!_isCancelled) {
- _addPendingEvent(new _DelayedError(error));
- }
+ stream._addError(error);
}
/**
- * Closes this controller.
+ * Send or enqueue a "done" message.
*
- * After closing, no further events may be added using [add] or [addError].
- *
- * You are allowed to close the controller more than once, but only the first
- * call has any effect.
- *
- * The first time a controller is closed, a "done" event is sent to its
- * stream.
+ * The "done" message should be sent at most once by a stream, and it
+ * should be the last message sent.
*/
- void close() {
- if (isClosed) return;
- _state |= _STATE_CLOSED;
- if (_subscription != null) {
- _subscription._close();
- } else if (!_isCancelled) {
- _addPendingEvent(const _DelayedDone());
- }
- }
-
- void _addPendingEvent(_DelayedEvent event) {
- if (_isCancelled) return;
- _StreamImplEvents events = _pendingEvents;
- if (events == null) {
- events = _pendingEvents = new _StreamImplEvents();
- }
- events.add(event);
- }
-
- void _recordListen(_BufferingStreamSubscription<T> subscription) {
- assert(_subscription == null);
- _subscription = subscription;
- subscription._setPendingEvents(_pendingEvents);
- _pendingEvents = null;
- subscription._guardCallback(() {
- _runGuarded(_onListen);
- });
- }
-
- void _recordCancel(StreamSubscription<T> subscription) {
- assert(identical(_subscription, subscription));
- _subscription = null;
- _state |= _STATE_CANCELLED;
- _runGuarded(_onCancel);
- }
-
- void _recordPause(StreamSubscription<T> subscription) {
- _runGuarded(_onPause);
- }
-
- void _recordResume(StreamSubscription<T> subscription) {
- _runGuarded(_onResume);
- }
+ void close() { stream._close(); }
}
typedef void _NotificationHandler();
-void _runGuarded(_NotificationHandler notificationHandler) {
- if (notificationHandler == null) return;
- try {
- notificationHandler();
- } catch (e, s) {
- _throwDelayed(e, s);
- }
-}
-
-class _ControllerStream<T> extends _StreamImpl<T> {
- _StreamControllerLifecycle<T> _controller;
- bool _hasListener = false;
-
- _ControllerStream(this._controller);
-
- StreamSubscription<T> _createSubscription(
- void onData(T data),
- void onError(Object error),
- void onDone(),
- bool cancelOnError) {
- if (_hasListener) {
- throw new StateError("The stream has already been listened to.");
+class _SingleControllerStream<T> extends _SingleStreamImpl<T> {
+ _NotificationHandler _onListen;
+ _NotificationHandler _onPause;
+ _NotificationHandler _onResume;
+ _NotificationHandler _onCancel;
+
+ // TODO(floitsch): share this code with _MultiControllerStream.
+ _runGuarded(_NotificationHandler notificationHandler) {
+ if (notificationHandler == null) return;
+ try {
+ notificationHandler();
+ } catch (e, s) {
+ _throwDelayed(e, s);
}
- _hasListener = true;
- return new _ControllerSubscription<T>(
- _controller, onData, onError, onDone, cancelOnError);
- }
-
- void _onListen(_BufferingStreamSubscription subscription) {
- _controller._recordListen(subscription);
}
-}
-
-class _ControllerSubscription<T> extends _BufferingStreamSubscription<T> {
- final _StreamControllerLifecycle<T> _controller;
- _ControllerSubscription(this._controller,
- void onData(T data),
- void onError(Object error),
- void onDone(),
- bool cancelOnError)
- : super(onData, onError, onDone, cancelOnError);
-
- void _onCancel() {
- _controller._recordCancel(this);
- }
+ _SingleControllerStream(this._onListen,
+ this._onPause,
+ this._onResume,
+ this._onCancel);
- void _onPause() {
- _controller._recordPause(this);
+ void _onSubscriptionStateChange() {
+ _runGuarded(_hasListener ? _onListen : _onCancel);
}
- void _onResume() {
- _controller._recordResume(this);
+ void _onPauseStateChange() {
+ _runGuarded(_isPaused ? _onPause : _onResume);
}
}
-
-class _MultiplexStreamController<T> implements StreamController<T>,
- _StreamControllerLifecycle<T> {
- final _NotificationHandler _onListen;
- final _NotificationHandler _onCancel;
- /** Set when the [close] method is called. */
- bool _isClosed = false;
-
- // TODO(lrn): Make a more efficient implementation of these subscriptions,
- // e.g., the traditional double-linked list with concurrent add and remove
- // while firing.
- Set<_BufferingStreamSubscription<T>> _streams;
-
- _MultiplexStreamController(this._onListen, this._onCancel)
- : _streams = new Set<_BufferingStreamSubscription<T>>();
-
- // StreamController interface.
-
- Stream<T> get stream => new _ControllerStream<T>(this);
-
- EventSink<T> get sink => new _EventSinkView<T>(this);
-
- bool get isClosed => _isClosed;
-
- /**
- * A multiplex controller is never paused.
- *
- * Each receiving stream may be paused individually, and they handle their
- * own buffering.
- */
- bool get isPaused => false;
-
- /** Whether there are currently a subscriber on the [Stream]. */
- bool get hasListener => !_streams.isEmpty;
-
- // _StreamControllerLifecycle interface.
-
- void _recordListen(_BufferingStreamSubscription<T> subscription) {
- bool isFirst = _streams.isEmpty;
- _streams.add(subscription);
- if (isFirst) {
- _runGuarded(_onListen);
- }
- }
-
- void _recordCancel(_BufferingStreamSubscription<T> subscription) {
- _streams.remove(subscription);
- if (_streams.isEmpty) {
- _runGuarded(_onCancel);
- }
- }
-
- void _recordPause(StreamSubscription<T> subscription) {}
- void _recordResume(StreamSubscription<T> subscription) {}
-
- // EventSink interface.
-
- void add(T data) {
- if (_streams.isEmpty) return;
- _forEachListener((_BufferingStreamSubscription<T> subscription) {
- subscription._add(data);
- });
- }
-
- void addError(Object error, [Object stackTrace]) {
- if (_streams.isEmpty) return;
- _forEachListener((_BufferingStreamSubscription<T> subscription) {
- subscription._addError(error);
- });
- }
-
- void close() {
- _isClosed = true;
- if (_streams.isEmpty) return;
- _forEachListener((_BufferingStreamSubscription<T> subscription) {
- _streams.remove(subscription);
- subscription._close();
- });
- }
-
- void _forEachListener(
- void action(_BufferingStreamSubscription<T> subscription)) {
- List<_BufferingStreamSubscription<T>> subscriptions = _streams.toList();
- for (_BufferingStreamSubscription<T> subscription in subscriptions) {
- if (_streams.contains(subscription)) {
- action(subscription);
- }
- }
- }
-}
-
« no previous file with comments | « sdk/lib/async/stream.dart ('k') | sdk/lib/async/stream_impl.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698