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

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: Address review comments. 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
« no previous file with comments | « sdk/lib/async/stream_controller.dart ('k') | tests/lib/async/stream_state_test.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 // 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 predicates.
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;
141 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 _isInputPaused => _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 wasInputPaused) {
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, wasInputPaused);
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 wasInputPaused = _isInputPaused;
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 && !wasInputPaused) {
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 hasn't changed.
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 = _isInputPaused;
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 * Schedules events if currently not paused and inside a callback.
462 */
372 void _addPendingEvent(_DelayedEvent event) { 463 void _addPendingEvent(_DelayedEvent event) {
373 if (_pendingEvents == null) _pendingEvents = new _StreamImplEvents(); 464 if (_pendingEvents == null) _pendingEvents = new _StreamImplEvents();
374 _StreamImplEvents events = _pendingEvents; 465 _StreamImplEvents events = _pendingEvents;
375 events.add(event); 466 events.add(event);
376 } 467 if (_isPaused || _isFiring) return;
377 468 if (_isInCallback) {
378 /** Fire any pending events until the pending event queue. */ 469 _schedulePendingEvents();
379 void _handlePendingEvents() { 470 return;
380 _PendingEvents events = _pendingEvents;
381 if (events == null) return;
382 while (!events.isEmpty && !_isPaused) {
383 events.handleNext(this);
384 } 471 }
385 } 472 }
386 473
474 /** Fire any pending events until the pending event queue is empty. */
475 void _handlePendingEvents() {
476 assert(_isInactive);
477 if (!_hasPendingEvent) return;
478 _PendingEvents events = _pendingEvents;
479 do {
480 if (_isPaused) return;
481 if (events.isScheduled) events.cancelSchedule();
482 events.handleNext(this);
483 } while (!events.isEmpty);
484 }
485
387 /** 486 /**
388 * Send a data event directly to each subscriber. 487 * Send a data event directly to each subscriber.
389 */ 488 */
390 _sendData(T value) { 489 _sendData(T value) {
391 assert(!_isPaused); 490 assert(!_isPaused);
392 assert(!_isComplete); 491 assert(!_isComplete);
492 if (!_hasSubscribers) return;
393 _forEachSubscriber((subscriber) { 493 _forEachSubscriber((subscriber) {
394 try { 494 try {
395 subscriber._sendData(value); 495 subscriber._sendData(value);
396 } on AsyncError catch (e) { 496 } on AsyncError catch (e) {
397 e.throwDelayed(); 497 e.throwDelayed();
398 } catch (e, s) { 498 } catch (e, s) {
399 new AsyncError(e, s).throwDelayed(); 499 new AsyncError(e, s).throwDelayed();
400 } 500 }
401 }); 501 });
402 } 502 }
403 503
404 /** 504 /**
405 * Sends an error event directly to each subscriber. 505 * Sends an error event directly to each subscriber.
406 */ 506 */
407 void _sendError(AsyncError error) { 507 void _sendError(AsyncError error) {
408 assert(!_isPaused); 508 assert(!_isPaused);
409 assert(!_isComplete); 509 assert(!_isComplete);
510 if (!_hasSubscribers) return;
410 _forEachSubscriber((subscriber) { 511 _forEachSubscriber((subscriber) {
411 try { 512 try {
412 subscriber._sendError(error); 513 subscriber._sendError(error);
413 } on AsyncError catch (e) { 514 } on AsyncError catch (e) {
414 e.throwDelayed(); 515 e.throwDelayed();
415 } catch (e, s) { 516 } catch (e, s) {
416 new AsyncError.withCause(e, s, error).throwDelayed(); 517 new AsyncError.withCause(e, s, error).throwDelayed();
417 } 518 }
418 }); 519 });
419 } 520 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
460 * some methods made public. 561 * some methods made public.
461 * 562 *
462 * The user interface of [_SingleStreamImpl] are the following methods: 563 * The user interface of [_SingleStreamImpl] are the following methods:
463 * * [_add]: Add a data event to the stream. 564 * * [_add]: Add a data event to the stream.
464 * * [_signalError]: Add an error event to the stream. 565 * * [_signalError]: Add an error event to the stream.
465 * * [_close]: Request to close the stream. 566 * * [_close]: Request to close the stream.
466 * * [_onSubscriberStateChange]: Called when receiving the first subscriber or 567 * * [_onSubscriberStateChange]: Called when receiving the first subscriber or
467 * when losing the last subscriber. 568 * when losing the last subscriber.
468 * * [_onPauseStateChange]: Called when entering or leaving paused mode. 569 * * [_onPauseStateChange]: Called when entering or leaving paused mode.
469 * * [_hasSubscribers]: Test whether there are currently any subscribers. 570 * * [_hasSubscribers]: Test whether there are currently any subscribers.
470 * * [_isPaused]: Test whether the stream is currently paused. 571 * * [_isInputPaused]: Test whether the stream is currently paused.
471 * The user should not add new events while the stream is paused, but if it 572 * 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 573 * happens anyway, the stream will enqueue the events just as when new events
473 * arrive while still firing an old event. 574 * arrive while still firing an old event.
474 */ 575 */
475 class _SingleStreamImpl<T> extends _StreamImpl<T> { 576 class _SingleStreamImpl<T> extends _StreamImpl<T> {
476 _StreamListener _subscriber = null; 577 _StreamListener _subscriber = null;
477 578
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]. */ 579 /** Whether there is currently a subscriber on this [Stream]. */
493 bool get _hasSubscribers => _subscriber != null; 580 bool get _hasSubscribers => _subscriber != null;
494 581
495 // ------------------------------------------------------------------- 582 // -------------------------------------------------------------------
496 // Internal implementation. 583 // Internal implementation.
497 584
585 _SingleStreamImpl() {
586 // Start out paused.
587 _updatePauseCount(1);
588 }
589
498 /** 590 /**
499 * Create the new subscription object. 591 * Create the new subscription object.
500 */ 592 */
501 _StreamSubscriptionImpl<T> _createSubscription( 593 _StreamSubscriptionImpl<T> _createSubscription(
502 void onData(T data), 594 void onData(T data),
503 void onError(AsyncError error), 595 void onError(AsyncError error),
504 void onDone(), 596 void onDone(),
505 bool unsubscribeOnError) { 597 bool unsubscribeOnError) {
506 return new _StreamSubscriptionImpl<T>( 598 return new _StreamSubscriptionImpl<T>(
507 this, onData, onError, onDone, unsubscribeOnError); 599 this, onData, onError, onDone, unsubscribeOnError);
508 } 600 }
509 601
510 void _addListener(_StreamListener subscription) { 602 void _addListener(_StreamListener subscription) {
603 assert(!_isComplete);
511 if (_hasSubscribers) { 604 if (_hasSubscribers) {
512 throw new StateError("Stream already has subscriber."); 605 throw new StateError("Stream already has subscriber.");
513 } 606 }
607 assert(_pauseCount == 1);
608 _updatePauseCount(-1);
514 _subscriber = subscription; 609 _subscriber = subscription;
515 subscription._setSubscribed(0); 610 subscription._setSubscribed(0);
516 _callOnSubscriptionStateChange(); 611 if (_isInactive) {
517 if (_hasPendingEvent) { 612 _checkCallbacks(false, true);
518 _schedulePendingEvents(); 613 if (!_isPaused && _hasPendingEvent) {
614 _schedulePendingEvents();
615 }
519 } 616 }
520 } 617 }
521 618
522 /** 619 /**
523 * Handle a cancel requested from a [_StreamSubscriptionImpl]. 620 * Handle a cancel requested from a [_StreamSubscriptionImpl].
524 * 621 *
525 * This method is called from [_StreamSubscriptionImpl.cancel]. 622 * 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 */ 623 */
530 void _cancel(_StreamListener subscriber) { 624 void _cancel(_StreamListener subscriber) {
531 assert(identical(subscriber._source, this)); 625 assert(identical(subscriber._source, this));
532 // We allow unsubscribing the currently firing subscription during 626 // We allow unsubscribing the currently firing subscription during
533 // the event firing, because it is indistinguishable from delaying it since 627 // the event firing, because it is indistinguishable from delaying it since
534 // that event has already received the event. 628 // that event has already received the event.
535 if (!identical(_subscriber, subscriber)) { 629 if (!identical(_subscriber, subscriber)) {
536 // You may unsubscribe more than once, only the first one counts. 630 // You may unsubscribe more than once, only the first one counts.
537 return; 631 return;
538 } 632 }
539 _subscriber = null; 633 _subscriber = null;
540 // Unsubscribing a paused subscription also cancels its pauses. 634 // Unsubscribing a paused subscription also cancels its pauses.
541 int subscriptionPauseCount = subscriber._setUnsubscribed(); 635 int resumeCount = subscriber._setUnsubscribed();
542 _updatePauseCount(-subscriptionPauseCount); 636 // Keep being paused while there is no subscriber and the stream is not
543 if (!_isFiring) { 637 // complete.
544 _callOnSubscriptionStateChange(); 638 _updatePauseCount(_isComplete ? -resumeCount : -resumeCount + 1);
639 if (_isInactive) {
640 _checkCallbacks(true, resumeCount > 0);
641 if (!_isPaused && _hasPendingEvent) {
642 _schedulePendingEvents();
643 }
545 } 644 }
546 } 645 }
547 646
548 void _forEachSubscriber( 647 void _forEachSubscriber(
549 void action(_StreamListener<T> subscription)) { 648 void action(_StreamListener<T> subscription)) {
550 assert(!_isPaused); 649 assert(!_isPaused);
650 bool wasInputPaused = _isInputPaused;
551 _StreamListener subscription = _subscriber; 651 _StreamListener subscription = _subscriber;
552 assert(subscription != null); 652 assert(subscription != null);
553 _startFiring(); 653 _startFiring();
554 action(subscription); 654 action(subscription);
555 _endFiring(); 655 _endFiring(wasInputPaused);
556 } 656 }
557 } 657 }
558 658
559 // ------------------------------------------------------------------- 659 // -------------------------------------------------------------------
560 // Default implementation of a stream with subscribers. 660 // Default implementation of a stream with subscribers.
561 // ------------------------------------------------------------------- 661 // -------------------------------------------------------------------
562 662
563 /** 663 /**
564 * Default implementation of stream capable of sending events to subscribers. 664 * Default implementation of stream capable of sending events to subscribers.
565 * 665 *
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
631 * 731 *
632 * The [action] must not throw, or the controller will be left in an 732 * The [action] must not throw, or the controller will be left in an
633 * invalid state. 733 * invalid state.
634 * 734 *
635 * This method must not be called while [isFiring] is true. 735 * This method must not be called while [isFiring] is true.
636 */ 736 */
637 void _forEachSubscriber( 737 void _forEachSubscriber(
638 void action(_StreamListener<T> subscription)) { 738 void action(_StreamListener<T> subscription)) {
639 assert(!_isFiring); 739 assert(!_isFiring);
640 if (!_hasSubscribers) return; 740 if (!_hasSubscribers) return;
741 bool wasInputPaused = _isInputPaused;
641 _startFiring(); 742 _startFiring();
642 _InternalLink cursor = this._nextLink; 743 _InternalLink cursor = this._nextLink;
643 while (!identical(cursor, this)) { 744 while (!identical(cursor, this)) {
644 _StreamListener<T> current = cursor; 745 _StreamListener<T> current = cursor;
645 if (current._needsEvent(_currentEventIdBit)) { 746 if (current._needsEvent(_currentEventIdBit)) {
646 action(current); 747 action(current);
647 // Marks as having received the event. 748 // Marks as having received the event.
648 current._toggleEventReceived(); 749 current._toggleEventReceived();
649 } 750 }
650 cursor = current._nextLink; 751 cursor = current._nextLink;
651 if (current._isPendingUnsubscribe) { 752 if (current._isPendingUnsubscribe) {
652 _removeListener(current); 753 _removeListener(current);
653 } 754 }
654 } 755 }
655 _endFiring(); 756 _endFiring(wasInputPaused);
656 } 757 }
657 758
658 void _addListener(_StreamListener listener) { 759 void _addListener(_StreamListener listener) {
659 listener._setSubscribed(_currentEventIdBit); 760 listener._setSubscribed(_currentEventIdBit);
660 bool firstSubscriber = !_hasSubscribers; 761 bool hadSubscribers = _hasSubscribers;
661 _InternalLinkList.add(this, listener); 762 _InternalLinkList.add(this, listener);
662 if (firstSubscriber) { 763 if (!hadSubscribers && _isInactive) {
663 _callOnSubscriptionStateChange(); 764 _checkCallbacks(false, false);
765 if (!_isPaused && _hasPendingEvent) {
766 _schedulePendingEvents();
767 }
664 } 768 }
665 } 769 }
666 770
667 /** 771 /**
668 * Handle a cancel requested from a [_StreamListener]. 772 * Handle a cancel requested from a [_StreamListener].
669 * 773 *
670 * This method is called from [_StreamListener.cancel]. 774 * This method is called from [_StreamListener.cancel].
671 * 775 *
672 * If an event is currently firing, the cancel is delayed 776 * If an event is currently firing, the cancel is delayed
673 * until after the subscribers have received the event. 777 * until after the subscribers have received the event.
674 */ 778 */
675 void _cancel(_StreamListener listener) { 779 void _cancel(_StreamListener listener) {
676 assert(identical(listener._source, this)); 780 assert(identical(listener._source, this));
677 if (_InternalLink.isUnlinked(listener)) { 781 if (_InternalLink.isUnlinked(listener)) {
678 // You may unsubscribe more than once, only the first one counts. 782 // You may unsubscribe more than once, only the first one counts.
679 return; 783 return;
680 } 784 }
681 if (_isFiring) { 785 if (_isFiring) {
682 if (listener._needsEvent(_currentEventIdBit)) { 786 if (listener._needsEvent(_currentEventIdBit)) {
683 assert(listener._isSubscribed); 787 assert(listener._isSubscribed);
684 listener._setPendingUnsubscribe(); 788 listener._setPendingUnsubscribe(_currentEventIdBit);
685 } else { 789 } else {
686 // The listener has been notified of the event (or don't need to, 790 // 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. 791 // if it's still pending subscription) so it's safe to remove it.
688 _removeListener(listener); 792 _removeListener(listener);
689 } 793 }
690 // Pause and subscription state changes are reported when we end 794 // Pause and subscription state changes are reported when we end
691 // firing. 795 // firing.
692 } else { 796 } else {
693 bool wasPaused = _isPaused; 797 bool wasInputPaused = _isInputPaused;
694 _removeListener(listener); 798 _removeListener(listener);
695 if (wasPaused != _isPaused) _onPauseStateChange(); 799 if (_isInactive) {
696 if (!_hasSubscribers) _callOnSubscriptionStateChange(); 800 _checkCallbacks(true, wasInputPaused);
801 if (!_isPaused && _hasPendingEvent) {
802 _schedulePendingEvents();
803 }
804 }
697 } 805 }
698 } 806 }
699 807
700 /** 808 /**
701 * Removes a listener from this stream and cancels its pauses. 809 * Removes a listener from this stream and cancels its pauses.
702 * 810 *
703 * This is a low-level action that doesn't call [_onSubscriptionStateChange]. 811 * This is a low-level action that doesn't call [_onSubscriptionStateChange].
704 * or [_callOnPauseStateChange]. 812 * or [_callOnPauseStateChange].
705 */ 813 */
706 void _removeListener(_StreamListener listener) { 814 void _removeListener(_StreamListener listener) {
707 int pauseCount = listener._setUnsubscribed(); 815 int pauseCount = listener._setUnsubscribed();
708 _updatePauseCount(-pauseCount);
709 _InternalLinkList.remove(listener); 816 _InternalLinkList.remove(listener);
817 if (pauseCount > 0) {
818 _updatePauseCount(-pauseCount);
819 if (!_isPaused && _hasPendingEvent) {
820 _state |= _STREAM_PENDING_RESUME;
821 }
822 }
710 } 823 }
711 } 824 }
712 825
713 826
714 /** Stream that generates its own events. */ 827 /** Stream that generates its own events. */
715 class _GeneratedSingleStreamImpl<T> extends _SingleStreamImpl<T> { 828 class _GeneratedSingleStreamImpl<T> extends _SingleStreamImpl<T> {
716 /** 829 /**
717 * Initializes the stream to have only the events provided by [events]. 830 * Initializes the stream to have only the events provided by [events].
718 * 831 *
719 * A [_PendingEvents] implementation provides events that are handled 832 * A [_PendingEvents] implementation provides events that are handled
(...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after
1024 /// the event. 1137 /// the event.
1025 void _toggleEventReceived() { 1138 void _toggleEventReceived() {
1026 _state ^= _LISTENER_EVENT_ID; 1139 _state ^= _LISTENER_EVENT_ID;
1027 } 1140 }
1028 1141
1029 void _setSubscribed(int eventIdBit) { 1142 void _setSubscribed(int eventIdBit) {
1030 assert(eventIdBit == 0 || eventIdBit == 1); 1143 assert(eventIdBit == 0 || eventIdBit == 1);
1031 _state = _LISTENER_SUBSCRIBED | (eventIdBit << _LISTENER_EVENT_ID_SHIFT); 1144 _state = _LISTENER_SUBSCRIBED | (eventIdBit << _LISTENER_EVENT_ID_SHIFT);
1032 } 1145 }
1033 1146
1034 void _setPendingUnsubscribe() { 1147 void _setPendingUnsubscribe(int currentEventIdBit) {
1035 assert(_isSubscribed); 1148 assert(_isSubscribed);
1036 _state |= _LISTENER_PENDING_UNSUBSCRIBE; 1149 // Sets the pending unsubscribe, and ensures that the listener
1150 // won't get the current event.
1151 _state |= _LISTENER_PENDING_UNSUBSCRIBE | _LISTENER_EVENT_ID;
1152 _state ^= (1 ^ currentEventIdBit) << _LISTENER_EVENT_ID_SHIFT;
1153 assert(!_needsEvent(currentEventIdBit));
1037 } 1154 }
1038 1155
1039 /** 1156 /**
1040 * Marks the listener as unsubscibed. 1157 * Marks the listener as unsubscibed.
1041 * 1158 *
1042 * Returns the number of unresumed pauses for the listener. 1159 * Returns the number of unresumed pauses for the listener.
1043 */ 1160 */
1044 int _setUnsubscribed() { 1161 int _setUnsubscribed() {
1045 assert(_isSubscribed); 1162 assert(_isSubscribed);
1046 int timesPaused = _state >> _LISTENER_PAUSE_COUNT_SHIFT; 1163 int timesPaused = _state >> _LISTENER_PAUSE_COUNT_SHIFT;
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
1108 1225
1109 void add(_DelayedEvent event) { 1226 void add(_DelayedEvent event) {
1110 if (lastPendingEvent == null) { 1227 if (lastPendingEvent == null) {
1111 firstPendingEvent = lastPendingEvent = event; 1228 firstPendingEvent = lastPendingEvent = event;
1112 } else { 1229 } else {
1113 lastPendingEvent = lastPendingEvent.next = event; 1230 lastPendingEvent = lastPendingEvent.next = event;
1114 } 1231 }
1115 } 1232 }
1116 1233
1117 void handleNext(_StreamImpl stream) { 1234 void handleNext(_StreamImpl stream) {
1118 if (isScheduled) cancelSchedule(); 1235 assert(!isScheduled);
1119 _DelayedEvent event = firstPendingEvent; 1236 _DelayedEvent event = firstPendingEvent;
1120 firstPendingEvent = event.next; 1237 firstPendingEvent = event.next;
1121 if (firstPendingEvent == null) { 1238 if (firstPendingEvent == null) {
1122 lastPendingEvent = null; 1239 lastPendingEvent = null;
1123 } 1240 }
1124 event.perform(stream); 1241 event.perform(stream);
1125 } 1242 }
1126 } 1243 }
1127 1244
1128 1245
(...skipping 20 matching lines...) Expand all
1149 void onError(void handleError(AsyncError error)) {} 1266 void onError(void handleError(AsyncError error)) {}
1150 1267
1151 void onDone(void handleDone()) { 1268 void onDone(void handleDone()) {
1152 _handler = handleDone; 1269 _handler = handleDone;
1153 } 1270 }
1154 1271
1155 void pause([Future signal]) { 1272 void pause([Future signal]) {
1156 if (_isComplete) { 1273 if (_isComplete) {
1157 throw new StateError("Subscription has been canceled."); 1274 throw new StateError("Subscription has been canceled.");
1158 } 1275 }
1159 if (_timer != null) _timer.cancel(); 1276 if (_timer != null) {
1277 _timer.cancel();
1278 _timer = null;
1279 }
1160 _pauseCount++; 1280 _pauseCount++;
1281 if (signal != null) signal.whenComplete(resume);
1161 } 1282 }
1162 1283
1163 void resume() { 1284 void resume() {
1164 if (_isComplete) { 1285 if (_isComplete) {
1165 throw new StateError("Subscription has been canceled."); 1286 throw new StateError("Subscription has been canceled.");
1166 } 1287 }
1167 if (_pauseCount == 0) return; 1288 if (_pauseCount == 0) return;
1168 _pauseCount--; 1289 _pauseCount--;
1169 if (_pauseCount == 0) { 1290 if (_pauseCount == 0) {
1170 _delayDone(); 1291 _delayDone();
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1214 onError: this._signalError, 1335 onError: this._signalError,
1215 onDone: this._close); 1336 onDone: this._close);
1216 } else { 1337 } else {
1217 // TODO(lrn): Check why this can happen. 1338 // TODO(lrn): Check why this can happen.
1218 if (_subscription == null) return; 1339 if (_subscription == null) return;
1219 _subscription.cancel(); 1340 _subscription.cancel();
1220 _subscription = null; 1341 _subscription = null;
1221 } 1342 }
1222 } 1343 }
1223 } 1344 }
OLDNEW
« no previous file with comments | « sdk/lib/async/stream_controller.dart ('k') | tests/lib/async/stream_state_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698