| 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 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 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 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 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 = _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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |