@@ -9,20 +9,20 @@ namespace Open.ChannelExtensions
99 abstract class BufferingChannelReader < TIn , TOut > : ChannelReader < TOut >
1010 {
1111 protected ChannelReader < TIn > ? Source ;
12- protected readonly Channel < TOut > Buffer ;
12+ protected readonly Channel < TOut > ? Buffer ;
1313 public BufferingChannelReader ( ChannelReader < TIn > source , bool singleReader )
1414 {
1515 Source = source ?? throw new ArgumentNullException ( nameof ( source ) ) ;
1616 Contract . EndContractBlock ( ) ;
1717
18- Buffer = Extensions . CreateChannel < TOut > ( - 1 , singleReader ) ;
19-
2018 if ( source . Completion . IsCompleted )
2119 {
22- Buffer . Writer . Complete ( source . Completion . Exception ) ;
20+ Buffer = null ;
2321 }
2422 else
2523 {
24+ Buffer = Extensions . CreateChannel < TOut > ( - 1 , singleReader ) ;
25+
2626 source . Completion . ContinueWith ( t =>
2727 {
2828 // Need to be sure writing is done before we continue...
@@ -37,55 +37,58 @@ public BufferingChannelReader(ChannelReader<TIn> source, bool singleReader)
3737 }
3838 }
3939
40- public override Task Completion => Buffer . Reader . Completion ;
40+ public override Task Completion => Buffer ? . Reader . Completion ?? Task . CompletedTask ;
4141
4242 protected abstract bool TryPipeItems ( ) ;
4343
4444 public override bool TryRead ( out TOut item )
4545 {
46- do
46+ if ( Buffer != null ) do
4747 {
4848 if ( Buffer . Reader . TryRead ( out item ) )
4949 return true ;
5050 }
5151 while ( TryPipeItems ( ) ) ;
5252
53- #pragma warning disable CS8653 // A default expression introduces a null value for a type parameter.
54- item = default ;
55- #pragma warning restore CS8653 // A default expression introduces a null value for a type parameter.
53+ item = default ! ;
5654 return false ;
5755 }
5856
5957 public override ValueTask < bool > WaitToReadAsync ( CancellationToken cancellationToken = default )
6058 {
59+ if ( Buffer == null || Buffer . Reader . Completion . IsCompleted )
60+ return new ValueTask < bool > ( false ) ;
61+
6162 if ( cancellationToken . IsCancellationRequested )
6263 return new ValueTask < bool > ( Task . FromCanceled < bool > ( cancellationToken ) ) ;
6364
64- var source = Source ;
6565 var b = Buffer . Reader . WaitToReadAsync ( cancellationToken ) ;
66- if ( b . IsCompletedSuccessfully || source == null || source . Completion . IsCompleted )
66+ if ( b . IsCompleted )
6767 return b ;
6868
69- var s = source . WaitToReadAsync ( cancellationToken ) ;
70- if ( s . IsCompletedSuccessfully )
71- return s . Result ? new ValueTask < bool > ( true ) : b ;
69+ var source = Source ;
70+ if ( source == null )
71+ return b ;
7272
7373 return WaitCore ( ) ;
7474
7575 async ValueTask < bool > WaitCore ( )
7676 {
77- cancellationToken . ThrowIfCancellationRequested ( ) ;
78-
79- // Not sure if there's a better way to 'WhenAny' with a ValueTask yet.
80- var bt = b . AsTask ( ) ;
81- var st = s . AsTask ( ) ;
82- var first = await Task . WhenAny ( bt , st ) ;
83- // Either one? Ok go.
84- if ( first . Result ) return true ;
85- // Buffer returned false? We're done.
86- if ( first == bt ) return false ;
87- // Second return false? Wait for buffer.
88- return await bt ;
77+
78+ start :
79+
80+ if ( b . IsCompleted ) return await b ;
81+
82+ var s = source . WaitToReadAsync ( cancellationToken ) ;
83+ if ( s . IsCompleted && ! b . IsCompleted )
84+ TryPipeItems ( ) ;
85+
86+ if ( b . IsCompleted ) return await b ;
87+ await s ;
88+ if ( b . IsCompleted ) return await b ;
89+ TryPipeItems ( ) ;
90+
91+ goto start ;
8992 }
9093 }
9194 }
0 commit comments