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

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

Issue 12393009: Change and structure how Stream implementations do callbacks. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 9 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
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 // States shared by single/multi stream implementations. 7 // States shared by single/multi stream implementations.
8 8
9 // Completion state of the stream.
9 /// Initial and default state where the stream can receive and send events. 10 /// Initial and default state where the stream can receive and send events.
10 const int _STREAM_OPEN = 0; 11 const int _STREAM_OPEN = 0;
11 /// The stream has received a request to complete, but hasn't done so yet. 12 /// The stream has received a request to complete, but hasn't done so yet.
12 /// No further events can be added to the stream. 13 /// No further events can be added to the stream.
13 const int _STREAM_CLOSED = 1; 14 const int _STREAM_CLOSED = 1;
14 /// The stream has completed and will no longer receive or send events. 15 /// The stream has completed and will no longer receive or send events.
15 /// Also counts as closed. The stream must not be paused when it's completed. 16 /// Also counts as closed. The stream must not be paused when it's completed.
16 /// Always used in conjunction with [_STREAM_CLOSED]. 17 /// Always used in conjunction with [_STREAM_CLOSED].
17 const int _STREAM_COMPLETE = 2; 18 const int _STREAM_COMPLETE = 2;
19
18 /// Bit that alternates between events, and listeners are updated to the 20 /// Bit that alternates between events, and listeners are updated to the
19 /// current value when they are notified of the event. 21 /// current value when they are notified of the event.
20 const int _STREAM_EVENT_ID = 4; 22 const int _STREAM_EVENT_ID = 4;
21 const int _STREAM_EVENT_ID_SHIFT = 2; 23 const int _STREAM_EVENT_ID_SHIFT = 2;
24
25 // The activity state of the stream: What is it currently doing.
22 /// Bit set while firing and clear while not. 26 /// Bit set while firing and clear while not.
23 const int _STREAM_FIRING = 8; 27 const int _STREAM_FIRING = 8;
24 /// Bit set while calling a pause-state or subscription-state change callback. 28 /// Bit set while calling a pause-state or subscription-state change callback.
25 const int _STREAM_CALLBACK = 16; 29 const int _STREAM_CALLBACK = 16;
30
31 // The pause state of the stream.
32 /// Bit set when resuming with pending events. Cleared after all pending events
33 /// have been transmitted. Means that the controller still considers the
34 /// stream paused, even if the listener doesn't.
35 const int _STREAM_PENDING_RESUME = 32;
26 /// The count of times a stream has paused is stored in the 36 /// The count of times a stream has paused is stored in the
27 /// state, shifted by this amount. 37 /// state, shifted by this amount.
28 const int _STREAM_PAUSE_COUNT_SHIFT = 5; 38 const int _STREAM_PAUSE_COUNT_SHIFT = 6;
29 39
30 // States for listeners. 40 // States for listeners.
31 41
32 /// The listener is currently not subscribed to its source stream. 42 /// The listener is currently not subscribed to its source stream.
33 const int _LISTENER_UNSUBSCRIBED = 0; 43 const int _LISTENER_UNSUBSCRIBED = 0;
34 /// The listener is actively subscribed to its source stream. 44 /// The listener is actively subscribed to its source stream.
35 const int _LISTENER_SUBSCRIBED = 1; 45 const int _LISTENER_SUBSCRIBED = 1;
36 /// The listener is subscribed until it has been notified of the current event. 46 /// The listener is subscribed until it has been notified of the current event.
37 /// This flag bit is always used in conjuction with [_LISTENER_SUBSCRIBED]. 47 /// This flag bit is always used in conjuction with [_LISTENER_SUBSCRIBED].
38 const int _LISTENER_PENDING_UNSUBSCRIBE = 2; 48 const int _LISTENER_PENDING_UNSUBSCRIBE = 2;
49
39 /// Bit that contains the last sent event's "id bit". 50 /// Bit that contains the last sent event's "id bit".
40 const int _LISTENER_EVENT_ID = 4; 51 const int _LISTENER_EVENT_ID = 4;
41 const int _LISTENER_EVENT_ID_SHIFT = 2; 52 const int _LISTENER_EVENT_ID_SHIFT = 2;
53
42 /// The count of times a listener has paused is stored in the 54 /// The count of times a listener has paused is stored in the
43 /// state, shifted by this amount. 55 /// state, shifted by this amount.
44 const int _LISTENER_PAUSE_COUNT_SHIFT = 3; 56 const int _LISTENER_PAUSE_COUNT_SHIFT = 3;
45 57
46 58
47 // ------------------------------------------------------------------- 59 // -------------------------------------------------------------------
48 // Common base class for single and multi-subscription streams. 60 // Common base class for single and multi-subscription streams.
49 // ------------------------------------------------------------------- 61 // -------------------------------------------------------------------
50 abstract class _StreamImpl<T> extends Stream<T> { 62 abstract class _StreamImpl<T> extends Stream<T> {
51 /** Current state of the stream. */ 63 /** Current state of the stream. */
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
85 // StreamSink interface-like methods for sending events into the stream. 97 // StreamSink interface-like methods for sending events into the stream.
86 // It's the responsibility of the caller to ensure that the stream is not 98 // It's the responsibility of the caller to ensure that the stream is not
87 // paused when adding events. If the stream is paused, the events will be 99 // paused when adding events. If the stream is paused, the events will be
88 // queued, but it's better to not send events at all. 100 // queued, but it's better to not send events at all.
89 101
90 /** 102 /**
91 * Send or queue a data event. 103 * Send or queue a data event.
92 */ 104 */
93 void _add(T value) { 105 void _add(T value) {
94 if (_isClosed) throw new StateError("Sending on closed stream"); 106 if (_isClosed) throw new StateError("Sending on closed stream");
95 if (!_canFireEvent) { 107 if (!_mayFireState) {
108 // Not the time to send events.
96 _addPendingEvent(new _DelayedData<T>(value)); 109 _addPendingEvent(new _DelayedData<T>(value));
97 return; 110 return;
98 } 111 }
99 _sendData(value); 112 if (_hasPendingEvent) {
113 _addPendingEvent(new _DelayedData<T>(value));
114 } else {
115 _sendData(value);
116 }
100 _handlePendingEvents(); 117 _handlePendingEvents();
101 } 118 }
102 119
103 /** 120 /**
104 * Send or enqueue an error event. 121 * Send or enqueue an error event.
105 * 122 *
106 * If a subscription has requested to be unsubscribed on errors, 123 * If a subscription has requested to be unsubscribed on errors,
107 * it will be unsubscribed after receiving this event. 124 * it will be unsubscribed after receiving this event.
108 */ 125 */
109 void _signalError(AsyncError error) { 126 void _signalError(AsyncError error) {
110 if (_isClosed) throw new StateError("Sending on closed stream"); 127 if (_isClosed) throw new StateError("Sending on closed stream");
111 if (!_canFireEvent) { 128 if (!_mayFireState) {
129 // Not the time to send events.
112 _addPendingEvent(new _DelayedError(error)); 130 _addPendingEvent(new _DelayedError(error));
113 return; 131 return;
114 } 132 }
115 _sendError(error); 133 if (_hasPendingEvent) {
134 _addPendingEvent(new _DelayedError(error));
135 } else {
136 _sendError(error);
137 }
116 _handlePendingEvents(); 138 _handlePendingEvents();
117 } 139 }
118 140
119 /** 141 /**
120 * Send or enqueue a "done" message. 142 * Send or enqueue a "done" message.
121 * 143 *
122 * The "done" message should be sent at most once by a stream, and it 144 * The "done" message should be sent at most once by a stream, and it
123 * should be the last message sent. 145 * should be the last message sent.
124 */ 146 */
125 void _close() { 147 void _close() {
126 if (_isClosed) return; 148 if (_isClosed) return;
127 _state |= _STREAM_CLOSED; 149 _state |= _STREAM_CLOSED;
128 if (!_canFireEvent) { 150 if (!_mayFireState) {
129 // You can't enqueue an event after the Done, so make it const. 151 // Not the time to send events.
130 _addPendingEvent(const _DelayedDone()); 152 _addPendingEvent(const _DelayedDone());
131 return; 153 return;
132 } 154 }
133 _sendDone(); 155 if (_hasPendingEvent) {
134 assert(!_hasPendingEvent); 156 _addPendingEvent(new _DelayedDone());
157 _handlePendingEvents();
158 } else {
159 _sendDone();
160 assert(_isComplete);
161 assert(!_hasPendingEvent);
162 }
135 } 163 }
136 164
137 // ------------------------------------------------------------------- 165 // -------------------------------------------------------------------
138 // Internal implementation. 166 // Internal implementation.
139 167
140 // State prediates. 168 // State prediates.
floitsch 2013/03/01 21:52:30 predicates
Lasse Reichstein Nielsen 2013/03/04 11:53:02 Done.
141 169
170 // Lifecycle state.
171 /** Whether the stream is in the default, open, state for events. */
172 bool get _isOpen => (_state & (_STREAM_CLOSED | _STREAM_COMPLETE)) == 0;
173
142 /** Whether the stream has been closed (a done event requested). */ 174 /** Whether the stream has been closed (a done event requested). */
143 bool get _isClosed => (_state & _STREAM_CLOSED) != 0; 175 bool get _isClosed => (_state & _STREAM_CLOSED) != 0;
144 176
145 /** Whether the stream is completed. */ 177 /** Whether the stream is completed. */
146 bool get _isComplete => (_state & _STREAM_COMPLETE) != 0; 178 bool get _isComplete => (_state & _STREAM_COMPLETE) != 0;
147 179
180 // Pause state.
181
148 /** Whether one or more active subscribers have requested a pause. */ 182 /** Whether one or more active subscribers have requested a pause. */
149 bool get _isPaused => _state >= (1 << _STREAM_PAUSE_COUNT_SHIFT); 183 bool get _isPaused => _state >= (1 << _STREAM_PAUSE_COUNT_SHIFT);
150 184
185 /** How many times the stream has been paused. */
186 int get _pauseCount => _state >> _STREAM_PAUSE_COUNT_SHIFT;
187
188 /**
189 * Whether a controller thinks the stream is paused.
190 *
191 * When this changes, a pause-state change callback is performed.
192 *
193 * It may differ from [_isPaused] if there are pending events
194 * in the queue when the listeners resume. The controller won't
195 * be informed until all queued events have been fired.
196 */
197 bool get _isControllerPaused => _state >= (_STREAM_PENDING_RESUME);
198
199 /** Whether we have a pending resume scheduled. */
200 bool get _hasPendingResume => (_state & _STREAM_PENDING_RESUME) != 0;
201
202
203 // Action state. If the stream makes a call-out to external code,
204 // this state tracks it and avoids reentrancy problems.
205
206 /** Whether the stream is not currently firing or calling a callback. */
207 bool get _isInactive => (_state & (_STREAM_CALLBACK | _STREAM_FIRING)) == 0;
208
151 /** Whether we are currently executing a state-chance callback. */ 209 /** Whether we are currently executing a state-chance callback. */
152 bool get _isInCallback => (_state & _STREAM_CALLBACK) != 0; 210 bool get _isInCallback => (_state & _STREAM_CALLBACK) != 0;
153 211
212 /** Whether we are currently firing an event. */
213 bool get _isFiring => (_state & _STREAM_FIRING) != 0;
214
154 /** Check whether the pending event queue is non-empty */ 215 /** Check whether the pending event queue is non-empty */
155 bool get _hasPendingEvent => 216 bool get _hasPendingEvent =>
156 _pendingEvents != null && !_pendingEvents.isEmpty; 217 _pendingEvents != null && !_pendingEvents.isEmpty;
157 218
158 /** Whether we are currently firing an event. */ 219 /**
159 bool get _isFiring => (_state & _STREAM_FIRING) != 0; 220 * The bit representing the current or last event fired.
221 *
222 * This bit matches a bit on listeners that have received the corresponding
223 * event. It is toggled for each new event being fired.
224 */
225 int get _currentEventIdBit =>
226 (_state & _STREAM_EVENT_ID ) >> _STREAM_EVENT_ID_SHIFT;
227
228 /** Whether there is currently a subscriber on this [Stream]. */
229 bool get _hasSubscribers;
230
160 231
161 /** Whether the state bits allow firing. */ 232 /** Whether the state bits allow firing. */
162 bool get _mayFireState { 233 bool get _mayFireState {
163 // The state disallows firing if: 234 // The state allows firing unless:
164 // - an event is currently firing 235 // - it's currently firing
165 // - a stat-change callback is being called 236 // - it's currently in a callback
166 // - the pause-count is not zero. 237 // - it's paused
167 const int mask = 238 const int mask =
168 _STREAM_FIRING | 239 _STREAM_FIRING |
169 _STREAM_CALLBACK | 240 _STREAM_CALLBACK |
170 ~((1 << _STREAM_PAUSE_COUNT_SHIFT) - 1); 241 ~((1 << _STREAM_PAUSE_COUNT_SHIFT) - 1);
171 return (_state & mask) == 0; 242 return (_state & mask) == 0;
172 } 243 }
173 244
174 int get _currentEventIdBit =>
175 (_state & _STREAM_EVENT_ID ) >> _STREAM_EVENT_ID_SHIFT;
176
177 /** Whether there is currently a subscriber on this [Stream]. */
178 bool get _hasSubscribers;
179
180 /** Whether the stream can fire a new event. */
181 bool get _canFireEvent => _mayFireState && !_hasPendingEvent;
182
183 // State modification. 245 // State modification.
184 246
185 /** Record an increases in the number of times the listener has paused. */ 247 /** Record an increases in the number of times the listener has paused. */
186 void _incrementPauseCount(_StreamListener<T> listener) { 248 void _incrementPauseCount(_StreamListener<T> listener) {
187 listener._incrementPauseCount(); 249 listener._incrementPauseCount();
250 _state &= ~_STREAM_PENDING_RESUME;
188 _updatePauseCount(1); 251 _updatePauseCount(1);
189 } 252 }
190 253
191 /** Record a decrease in the number of times the listener has paused. */ 254 /** Record a decrease in the number of times the listener has paused. */
192 void _decrementPauseCount(_StreamListener<T> listener) { 255 void _decrementPauseCount(_StreamListener<T> listener) {
193 assert(_isPaused); 256 assert(_isPaused);
194 listener._decrementPauseCount(); 257 listener._decrementPauseCount();
195 _updatePauseCount(-1); 258 _updatePauseCount(-1);
196 } 259 }
197 260
(...skipping 17 matching lines...) Expand all
215 _state |= _STREAM_CLOSED; 278 _state |= _STREAM_CLOSED;
216 } 279 }
217 280
218 void _setComplete() { 281 void _setComplete() {
219 assert(_isClosed); 282 assert(_isClosed);
220 _state = _state |_STREAM_COMPLETE; 283 _state = _state |_STREAM_COMPLETE;
221 } 284 }
222 285
223 void _startFiring() { 286 void _startFiring() {
224 assert(!_isFiring); 287 assert(!_isFiring);
288 assert(!_isInCallback);
225 assert(_hasSubscribers); 289 assert(_hasSubscribers);
226 assert(!_isPaused); 290 assert(!_isPaused);
227 // This sets the _STREAM_FIRING bit and toggles the _STREAM_EVENT_ID 291 // This sets the _STREAM_FIRING bit and toggles the _STREAM_EVENT_ID
228 // bit. All current subscribers will now have a _LISTENER_EVENT_ID 292 // bit. All current subscribers will now have a _LISTENER_EVENT_ID
229 // that doesn't match _STREAM_EVENT_ID, and they will receive the 293 // that doesn't match _STREAM_EVENT_ID, and they will receive the
230 // event being fired. 294 // event being fired.
231 _state ^= _STREAM_FIRING | _STREAM_EVENT_ID; 295 _state ^= _STREAM_FIRING | _STREAM_EVENT_ID;
232 } 296 }
233 297
234 void _endFiring() { 298 void _endFiring(bool wasControllerPaused) {
235 assert(_isFiring); 299 assert(_isFiring);
236 _state ^= _STREAM_FIRING; 300 _state ^= _STREAM_FIRING;
237 if (!_hasSubscribers) { 301 // Had listeners, or we wouldn't have fired.
238 _callOnSubscriptionStateChange(); 302 _checkCallbacks(true, wasControllerPaused);
239 } else if (_isPaused) {
240 _callOnPauseStateChange();
241 }
242 } 303 }
243 304
244 /** 305 /**
245 * Record that a listener wants a pause from events. 306 * Record that a listener wants a pause from events.
246 * 307 *
247 * This methods is called from [_StreamListener.pause()]. 308 * This methods is called from [_StreamListener.pause()].
248 * Subclasses can override this method, along with [isPaused] and 309 * Subclasses can override this method, along with [isPaused] and
249 * [createSubscription], if they want to do a different handling of paused 310 * [createSubscription], if they want to do a different handling of paused
250 * subscriptions, e.g., a filtering stream pausing its own source if all its 311 * subscriptions, e.g., a filtering stream pausing its own source if all its
251 * subscribers are paused. 312 * subscribers are paused.
252 */ 313 */
253 void _pause(_StreamListener<T> listener, Future resumeSignal) { 314 void _pause(_StreamListener<T> listener, Future resumeSignal) {
254 assert(identical(listener._source, this)); 315 assert(identical(listener._source, this));
255 if (!listener._isSubscribed) { 316 if (!listener._isSubscribed) {
256 throw new StateError("Subscription has been canceled."); 317 throw new StateError("Subscription has been canceled.");
257 } 318 }
258 assert(!_isComplete); // There can be no subscribers when complete. 319 assert(!_isComplete); // There can be no subscribers when complete.
320 bool wasControllerPaused = _isControllerPaused;
floitsch 2013/03/01 21:52:30 what about calling this "wasInputPaused" ?
Lasse Reichstein Nielsen 2013/03/04 11:53:02 The real name should be "wasLastPauseStateChangeCa
259 bool wasPaused = _isPaused; 321 bool wasPaused = _isPaused;
260 _incrementPauseCount(listener); 322 _incrementPauseCount(listener);
261 if (resumeSignal != null) { 323 if (resumeSignal != null) {
262 resumeSignal.whenComplete(() { this._resume(listener, true); }); 324 resumeSignal.whenComplete(() { this._resume(listener, true); });
263 } 325 }
264 if (!wasPaused && !_isFiring) { 326 if (!wasPaused && _hasPendingEvent && _pendingEvents.isScheduled) {
265 _callOnPauseStateChange(); 327 _pendingEvents.cancelSchedule();
328 }
329 if (_isInactive && !wasControllerPaused) {
330 _checkCallbacks(true, false);
331 if (!_isPaused && _hasPendingEvent) {
332 _schedulePendingEvents();
333 }
266 } 334 }
267 } 335 }
268 336
269 /** Stops pausing due to one request from the given listener. */ 337 /** Stops pausing due to one request from the given listener. */
270 void _resume(_StreamListener<T> listener, bool fromEvent) { 338 void _resume(_StreamListener<T> listener, bool fromEvent) {
271 if (!listener.isPaused) return; 339 if (!listener.isPaused) return;
272 assert(listener._isSubscribed); 340 assert(listener._isSubscribed);
273 assert(_isPaused); 341 assert(_isPaused);
274 _decrementPauseCount(listener); 342 _decrementPauseCount(listener);
275 if (!_isPaused) { 343 if (!_isPaused) {
276 if (!_isFiring) _callOnPauseStateChange();
277 if (_hasPendingEvent) { 344 if (_hasPendingEvent) {
345 _state |= _STREAM_PENDING_RESUME;
346 // Controller's pause state haven't changed.
floitsch 2013/03/01 21:52:30 hasn't
278 // If we can fire events now, fire any pending events right away. 347 // If we can fire events now, fire any pending events right away.
279 if (fromEvent && !_isFiring) { 348 if (_isInactive) {
280 _handlePendingEvents(); 349 if (fromEvent) {
281 } else { 350 _handlePendingEvents();
282 _schedulePendingEvents(); 351 } else {
352 _schedulePendingEvents();
353 }
354 }
355 } else if (_isInactive) {
356 _checkCallbacks(true, true);
357 if (!_isPaused && _hasPendingEvent) {
358 if (fromEvent) {
359 _handlePendingEvents();
360 } else {
361 _schedulePendingEvents();
362 }
283 } 363 }
284 } 364 }
285 } 365 }
286 } 366 }
287 367
288 /** Schedule pending events to be executed. */ 368 /** Schedule pending events to be executed. */
289 void _schedulePendingEvents() { 369 void _schedulePendingEvents() {
290 assert(_hasPendingEvent); 370 assert(_hasPendingEvent);
291 _pendingEvents.schedule(this); 371 _pendingEvents.schedule(this);
292 } 372 }
(...skipping 30 matching lines...) Expand all
323 * Any change in the pause state is only reported after all subscribers have 403 * Any change in the pause state is only reported after all subscribers have
324 * received the event. 404 * received the event.
325 * 405 *
326 * The [action] must not throw, or the controller will be left in an 406 * The [action] must not throw, or the controller will be left in an
327 * invalid state. 407 * invalid state.
328 * 408 *
329 * This method must not be called while [isFiring] is true. 409 * This method must not be called while [isFiring] is true.
330 */ 410 */
331 void _forEachSubscriber(void action(_StreamSubscriptionImpl<T> subscription)); 411 void _forEachSubscriber(void action(_StreamSubscriptionImpl<T> subscription));
332 412
333 /** Calls [_onPauseStateChange] while setting callback bit. */ 413 /**
334 void _callOnPauseStateChange() { 414 * Checks whether the subscription/pause state has changed.
335 // After calling [_close], all pauses are handled internally by the Stream. 415 *
336 if (_isClosed) return; 416 * Calls the appropriate callback if the state has changed from the
337 if (!_isInCallback) { 417 * provided one. Repeats calling callbacks as long as the call changes
338 _state |= _STREAM_CALLBACK; 418 * the state.
339 _onPauseStateChange(); 419 */
340 _state ^= _STREAM_CALLBACK; 420 void _checkCallbacks(bool hadSubscribers, bool wasPaused) {
341 } else { 421 assert(!_isFiring);
342 _onPauseStateChange(); 422 // Will be handled after the current callback.
423 if (_isInCallback) return;
424 if (_hasPendingResume && !_hasPendingEvent) {
425 _state ^= _STREAM_PENDING_RESUME;
426 }
427 _state |= _STREAM_CALLBACK;
428 while (true) {
429 bool hasSubscribers = _hasSubscribers;
430 bool isPaused = _isControllerPaused;
431 if (hadSubscribers != hasSubscribers) {
432 _onSubscriptionStateChange();
433 } else if (isPaused != wasPaused) {
434 _onPauseStateChange();
435 } else {
436 _state ^= _STREAM_CALLBACK;
437 return;
438 }
439 wasPaused = isPaused;
440 hadSubscribers = hasSubscribers;
343 } 441 }
344 } 442 }
345 443
346 /** Calls [_onSubscriptionStateChange] while setting callback bit. */
347 void _callOnSubscriptionStateChange() {
348 if (!_isInCallback) {
349 _state |= _STREAM_CALLBACK;
350 _onSubscriptionStateChange();
351 _state ^= _STREAM_CALLBACK;
352 } else {
353 _onSubscriptionStateChange();
354 }
355 }
356
357 /** 444 /**
358 * Called when the first subscriber requests a pause or the last a resume. 445 * Called when the first subscriber requests a pause or the last a resume.
359 * 446 *
360 * Read [isPaused] to see the new state. 447 * Read [isPaused] to see the new state.
361 */ 448 */
362 void _onPauseStateChange() {} 449 void _onPauseStateChange() {}
363 450
364 /** 451 /**
365 * Called when the first listener subscribes or the last unsubscribes. 452 * Called when the first listener subscribes or the last unsubscribes.
366 * 453 *
367 * Read [hasSubscribers] to see what the new state is. 454 * Read [hasSubscribers] to see what the new state is.
368 */ 455 */
369 void _onSubscriptionStateChange() {} 456 void _onSubscriptionStateChange() {}
370 457
371 /** Add a pending event at the end of the pending event queue. */ 458 /**
459 * Add a pending event at the end of the pending event queue.
460 *
461 * Only called from [_add], [_signalError] and [_close], so in some
462 * cases events may be fired from here.
floitsch 2013/03/01 21:52:30 That sounds bad. adding a pending event should not
Lasse Reichstein Nielsen 2013/03/04 11:53:02 True, it is unnecessary here.
463 */
372 void _addPendingEvent(_DelayedEvent event) { 464 void _addPendingEvent(_DelayedEvent event) {
373 if (_pendingEvents == null) _pendingEvents = new _StreamImplEvents(); 465 if (_pendingEvents == null) _pendingEvents = new _StreamImplEvents();
374 _StreamImplEvents events = _pendingEvents; 466 _StreamImplEvents events = _pendingEvents;
375 events.add(event); 467 events.add(event);
468 if (_isPaused || _isFiring) return;
469 if (_isInCallback) {
470 _schedulePendingEvents();
471 return;
472 }
473 // Event only delayed because of other events in queue, so just fire them
474 // now.
475 _handlePendingEvents();
376 } 476 }
377 477
378 /** Fire any pending events until the pending event queue. */ 478 /** Fire any pending events until the pending event queue is empty. */
379 void _handlePendingEvents() { 479 void _handlePendingEvents() {
480 assert(_isInactive);
481 if (!_hasPendingEvent) return;
380 _PendingEvents events = _pendingEvents; 482 _PendingEvents events = _pendingEvents;
381 if (events == null) return; 483 do {
382 while (!events.isEmpty && !_isPaused) { 484 if (_isPaused) return;
485 if (events.isScheduled) events.cancelSchedule();
383 events.handleNext(this); 486 events.handleNext(this);
384 } 487 } while (!events.isEmpty);
385 } 488 }
386 489
387 /** 490 /**
388 * Send a data event directly to each subscriber. 491 * Send a data event directly to each subscriber.
389 */ 492 */
390 _sendData(T value) { 493 _sendData(T value) {
391 assert(!_isPaused); 494 assert(!_isPaused);
392 assert(!_isComplete); 495 assert(!_isComplete);
496 if (!_hasSubscribers) return;
393 _forEachSubscriber((subscriber) { 497 _forEachSubscriber((subscriber) {
394 try { 498 try {
395 subscriber._sendData(value); 499 subscriber._sendData(value);
396 } on AsyncError catch (e) { 500 } on AsyncError catch (e) {
397 e.throwDelayed(); 501 e.throwDelayed();
398 } catch (e, s) { 502 } catch (e, s) {
399 new AsyncError(e, s).throwDelayed(); 503 new AsyncError(e, s).throwDelayed();
400 } 504 }
401 }); 505 });
402 } 506 }
403 507
404 /** 508 /**
405 * Sends an error event directly to each subscriber. 509 * Sends an error event directly to each subscriber.
406 */ 510 */
407 void _sendError(AsyncError error) { 511 void _sendError(AsyncError error) {
408 assert(!_isPaused); 512 assert(!_isPaused);
409 assert(!_isComplete); 513 assert(!_isComplete);
514 if (!_hasSubscribers) return;
410 _forEachSubscriber((subscriber) { 515 _forEachSubscriber((subscriber) {
411 try { 516 try {
412 subscriber._sendError(error); 517 subscriber._sendError(error);
413 } on AsyncError catch (e) { 518 } on AsyncError catch (e) {
414 e.throwDelayed(); 519 e.throwDelayed();
415 } catch (e, s) { 520 } catch (e, s) {
416 new AsyncError.withCause(e, s, error).throwDelayed(); 521 new AsyncError.withCause(e, s, error).throwDelayed();
417 } 522 }
418 }); 523 });
419 } 524 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
460 * some methods made public. 565 * some methods made public.
461 * 566 *
462 * The user interface of [_SingleStreamImpl] are the following methods: 567 * The user interface of [_SingleStreamImpl] are the following methods:
463 * * [_add]: Add a data event to the stream. 568 * * [_add]: Add a data event to the stream.
464 * * [_signalError]: Add an error event to the stream. 569 * * [_signalError]: Add an error event to the stream.
465 * * [_close]: Request to close the stream. 570 * * [_close]: Request to close the stream.
466 * * [_onSubscriberStateChange]: Called when receiving the first subscriber or 571 * * [_onSubscriberStateChange]: Called when receiving the first subscriber or
467 * when losing the last subscriber. 572 * when losing the last subscriber.
468 * * [_onPauseStateChange]: Called when entering or leaving paused mode. 573 * * [_onPauseStateChange]: Called when entering or leaving paused mode.
469 * * [_hasSubscribers]: Test whether there are currently any subscribers. 574 * * [_hasSubscribers]: Test whether there are currently any subscribers.
470 * * [_isPaused]: Test whether the stream is currently paused. 575 * * [_isControllerPaused]: Test whether the stream is currently paused.
floitsch 2013/03/01 21:52:30 We still have the "_isPaused" too. Is that one not
Lasse Reichstein Nielsen 2013/03/04 11:53:02 True, it's not considered "public". Only use it in
471 * The user should not add new events while the stream is paused, but if it 576 * The user should not add new events while the stream is paused, but if it
472 * happens anyway, the stream will enqueue the events just as when new events 577 * happens anyway, the stream will enqueue the events just as when new events
473 * arrive while still firing an old event. 578 * arrive while still firing an old event.
474 */ 579 */
475 class _SingleStreamImpl<T> extends _StreamImpl<T> { 580 class _SingleStreamImpl<T> extends _StreamImpl<T> {
476 _StreamListener _subscriber = null; 581 _StreamListener _subscriber = null;
477 582
478 // A single-stream is considered paused when it has no subscriber.
479 // Exception is when it's complete (which only matters for pause-state-change
480 // callbacks), where it's not considered paused.
481 bool get _isPaused => (!_hasSubscribers && !_isComplete) || super._isPaused;
482
483
484 bool get _canFireEvent {
485 // A single-stream is considered paused when it has no subscriber and
486 // isn't complete, so it can't fire if there is no subscriber.
487 // It won't try to fire when completed, so no need to check that.
488 return _mayFireState && !_hasPendingEvent && _hasSubscribers;
489 }
490
491
492 /** Whether there is currently a subscriber on this [Stream]. */ 583 /** Whether there is currently a subscriber on this [Stream]. */
493 bool get _hasSubscribers => _subscriber != null; 584 bool get _hasSubscribers => _subscriber != null;
494 585
495 // ------------------------------------------------------------------- 586 // -------------------------------------------------------------------
496 // Internal implementation. 587 // Internal implementation.
497 588
589 _SingleStreamImpl() {
590 // Start out paused.
591 _updatePauseCount(1);
592 }
593
498 /** 594 /**
499 * Create the new subscription object. 595 * Create the new subscription object.
500 */ 596 */
501 _StreamSubscriptionImpl<T> _createSubscription( 597 _StreamSubscriptionImpl<T> _createSubscription(
502 void onData(T data), 598 void onData(T data),
503 void onError(AsyncError error), 599 void onError(AsyncError error),
504 void onDone(), 600 void onDone(),
505 bool unsubscribeOnError) { 601 bool unsubscribeOnError) {
506 return new _StreamSubscriptionImpl<T>( 602 return new _StreamSubscriptionImpl<T>(
507 this, onData, onError, onDone, unsubscribeOnError); 603 this, onData, onError, onDone, unsubscribeOnError);
508 } 604 }
509 605
510 void _addListener(_StreamListener subscription) { 606 void _addListener(_StreamListener subscription) {
607 assert(!_isComplete);
511 if (_hasSubscribers) { 608 if (_hasSubscribers) {
512 throw new StateError("Stream already has subscriber."); 609 throw new StateError("Stream already has subscriber.");
513 } 610 }
611 assert(_pauseCount == 1);
612 _updatePauseCount(-1);
514 _subscriber = subscription; 613 _subscriber = subscription;
515 subscription._setSubscribed(0); 614 subscription._setSubscribed(0);
516 _callOnSubscriptionStateChange(); 615 if (_isInactive) {
517 if (_hasPendingEvent) { 616 _checkCallbacks(false, true);
518 _schedulePendingEvents(); 617 if (!_isPaused && _hasPendingEvent) {
618 _schedulePendingEvents();
619 }
519 } 620 }
520 } 621 }
521 622
522 /** 623 /**
523 * Handle a cancel requested from a [_StreamSubscriptionImpl]. 624 * Handle a cancel requested from a [_StreamSubscriptionImpl].
524 * 625 *
525 * This method is called from [_StreamSubscriptionImpl.cancel]. 626 * This method is called from [_StreamSubscriptionImpl.cancel].
526 *
527 * If an event is currently firing, the cancel is delayed
528 * until after the subscriber has received the event.
529 */ 627 */
530 void _cancel(_StreamListener subscriber) { 628 void _cancel(_StreamListener subscriber) {
531 assert(identical(subscriber._source, this)); 629 assert(identical(subscriber._source, this));
532 // We allow unsubscribing the currently firing subscription during 630 // We allow unsubscribing the currently firing subscription during
533 // the event firing, because it is indistinguishable from delaying it since 631 // the event firing, because it is indistinguishable from delaying it since
534 // that event has already received the event. 632 // that event has already received the event.
535 if (!identical(_subscriber, subscriber)) { 633 if (!identical(_subscriber, subscriber)) {
536 // You may unsubscribe more than once, only the first one counts. 634 // You may unsubscribe more than once, only the first one counts.
537 return; 635 return;
538 } 636 }
539 _subscriber = null; 637 _subscriber = null;
540 // Unsubscribing a paused subscription also cancels its pauses. 638 // Unsubscribing a paused subscription also cancels its pauses.
541 int subscriptionPauseCount = subscriber._setUnsubscribed(); 639 int resumeCount = subscriber._setUnsubscribed();
542 _updatePauseCount(-subscriptionPauseCount); 640 // Keep being paused while there is no subscriber and the stream is not
floitsch 2013/03/01 21:52:30 I don't understand this: why do we pause a Stream
Lasse Reichstein Nielsen 2013/03/04 11:53:02 This is how single-subscription streams have worke
543 if (!_isFiring) { 641 // complete.
544 _callOnSubscriptionStateChange(); 642 _updatePauseCount(_isComplete ? -resumeCount : -resumeCount + 1);
643 if (_isInactive) {
644 _checkCallbacks(true, resumeCount > 0);
645 if (!_isPaused && _hasPendingEvent) {
646 _schedulePendingEvents();
647 }
545 } 648 }
546 } 649 }
547 650
548 void _forEachSubscriber( 651 void _forEachSubscriber(
549 void action(_StreamListener<T> subscription)) { 652 void action(_StreamListener<T> subscription)) {
550 assert(!_isPaused); 653 assert(!_isPaused);
654 bool wasControllerPaused = _isControllerPaused;
551 _StreamListener subscription = _subscriber; 655 _StreamListener subscription = _subscriber;
552 assert(subscription != null); 656 assert(subscription != null);
553 _startFiring(); 657 _startFiring();
554 action(subscription); 658 action(subscription);
555 _endFiring(); 659 _endFiring(wasControllerPaused);
556 } 660 }
557 } 661 }
558 662
559 // ------------------------------------------------------------------- 663 // -------------------------------------------------------------------
560 // Default implementation of a stream with subscribers. 664 // Default implementation of a stream with subscribers.
561 // ------------------------------------------------------------------- 665 // -------------------------------------------------------------------
562 666
563 /** 667 /**
564 * Default implementation of stream capable of sending events to subscribers. 668 * Default implementation of stream capable of sending events to subscribers.
565 * 669 *
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
631 * 735 *
632 * The [action] must not throw, or the controller will be left in an 736 * The [action] must not throw, or the controller will be left in an
633 * invalid state. 737 * invalid state.
634 * 738 *
635 * This method must not be called while [isFiring] is true. 739 * This method must not be called while [isFiring] is true.
636 */ 740 */
637 void _forEachSubscriber( 741 void _forEachSubscriber(
638 void action(_StreamListener<T> subscription)) { 742 void action(_StreamListener<T> subscription)) {
639 assert(!_isFiring); 743 assert(!_isFiring);
640 if (!_hasSubscribers) return; 744 if (!_hasSubscribers) return;
745 bool wasControllerPaused = _isControllerPaused;
641 _startFiring(); 746 _startFiring();
642 _InternalLink cursor = this._nextLink; 747 _InternalLink cursor = this._nextLink;
643 while (!identical(cursor, this)) { 748 while (!identical(cursor, this)) {
644 _StreamListener<T> current = cursor; 749 _StreamListener<T> current = cursor;
645 if (current._needsEvent(_currentEventIdBit)) { 750 if (current._needsEvent(_currentEventIdBit)) {
646 action(current); 751 action(current);
647 // Marks as having received the event. 752 // Marks as having received the event.
648 current._toggleEventReceived(); 753 current._toggleEventReceived();
649 } 754 }
650 cursor = current._nextLink; 755 cursor = current._nextLink;
651 if (current._isPendingUnsubscribe) { 756 if (current._isPendingUnsubscribe) {
652 _removeListener(current); 757 _removeListener(current);
653 } 758 }
654 } 759 }
655 _endFiring(); 760 _endFiring(wasControllerPaused);
656 } 761 }
657 762
658 void _addListener(_StreamListener listener) { 763 void _addListener(_StreamListener listener) {
659 listener._setSubscribed(_currentEventIdBit); 764 listener._setSubscribed(_currentEventIdBit);
660 bool firstSubscriber = !_hasSubscribers; 765 bool hadSubscribers = _hasSubscribers;
661 _InternalLinkList.add(this, listener); 766 _InternalLinkList.add(this, listener);
662 if (firstSubscriber) { 767 if (!hadSubscribers && _isInactive) {
663 _callOnSubscriptionStateChange(); 768 _checkCallbacks(false, false);
769 if (!_isPaused && _hasPendingEvent) {
770 _schedulePendingEvents();
771 }
664 } 772 }
665 } 773 }
666 774
667 /** 775 /**
668 * Handle a cancel requested from a [_StreamListener]. 776 * Handle a cancel requested from a [_StreamListener].
669 * 777 *
670 * This method is called from [_StreamListener.cancel]. 778 * This method is called from [_StreamListener.cancel].
671 * 779 *
672 * If an event is currently firing, the cancel is delayed 780 * If an event is currently firing, the cancel is delayed
673 * until after the subscribers have received the event. 781 * until after the subscribers have received the event.
674 */ 782 */
675 void _cancel(_StreamListener listener) { 783 void _cancel(_StreamListener listener) {
676 assert(identical(listener._source, this)); 784 assert(identical(listener._source, this));
677 if (_InternalLink.isUnlinked(listener)) { 785 if (_InternalLink.isUnlinked(listener)) {
678 // You may unsubscribe more than once, only the first one counts. 786 // You may unsubscribe more than once, only the first one counts.
679 return; 787 return;
680 } 788 }
681 if (_isFiring) { 789 if (_isFiring) {
682 if (listener._needsEvent(_currentEventIdBit)) { 790 if (listener._needsEvent(_currentEventIdBit)) {
683 assert(listener._isSubscribed); 791 assert(listener._isSubscribed);
684 listener._setPendingUnsubscribe(); 792 listener._setPendingUnsubscribe(_currentEventIdBit);
685 } else { 793 } else {
686 // The listener has been notified of the event (or don't need to, 794 // The listener has been notified of the event (or don't need to,
687 // if it's still pending subscription) so it's safe to remove it. 795 // if it's still pending subscription) so it's safe to remove it.
688 _removeListener(listener); 796 _removeListener(listener);
689 } 797 }
690 // Pause and subscription state changes are reported when we end 798 // Pause and subscription state changes are reported when we end
691 // firing. 799 // firing.
692 } else { 800 } else {
693 bool wasPaused = _isPaused; 801 bool wasControllerPaused = _isControllerPaused;
694 _removeListener(listener); 802 _removeListener(listener);
695 if (wasPaused != _isPaused) _onPauseStateChange(); 803 if (_isInactive) {
696 if (!_hasSubscribers) _callOnSubscriptionStateChange(); 804 _checkCallbacks(true, wasControllerPaused);
805 if (!_isPaused && _hasPendingEvent) {
806 _schedulePendingEvents();
807 }
808 }
697 } 809 }
698 } 810 }
699 811
700 /** 812 /**
701 * Removes a listener from this stream and cancels its pauses. 813 * Removes a listener from this stream and cancels its pauses.
702 * 814 *
703 * This is a low-level action that doesn't call [_onSubscriptionStateChange]. 815 * This is a low-level action that doesn't call [_onSubscriptionStateChange].
704 * or [_callOnPauseStateChange]. 816 * or [_callOnPauseStateChange].
705 */ 817 */
706 void _removeListener(_StreamListener listener) { 818 void _removeListener(_StreamListener listener) {
707 int pauseCount = listener._setUnsubscribed(); 819 int pauseCount = listener._setUnsubscribed();
708 _updatePauseCount(-pauseCount);
709 _InternalLinkList.remove(listener); 820 _InternalLinkList.remove(listener);
821 if (pauseCount > 0) {
822 _updatePauseCount(-pauseCount);
823 if (!_isPaused && _hasPendingEvent) {
824 _state |= _STREAM_PENDING_RESUME;
825 }
826 }
710 } 827 }
711 } 828 }
712 829
713 830
714 /** Stream that generates its own events. */ 831 /** Stream that generates its own events. */
715 class _GeneratedSingleStreamImpl<T> extends _SingleStreamImpl<T> { 832 class _GeneratedSingleStreamImpl<T> extends _SingleStreamImpl<T> {
716 /** 833 /**
717 * Initializes the stream to have only the events provided by [events]. 834 * Initializes the stream to have only the events provided by [events].
718 * 835 *
719 * A [_PendingEvents] implementation provides events that are handled 836 * A [_PendingEvents] implementation provides events that are handled
(...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after
1024 /// the event. 1141 /// the event.
1025 void _toggleEventReceived() { 1142 void _toggleEventReceived() {
1026 _state ^= _LISTENER_EVENT_ID; 1143 _state ^= _LISTENER_EVENT_ID;
1027 } 1144 }
1028 1145
1029 void _setSubscribed(int eventIdBit) { 1146 void _setSubscribed(int eventIdBit) {
1030 assert(eventIdBit == 0 || eventIdBit == 1); 1147 assert(eventIdBit == 0 || eventIdBit == 1);
1031 _state = _LISTENER_SUBSCRIBED | (eventIdBit << _LISTENER_EVENT_ID_SHIFT); 1148 _state = _LISTENER_SUBSCRIBED | (eventIdBit << _LISTENER_EVENT_ID_SHIFT);
1032 } 1149 }
1033 1150
1034 void _setPendingUnsubscribe() { 1151 void _setPendingUnsubscribe(int currentEventIdBit) {
1035 assert(_isSubscribed); 1152 assert(_isSubscribed);
1036 _state |= _LISTENER_PENDING_UNSUBSCRIBE; 1153 // Sets the pending unsubscribe, and ensures that the listener
1154 // won't get the current event.
1155 _state |= _LISTENER_PENDING_UNSUBSCRIBE | _LISTENER_EVENT_ID;
1156 _state ^= (1 ^ currentEventIdBit) << _LISTENER_EVENT_ID_SHIFT;
1157 assert(!_needsEvent(currentEventIdBit));
1037 } 1158 }
1038 1159
1039 /** 1160 /**
1040 * Marks the listener as unsubscibed. 1161 * Marks the listener as unsubscibed.
1041 * 1162 *
1042 * Returns the number of unresumed pauses for the listener. 1163 * Returns the number of unresumed pauses for the listener.
1043 */ 1164 */
1044 int _setUnsubscribed() { 1165 int _setUnsubscribed() {
1045 assert(_isSubscribed); 1166 assert(_isSubscribed);
1046 int timesPaused = _state >> _LISTENER_PAUSE_COUNT_SHIFT; 1167 int timesPaused = _state >> _LISTENER_PAUSE_COUNT_SHIFT;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
1108 1229
1109 void add(_DelayedEvent event) { 1230 void add(_DelayedEvent event) {
1110 if (lastPendingEvent == null) { 1231 if (lastPendingEvent == null) {
1111 firstPendingEvent = lastPendingEvent = event; 1232 firstPendingEvent = lastPendingEvent = event;
1112 } else { 1233 } else {
1113 lastPendingEvent = lastPendingEvent.next = event; 1234 lastPendingEvent = lastPendingEvent.next = event;
1114 } 1235 }
1115 } 1236 }
1116 1237
1117 void handleNext(_StreamImpl stream) { 1238 void handleNext(_StreamImpl stream) {
1118 if (isScheduled) cancelSchedule(); 1239 assert(!isScheduled);
1119 _DelayedEvent event = firstPendingEvent; 1240 _DelayedEvent event = firstPendingEvent;
1120 firstPendingEvent = event.next; 1241 firstPendingEvent = event.next;
1121 if (firstPendingEvent == null) { 1242 if (firstPendingEvent == null) {
1122 lastPendingEvent = null; 1243 lastPendingEvent = null;
1123 } 1244 }
1124 event.perform(stream); 1245 event.perform(stream);
1125 } 1246 }
1126 } 1247 }
1127 1248
1128 1249
(...skipping 20 matching lines...) Expand all
1149 void onError(void handleError(AsyncError error)) {} 1270 void onError(void handleError(AsyncError error)) {}
1150 1271
1151 void onDone(void handleDone()) { 1272 void onDone(void handleDone()) {
1152 _handler = handleDone; 1273 _handler = handleDone;
1153 } 1274 }
1154 1275
1155 void pause([Future signal]) { 1276 void pause([Future signal]) {
1156 if (_isComplete) { 1277 if (_isComplete) {
1157 throw new StateError("Subscription has been canceled."); 1278 throw new StateError("Subscription has been canceled.");
1158 } 1279 }
1159 if (_timer != null) _timer.cancel(); 1280 if (_timer != null) {
1281 _timer.cancel();
1282 _timer = null;
1283 }
1160 _pauseCount++; 1284 _pauseCount++;
1285 if (signal != null) signal.whenComplete(resume);
1161 } 1286 }
1162 1287
1163 void resume() { 1288 void resume() {
1164 if (_isComplete) { 1289 if (_isComplete) {
1165 throw new StateError("Subscription has been canceled."); 1290 throw new StateError("Subscription has been canceled.");
1166 } 1291 }
1167 if (_pauseCount == 0) return; 1292 if (_pauseCount == 0) return;
1168 _pauseCount--; 1293 _pauseCount--;
1169 if (_pauseCount == 0) { 1294 if (_pauseCount == 0) {
1170 _delayDone(); 1295 _delayDone();
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1214 onError: this._signalError, 1339 onError: this._signalError,
1215 onDone: this._close); 1340 onDone: this._close);
1216 } else { 1341 } else {
1217 // TODO(lrn): Check why this can happen. 1342 // TODO(lrn): Check why this can happen.
1218 if (_subscription == null) return; 1343 if (_subscription == null) return;
1219 _subscription.cancel(); 1344 _subscription.cancel();
1220 _subscription = null; 1345 _subscription = null;
1221 } 1346 }
1222 } 1347 }
1223 } 1348 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698