Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.async; | 5 part of dart.async; |
| 6 | 6 |
| 7 // 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |