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

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

Issue 882713009: Make synchronous broadcast StreamController throw if adding event while adding event. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Update version number. Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 part of dart.async; 5 part of dart.async;
6 6
7 // ------------------------------------------------------------------- 7 // -------------------------------------------------------------------
8 // Controller for creating and adding events to a stream. 8 // Controller for creating and adding events to a stream.
9 // ------------------------------------------------------------------- 9 // -------------------------------------------------------------------
10 10
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
46 * the stream at all, and won't trigger callbacks. From the controller's point 46 * the stream at all, and won't trigger callbacks. From the controller's point
47 * of view, the stream is completely inert when has completed. 47 * of view, the stream is completely inert when has completed.
48 */ 48 */
49 abstract class StreamController<T> implements StreamSink<T> { 49 abstract class StreamController<T> implements StreamSink<T> {
50 /** The stream that this controller is controlling. */ 50 /** The stream that this controller is controlling. */
51 Stream<T> get stream; 51 Stream<T> get stream;
52 52
53 /** 53 /**
54 * A controller with a [stream] that supports only one single subscriber. 54 * A controller with a [stream] that supports only one single subscriber.
55 * 55 *
56 * If [sync] is true, events may be passed directly to the stream's listener 56 * If [sync] is true, the returned stream controller is a
57 * during an [add], [addError] or [close] call. If [sync] is false, the event 57 * [SynchronousStreamController], and must be used with the care
58 * will be passed to the listener at a later time, after the code creating 58 * and attention necessary to not break the [Stream] contract.
59 * the event has returned.
60 * 59 *
61 * The controller will buffer all incoming events until the subscriber is 60 * The controller will buffer all incoming events until the subscriber is
62 * registered. 61 * registered.
63 * 62 *
64 * The [onPause] function is called when the stream becomes 63 * The [onPause] function is called when the stream becomes
65 * paused. [onResume] is called when the stream resumed. 64 * paused. [onResume] is called when the stream resumed.
66 * 65 *
67 * The [onListen] callback is called when the stream 66 * The [onListen] callback is called when the stream
68 * receives its listener and [onCancel] when the listener ends 67 * receives its listener and [onCancel] when the listener ends
69 * its subscription. If [onCancel] needs to perform an asynchronous operation, 68 * its subscription. If [onCancel] needs to perform an asynchronous operation,
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
101 * call has returned. The controller does not have any internal queue of 100 * call has returned. The controller does not have any internal queue of
102 * events, and if there are no listeners at the time the event is added, 101 * events, and if there are no listeners at the time the event is added,
103 * it will just be dropped, or, if it is an error, be reported as uncaught. 102 * it will just be dropped, or, if it is an error, be reported as uncaught.
104 * 103 *
105 * Each listener subscription is handled independently, 104 * Each listener subscription is handled independently,
106 * and if one pauses, only the pausing listener is affected. 105 * and if one pauses, only the pausing listener is affected.
107 * A paused listener will buffer events internally until unpaused or canceled. 106 * A paused listener will buffer events internally until unpaused or canceled.
108 * 107 *
109 * If [sync] is true, events may be fired directly by the stream's 108 * If [sync] is true, events may be fired directly by the stream's
110 * subscriptions during an [add], [addError] or [close] call. 109 * subscriptions during an [add], [addError] or [close] call.
111 * If [sync] is false, the event will be fired at a later time, 110 * The returned stream controller is a [SynchronousStreamController],
111 * and must be used with the care and attention necessary to not break
112 * the [Stream] contract.
113 *
114 * If [sync] is false, the event will always be fired at a later time,
112 * after the code adding the event has completed. 115 * after the code adding the event has completed.
113 * 116 * In that case, no guarantees are given with regard to when
114 * When [sync] is false, no guarantees are given with regard to when
115 * multiple listeners get the events, except that each listener will get 117 * multiple listeners get the events, except that each listener will get
116 * all events in the correct order. Each subscription handles the events 118 * all events in the correct order. Each subscription handles the events
117 * individually. 119 * individually.
118 * If two events are sent on an async controller with two listeners, 120 * If two events are sent on an async controller with two listeners,
119 * one of the listeners may get both events 121 * one of the listeners may get both events
120 * before the other listener gets any. 122 * before the other listener gets any.
121 * A listener must be subscribed both when the event is initiated 123 * A listener must be subscribed both when the event is initiated
122 * (that is, when [add] is called) 124 * (that is, when [add] is called)
123 * and when the event is later delivered, 125 * and when the event is later delivered,
124 * in order to receive the event. 126 * in order to receive the event.
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
195 * 197 *
196 * If [cancelOnError] is true, only the first error on [source] is 198 * If [cancelOnError] is true, only the first error on [source] is
197 * forwarded to the controller's stream, and the `addStream` ends 199 * forwarded to the controller's stream, and the `addStream` ends
198 * after this. If [cancelOnError] is false, all errors are forwarded 200 * after this. If [cancelOnError] is false, all errors are forwarded
199 * and only a done event will end the `addStream`. 201 * and only a done event will end the `addStream`.
200 */ 202 */
201 Future addStream(Stream<T> source, {bool cancelOnError: true}); 203 Future addStream(Stream<T> source, {bool cancelOnError: true});
202 } 204 }
203 205
204 206
207 /**
208 * A stream controller that delivers its events synchronously.
209 *
210 * A synchronous stream controller is intended for cases where
211 * an already asynchronous event triggers an event on a stream.
212 *
213 * Instead of adding the event to the stream in a later microtask,
214 * causing extra latency, the event is instead fired immediately by the
215 * synchronous stream controller, as if the stream event was
216 * the current event or microtask.
217 *
218 * The synchronous stream controller can be used to break the contract
219 * on [Stream], and it must be used carefully to avoid doing so.
220 *
221 * The only advantage to using a [SynchronousStreamController] over a
222 * normal [StreamController] is the improved latency.
223 * Only use the synchronous version if the improvement is significant,
224 * and if its use is safe. Otherwise just use a normal stream controller,
225 * which will always have the correct behavior for a [Stream], and won't
226 * accidentally break other code.
227 *
228 * Adding events to a synchronous controller should only happen as the
229 * very last part of a the handling of the original event.
230 * At that point, adding an event to the stream is equivalent to
231 * returning to the event loop and adding the event in the next microtask.
232 *
233 * Each listener callback will be run as if it was a top-level event
234 * or microtask. This means that if it throws, the error will be reported as
235 * uncaught as soon as possible.
236 * This is one reason to add the event as the last thing in the original event
237 * handler - any action done after adding the event will delay the report of
238 * errors in the event listener callbacks.
239 *
240 * If an event is added in a setting that isn't known to be another event,
241 * it may cause the stream's listener to get that event before the listener
242 * is ready to handle it. We promise that after calling [Stream.listen],
243 * you won't get any events until the code doing the listen has completed.
244 * Calling [add] in response to a function call of unknown origin may break
245 * that promise.
246 *
247 * An [onListen] callback from the controller is *not* an asynchronous event,
248 * and adding events to the controller in the `onListen` callback is always
249 * wrong. The events will be delivered before the listener has even received
250 * the subscription yet.
251 *
252 * The synchronous broadcast stream controller also has a restrictions that a
253 * normal stream controller does not:
254 * The [add], [addError], [close] and [addStream] methods *must not* be
255 * called while an event is being delivered.
256 * That is, if a callback on a subscription on the controller's stream causes
257 * a call to any of the functions above, the call will fail.
258 * A broadcast stream may have more than one listener, and if an
259 * event is added synchronously while another is being also in the process
260 * of being added, the latter event might reach some listeners before
261 * the former. To prevent that, an event cannot be added while a previous
262 * event is being fired.
263 * This guarantees that an event is fully delivered when the
264 * first [add], [addError] or [close] returns,
265 * and further events will be delivered in the correct order.
266 *
267 * This still only guarantees that the event is delivered to the subscription.
268 * If the subscription is paused, the actual callback may still happen later,
269 * and the event will instead be buffered by the subscription.
270 * Barring pausing, and the following buffered events that haven't been
271 * delivered yet, callbacks will be called synchronously when an event is added.
272 *
273 * Adding an event to a synchronous non-broadcast stream controller while
274 * another event is in progress may cause the second event to be delayed
275 * and not be delivered synchronously, and until that event is delivered,
276 * the controller will not act synchronously.
277 */
278 abstract class SynchronousStreamController<T> implements StreamController<T> {
279 /**
280 * Adds event to the controller's stream.
281 *
282 * As [StreamController.add], but must not be called while an event is
283 * being added by [add], [addError] or [close].
284 */
285 void add(T data);
286
287 /**
288 * Adds error to the controller's stream.
289 *
290 * As [StreamController.addError], but must not be called while an event is
291 * being added by [add], [addError] or [close].
292 */
293 void addError(Object error, StackTrace stackTrace);
294
295 /**
296 * Closes the controller's stream.
297 *
298 * As [StreamController.close], but must not be called while an event is
299 * being added by [add], [addError] or [close].
300 */
301 Future close();
302 }
303
205 abstract class _StreamControllerLifecycle<T> { 304 abstract class _StreamControllerLifecycle<T> {
206 StreamSubscription<T> _subscribe( 305 StreamSubscription<T> _subscribe(
207 void onData(T data), 306 void onData(T data),
208 Function onError, 307 Function onError,
209 void onDone(), 308 void onDone(),
210 bool cancelOnError); 309 bool cancelOnError);
211 void _recordPause(StreamSubscription<T> subscription) {} 310 void _recordPause(StreamSubscription<T> subscription) {}
212 void _recordResume(StreamSubscription<T> subscription) {} 311 void _recordResume(StreamSubscription<T> subscription) {}
213 Future _recordCancel(StreamSubscription<T> subscription) => null; 312 Future _recordCancel(StreamSubscription<T> subscription) => null;
214 } 313 }
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 */ 513 */
415 void add(T value) { 514 void add(T value) {
416 if (!_mayAddEvent) throw _badEventState(); 515 if (!_mayAddEvent) throw _badEventState();
417 _add(value); 516 _add(value);
418 } 517 }
419 518
420 /** 519 /**
421 * Send or enqueue an error event. 520 * Send or enqueue an error event.
422 */ 521 */
423 void addError(Object error, [StackTrace stackTrace]) { 522 void addError(Object error, [StackTrace stackTrace]) {
523 if (!_mayAddEvent) throw _badEventState();
424 error = _nonNullError(error); 524 error = _nonNullError(error);
425 if (!_mayAddEvent) throw _badEventState();
426 AsyncError replacement = Zone.current.errorCallback(error, stackTrace); 525 AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
427 if (replacement != null) { 526 if (replacement != null) {
428 error = _nonNullError(replacement.error); 527 error = _nonNullError(replacement.error);
429 stackTrace = replacement.stackTrace; 528 stackTrace = replacement.stackTrace;
430 } 529 }
431 _addError(error, stackTrace); 530 _addError(error, stackTrace);
432 } 531 }
433 532
434 /** 533 /**
435 * Closes this controller and sends a done event on the stream. 534 * Closes this controller and sends a done event on the stream.
436 * 535 *
437 * The first time a controller is closed, a "done" event is added to its 536 * The first time a controller is closed, a "done" event is added to its
438 * stream. 537 * stream.
439 * 538 *
440 * You are allowed to close the controller more than once, but only the first 539 * You are allowed to close the controller more than once, but only the first
441 * call has any effect. 540 * call has any effect.
442 * 541 *
443 * After closing, no further events may be added using [add] or [addError]. 542 * After closing, no further events may be added using [add], [addError]
543 * or [addStream].
444 * 544 *
445 * The returned future is completed when the done event has been delivered. 545 * The returned future is completed when the done event has been delivered.
446 */ 546 */
447 Future close() { 547 Future close() {
448 if (isClosed) { 548 if (isClosed) {
449 return _ensureDoneFuture(); 549 return _ensureDoneFuture();
450 } 550 }
451 if (!_mayAddEvent) throw _badEventState(); 551 if (!_mayAddEvent) throw _badEventState();
452 _closeUnchecked(); 552 _closeUnchecked();
453 return _ensureDoneFuture(); 553 return _ensureDoneFuture();
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
583 void _recordResume(StreamSubscription<T> subscription) { 683 void _recordResume(StreamSubscription<T> subscription) {
584 if (_isAddingStream) { 684 if (_isAddingStream) {
585 _StreamControllerAddStreamState addState = _varData; 685 _StreamControllerAddStreamState addState = _varData;
586 addState.resume(); 686 addState.resume();
587 } 687 }
588 _runGuarded(_onResume); 688 _runGuarded(_onResume);
589 } 689 }
590 } 690 }
591 691
592 abstract class _SyncStreamControllerDispatch<T> 692 abstract class _SyncStreamControllerDispatch<T>
593 implements _StreamController<T> { 693 implements _StreamController<T>, SynchronousStreamController<T> {
694 int get _state;
695 void set _state(int state);
696
594 void _sendData(T data) { 697 void _sendData(T data) {
595 _subscription._add(data); 698 _subscription._add(data);
596 } 699 }
597 700
598 void _sendError(Object error, StackTrace stackTrace) { 701 void _sendError(Object error, StackTrace stackTrace) {
599 _subscription._addError(error, stackTrace); 702 _subscription._addError(error, stackTrace);
600 } 703 }
601 704
602 void _sendDone() { 705 void _sendDone() {
603 _subscription._close(); 706 _subscription._close();
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
799 _StreamControllerAddStreamState(_StreamController controller, 902 _StreamControllerAddStreamState(_StreamController controller,
800 this.varData, 903 this.varData,
801 Stream source, 904 Stream source,
802 bool cancelOnError) 905 bool cancelOnError)
803 : super(controller, source, cancelOnError) { 906 : super(controller, source, cancelOnError) {
804 if (controller.isPaused) { 907 if (controller.isPaused) {
805 addSubscription.pause(); 908 addSubscription.pause();
806 } 909 }
807 } 910 }
808 } 911 }
OLDNEW
« no previous file with comments | « sdk/lib/async/broadcast_stream_controller.dart ('k') | tests/lib/async/stream_controller_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698