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

Unified Diff: sdk/lib/async/stream.dart

Issue 96473003: Add Stream.timeout method. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Updated documentation. Created 7 years, 1 month 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tests/lib/async/stream_timeout_test.dart » ('j') | tests/lib/async/stream_timeout_test.dart » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/async/stream.dart
diff --git a/sdk/lib/async/stream.dart b/sdk/lib/async/stream.dart
index 58bb090cba99e14547def60c9a5e0d31e43dede9..7dd348e93b061d6bb9d35a8623daf32735bbb897 100644
--- a/sdk/lib/async/stream.dart
+++ b/sdk/lib/async/stream.dart
@@ -649,7 +649,7 @@ abstract class Stream<T> {
* as [test] returns [:true:] for the event data. The stream is done
* when either this stream is done, or when this stream first provides
* a value that [test] doesn't accept.
- *
+ *
* Stops listening to the stream after the accepted elements.
*
* Internally the method cancels its subscription after these elements. This
@@ -960,6 +960,101 @@ abstract class Stream<T> {
cancelOnError: true);
return future;
}
+
+ /**
+ * Creates a new stream with the same events as this stream.
+ *
+ * Whenever more than [timeLimit] passes between two events from this stream,
+ * the [onTimeout] function is called.
+ *
+ * The countdown doesn't start until the returned stream is listened to.
+ * The countdown is reset every time an event is forwarded from this stream,
+ * or when the stream is paused and resumed.
+ *
+ * If the [onTimeout] function accepts one argument, it is called with an
+ * [EventSink] that allows putting events into the returned stream.
+ * This `EventSink` is only valid during the call to `onTimeout`.
+ *
+ * If the `onTimeout` function accepts two arguments, it is called with both
floitsch 2013/11/29 13:43:48 As discussed. Let's not do this.
Lasse Reichstein Nielsen 2013/11/29 13:58:03 Done.
+ * the `EventSink` and a function that allows canceling the input stream
+ * subscription. This will stop any further events from reaching the output
+ * stream.
+ *
+ * If `onTimeout` is omitted, a timeout will just put a [TimeoutException]
+ * into the error channel of the returned stream.
+ */
+ Stream timeout(Duration timeLimit, [Function onTimeout]) {
floitsch 2013/11/29 13:43:48 make it named.
Lasse Reichstein Nielsen 2013/11/29 13:58:03 Done.
+ StreamSubscription<T> subscription;
+ _StreamController controller;
+ Timer timer;
+ Zone outerZone = Zone.current;
+ Function timeout;
+ if (onTimeout == null) {
+ timeout = () {
+ controller.addError(new TimeoutException(/*"No stream event",*/
+ timeLimit));
+ };
+ } else {
+ Zone zone = outerZone.fork();
floitsch 2013/11/29 13:43:48 no need to fork.
Lasse Reichstein Nielsen 2013/11/29 13:58:03 Done, by not doing!
+ if (onTimeout is ZoneBinaryCallback) {
+ onTimeout = zone.registerBinaryCallback(onTimeout);
+ _ControllerEventSinkWrapper wrapper =
+ new _ControllerEventSinkWrapper(null);
+ timeout = () {
+ wrapper._sink = controller; // Only valid during call.
+ zone.runBinaryGuarded(onTimeout, wrapper, subscription.cancel);
+ wrapper._sink = null;
+ };
+ } else if (onTimeout is ZoneUnaryCallback) {
+ onTimeout = zone.registerUnaryCallback(onTimeout);
+ _ControllerEventSinkWrapper wrapper =
+ new _ControllerEventSinkWrapper(null);
+ timeout = () {
+ wrapper._sink = controller; // Only valid during call.
+ zone.runUnaryGuarded(onTimeout, wrapper);
+ wrapper._sink = null;
+ };
+ } else {
+ onTimeout = zone.registerCallback(onTimeout);
floitsch 2013/11/29 13:43:48 I would put that up to the onTimeout == null.
Lasse Reichstein Nielsen 2013/11/29 13:58:03 What? If onTimeout is null, why call registerCallb
+ timeout = () { zone.runGuarded(onTimeout); };
+ }
+ }
+
+ void onData(T event) {
+ timer.cancel();
floitsch 2013/11/29 13:43:48 This will be expensive. For a first implementation
Lasse Reichstein Nielsen 2013/11/29 13:58:03 How will that work? Have the initial timer run to
floitsch 2013/11/29 15:03:49 yes. something like that.
+ controller.add(event);
+ timer = outerZone.createTimer(timeLimit, timeout);
+ }
+ void onError(error, StackTrace stackTrace) {
+ timer.cancel();
+ controller.addError(error, stackTrace);
+ timer = outerZone.createTimer(timeLimit, timeout);
+ }
+ void onDone() {
+ timer.cancel();
+ controller.close();
+ }
+ controller = new _SyncStreamController(
+ () {
+ subscription = this.listen(onData, onError: onError, onDone: onDone);
+ timer = outerZone.createTimer(timeLimit, timeout);
+ },
+ () {
+ timer.cancel();
+ subscription.pause();
+ },
+ () {
+ subscription.resume();
+ timer = outerZone.createTimer(timeLimit, timeout);
+ },
+ () {
+ timer.cancel();
+ Future result = subscription.cancel();
+ subscription = null;
+ return result;
+ });
+ return controller.stream;
+ }
}
/**
@@ -1282,3 +1377,18 @@ abstract class StreamIterator<T> {
*/
Future cancel();
}
+
+
+/**
+ * Wraps an [_EventSink] so it exposes only the [EventSink] interface.
floitsch 2013/11/29 13:43:48 Don't we already have something like this?
Lasse Reichstein Nielsen 2013/11/29 13:58:03 Not exactly, sadly. We have something that takes a
+ */
+class _ControllerEventSinkWrapper<T> implements EventSink<T> {
+ EventSink _sink;
+ _ControllerEventSinkWrapper(this._sink);
+
+ void add(T data) { _sink.add(data); }
+ void addError(error, [StackTrace stackTrace]) {
+ _sink.addError(error, stackTrace);
+ }
+ void close() { _sink.close(); }
+}
« no previous file with comments | « no previous file | tests/lib/async/stream_timeout_test.dart » ('j') | tests/lib/async/stream_timeout_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698