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

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: 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 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 * 192 *
191 * If [cancelOnError] is true, only the first error on [source] is 193 * If [cancelOnError] is true, only the first error on [source] is
192 * forwarded to the controller's stream, and the `addStream` ends 194 * forwarded to the controller's stream, and the `addStream` ends
193 * after this. If [cancelOnError] is false, all errors are forwarded 195 * after this. If [cancelOnError] is false, all errors are forwarded
194 * and only a done event will end the `addStream`. 196 * and only a done event will end the `addStream`.
195 */ 197 */
196 Future addStream(Stream<T> source, {bool cancelOnError: true}); 198 Future addStream(Stream<T> source, {bool cancelOnError: true});
197 } 199 }
198 200
199 201
202 /**
203 * A stream controller that delivers its events synchronously.
204 *
205 * A synchronous stream controller is intended for cases where
206 * an already asynchronous event triggers an event on a stream.
207 *
208 * Instead of adding the event to the stream in a later microtask,
209 * causing extra latency, the event is instead fired immediately by the
210 * synchronous stream controller, as if the stream event was
211 * the current event or microtask.
212 *
213 * The synchronous stream controller can be used to break the contract
214 * on [Stream], and it must be used carefully to avoid doing so.
215 *
216 * The only advantage to using a [SynchronousStreamController] over a
217 * normal [StreamController] is the improved latency.
218 * Only use the synchronous version if the improvement is significant,
219 * and if its use is safe. Otherwise just use a normal stream controller,
220 * which will always have the correct behavior for a [Stream], and won't
221 * accidentally break other code.
222 *
223 * Adding events to a synchronous controller should only happen as the
224 * very last part of a the handling of the original event.
225 * At that point, adding an event to the stream is equivalent to
226 * returning to the event loop and adding the event in the next microtask.
227 *
228 * Each listener callback will be run as if it was a top-level event
229 * or microtask. This means that if it throws, the error will be reported as
230 * uncaught as soon as possible.
231 * This is one reason to add the event as the last thing in the original event
232 * handler - any action done after adding the event will delay the report of
233 * errors in the event listener callbacks.
234 *
235 * If an event is added in a setting that isn't known to be another event,
236 * it may cause the stream's listener to get that event before the listener
237 * is ready to handle it. We promise that after calling [Stream.listen],
238 * you won't get any events until the code doing the listen has completed.
239 * Calling [add] in response to a function call of unknown origin may break
240 * that promise.
241 *
242 * An [onListen] callback from the controller is *not* an asynchronous event,
243 * and adding events to the controller in the `onListen` callback is always
244 * wrong. The events will be delivered before the listener has even received
245 * the subscription yet.
246 *
247 * The synchronous broadcast stream controller also has a restrictions that a
248 * normal stream controller does not:
249 * The [add], [addError], [close] and [addStream] methods *must not* be
250 * called while an event is being delivered.
251 * That is, if a callback on a subscription on the controller's stream causes
252 * a call to any of the functions above, the call will fail.
253 * A broadcast stream may have more than one listener, and if an
254 * event is added synchronously while another is being also in the process
255 * of being added, the latter event might reach some listeners before
256 * the former. To prevent that, an event cannot be added while a previous
257 * event is being fired.
258 * This guarantees that an event is fully delivered when the
259 * first [add], [addError] or [close] returns,
260 * and further events will be delivered in the correct order.
261 *
262 * This still only guarantees that the event is delivered to the subscription.
263 * If the subscription is paused, the actual callback may still happen later,
264 * and the event will instead be buffered by the subscription.
265 * Barring pausing, and the following buffered events that haven't been
266 * delivered yet, callbacks will be called synchronously when an event is added.
267 *
268 * Adding an event to a synchronous non-broadcast stream controller while
269 * another event is in progress may cause the second event to be delayed
270 * and not be delivered synchronously, and until that event is delivered,
271 * the controller will not act synchronously.
272 */
273 abstract class SynchronousStreamController<T> implements StreamController<T> {
274 /**
275 * Adds event to the controller's stream.
276 *
277 * As [StreamController.add], but must not be called while an event is
278 * being added by [add], [addError] or [close].
279 */
280 void add(T data);
281
282 /**
283 * Adds error to the controller's stream.
284 *
285 * As [StreamController.addError], but must not be called while an event is
286 * being added by [add], [addError] or [close].
287 */
288 void addError(Object error, StackTrace stackTrace);
289
290 /**
291 * Closes the controller's stream.
292 *
293 * As [StreamController.close], but must not be called while an event is
294 * being added by [add], [addError] or [close].
295 */
296 Future close();
297 }
298
200 abstract class _StreamControllerLifecycle<T> { 299 abstract class _StreamControllerLifecycle<T> {
201 StreamSubscription<T> _subscribe( 300 StreamSubscription<T> _subscribe(
202 void onData(T data), 301 void onData(T data),
203 Function onError, 302 Function onError,
204 void onDone(), 303 void onDone(),
205 bool cancelOnError); 304 bool cancelOnError);
206 void _recordPause(StreamSubscription<T> subscription) {} 305 void _recordPause(StreamSubscription<T> subscription) {}
207 void _recordResume(StreamSubscription<T> subscription) {} 306 void _recordResume(StreamSubscription<T> subscription) {}
208 Future _recordCancel(StreamSubscription<T> subscription) => null; 307 Future _recordCancel(StreamSubscription<T> subscription) => null;
209 } 308 }
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 */ 508 */
410 void add(T value) { 509 void add(T value) {
411 if (!_mayAddEvent) throw _badEventState(); 510 if (!_mayAddEvent) throw _badEventState();
412 _add(value); 511 _add(value);
413 } 512 }
414 513
415 /** 514 /**
416 * Send or enqueue an error event. 515 * Send or enqueue an error event.
417 */ 516 */
418 void addError(Object error, [StackTrace stackTrace]) { 517 void addError(Object error, [StackTrace stackTrace]) {
518 if (!_mayAddEvent) throw _badEventState();
419 error = _nonNullError(error); 519 error = _nonNullError(error);
420 if (!_mayAddEvent) throw _badEventState();
421 AsyncError replacement = Zone.current.errorCallback(error, stackTrace); 520 AsyncError replacement = Zone.current.errorCallback(error, stackTrace);
422 if (replacement != null) { 521 if (replacement != null) {
423 error = _nonNullError(replacement.error); 522 error = _nonNullError(replacement.error);
424 stackTrace = replacement.stackTrace; 523 stackTrace = replacement.stackTrace;
425 } 524 }
426 _addError(error, stackTrace); 525 _addError(error, stackTrace);
427 } 526 }
428 527
429 /** 528 /**
430 * Closes this controller and sends a done event on the stream. 529 * Closes this controller and sends a done event on the stream.
431 * 530 *
432 * The first time a controller is closed, a "done" event is added to its 531 * The first time a controller is closed, a "done" event is added to its
433 * stream. 532 * stream.
434 * 533 *
435 * You are allowed to close the controller more than once, but only the first 534 * You are allowed to close the controller more than once, but only the first
436 * call has any effect. 535 * call has any effect.
437 * 536 *
438 * After closing, no further events may be added using [add] or [addError]. 537 * After closing, no further events may be added using [add], [addError]
538 * or [addStream].
439 * 539 *
440 * The returned future is completed when the done event has been delivered. 540 * The returned future is completed when the done event has been delivered.
441 */ 541 */
442 Future close() { 542 Future close() {
443 if (isClosed) { 543 if (isClosed) {
444 return _ensureDoneFuture(); 544 return _ensureDoneFuture();
445 } 545 }
446 if (!_mayAddEvent) throw _badEventState(); 546 if (!_mayAddEvent) throw _badEventState();
447 _closeUnchecked(); 547 _closeUnchecked();
448 return _ensureDoneFuture(); 548 return _ensureDoneFuture();
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 void _recordResume(StreamSubscription<T> subscription) { 678 void _recordResume(StreamSubscription<T> subscription) {
579 if (_isAddingStream) { 679 if (_isAddingStream) {
580 _StreamControllerAddStreamState addState = _varData; 680 _StreamControllerAddStreamState addState = _varData;
581 addState.resume(); 681 addState.resume();
582 } 682 }
583 _runGuarded(_onResume); 683 _runGuarded(_onResume);
584 } 684 }
585 } 685 }
586 686
587 abstract class _SyncStreamControllerDispatch<T> 687 abstract class _SyncStreamControllerDispatch<T>
588 implements _StreamController<T> { 688 implements _StreamController<T>, SynchronousStreamController<T> {
689 int get _state;
690 void set _state(int state);
691
589 void _sendData(T data) { 692 void _sendData(T data) {
590 _subscription._add(data); 693 _subscription._add(data);
591 } 694 }
592 695
593 void _sendError(Object error, StackTrace stackTrace) { 696 void _sendError(Object error, StackTrace stackTrace) {
594 _subscription._addError(error, stackTrace); 697 _subscription._addError(error, stackTrace);
595 } 698 }
596 699
597 void _sendDone() { 700 void _sendDone() {
598 _subscription._close(); 701 _subscription._close();
(...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after
794 _StreamControllerAddStreamState(_StreamController controller, 897 _StreamControllerAddStreamState(_StreamController controller,
795 this.varData, 898 this.varData,
796 Stream source, 899 Stream source,
797 bool cancelOnError) 900 bool cancelOnError)
798 : super(controller, source, cancelOnError) { 901 : super(controller, source, cancelOnError) {
799 if (controller.isPaused) { 902 if (controller.isPaused) {
800 addSubscription.pause(); 903 addSubscription.pause();
801 } 904 }
802 } 905 }
803 } 906 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698