Line data Source code
1 : // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 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. 4 : 5 : import 'dart:async'; 6 : 7 : import 'package:async/async.dart'; 8 : 9 : import '../stream_channel.dart'; 10 : 11 : /// A [StreamChannel] that specifically enforces the stream channel guarantee 12 : /// that closing the sink causes the stream to close before it emits any more 13 : /// events 14 : /// 15 : /// This is exposed via [new StreamChannel.withCloseGuarantee]. 16 : class CloseGuaranteeChannel<T> extends StreamChannelMixin<T> { 17 0 : @override 18 0 : Stream<T> get stream => _stream; 19 : late final _CloseGuaranteeStream<T> _stream; 20 : 21 0 : @override 22 0 : StreamSink<T> get sink => _sink; 23 : late final _CloseGuaranteeSink<T> _sink; 24 : 25 : /// The subscription to the inner stream. 26 : StreamSubscription<T>? _subscription; 27 : 28 : /// Whether the sink has closed, causing the underlying channel to disconnect. 29 : bool _disconnected = false; 30 : 31 0 : CloseGuaranteeChannel(Stream<T> innerStream, StreamSink<T> innerSink) { 32 0 : _sink = _CloseGuaranteeSink<T>(innerSink, this); 33 0 : _stream = _CloseGuaranteeStream<T>(innerStream, this); 34 : } 35 : } 36 : 37 : /// The stream for [CloseGuaranteeChannel]. 38 : /// 39 : /// This wraps the inner stream to save the subscription on the channel when 40 : /// [listen] is called. 41 : class _CloseGuaranteeStream<T> extends Stream<T> { 42 : /// The inner stream this is delegating to. 43 : final Stream<T> _inner; 44 : 45 : /// The [CloseGuaranteeChannel] this belongs to. 46 : final CloseGuaranteeChannel<T> _channel; 47 : 48 0 : _CloseGuaranteeStream(this._inner, this._channel); 49 : 50 0 : @override 51 : StreamSubscription<T> listen(void Function(T)? onData, 52 : {Function? onError, void Function()? onDone, bool? cancelOnError}) { 53 : // If the channel is already disconnected, we shouldn't dispatch anything 54 : // but a done event. 55 0 : if (_channel._disconnected) { 56 : onData = null; 57 : onError = null; 58 : } 59 : 60 0 : var subscription = _inner.listen(onData, 61 : onError: onError, onDone: onDone, cancelOnError: cancelOnError); 62 0 : if (!_channel._disconnected) { 63 0 : _channel._subscription = subscription; 64 : } 65 : return subscription; 66 : } 67 : } 68 : 69 : /// The sink for [CloseGuaranteeChannel]. 70 : /// 71 : /// This wraps the inner sink to cancel the stream subscription when the sink is 72 : /// canceled. 73 : class _CloseGuaranteeSink<T> extends DelegatingStreamSink<T> { 74 : /// The [CloseGuaranteeChannel] this belongs to. 75 : final CloseGuaranteeChannel<T> _channel; 76 : 77 0 : _CloseGuaranteeSink(StreamSink<T> inner, this._channel) : super(inner); 78 : 79 0 : @override 80 : Future<void> close() { 81 0 : var done = super.close(); 82 0 : _channel._disconnected = true; 83 0 : var subscription = _channel._subscription; 84 : if (subscription != null) { 85 : // Don't dispatch anything but a done event. 86 0 : subscription.onData(null); 87 0 : subscription.onError(null); 88 : } 89 : return done; 90 : } 91 : }