@@ -36,6 +36,40 @@ public static void EfficientCopyTo(this Stream input, Stream output)
36
36
}
37
37
}
38
38
39
+ public static async Task < int > ReadAsync ( this Stream stream , byte [ ] buffer , int offset , int count , TimeSpan timeout , CancellationToken cancellationToken )
40
+ {
41
+ var state = 1 ; // 1 == reading, 2 == done reading, 3 == timedout, 4 == cancelled
42
+
43
+ var bytesRead = 0 ;
44
+ using ( new Timer ( _ => ChangeState ( 3 ) , null , timeout , Timeout . InfiniteTimeSpan ) )
45
+ using ( cancellationToken . Register ( ( ) => ChangeState ( 4 ) ) )
46
+ {
47
+ try
48
+ {
49
+ bytesRead = await stream . ReadAsync ( buffer , offset , count , cancellationToken ) . ConfigureAwait ( false ) ;
50
+ ChangeState ( 2 ) ;
51
+ }
52
+ catch when ( state >= 3 )
53
+ {
54
+ // a different exception will be thrown instead below
55
+ }
56
+
57
+ if ( state == 3 ) { throw new TimeoutException ( ) ; }
58
+ if ( state == 4 ) { throw new OperationCanceledException ( ) ; }
59
+ }
60
+
61
+ return bytesRead ;
62
+
63
+ void ChangeState ( int to )
64
+ {
65
+ var from = Interlocked . CompareExchange ( ref state , to , 1 ) ;
66
+ if ( from == 1 && to >= 3 )
67
+ {
68
+ try { stream . Dispose ( ) ; } catch { } // disposing the stream aborts the read attempt
69
+ }
70
+ }
71
+ }
72
+
39
73
public static void ReadBytes ( this Stream stream , byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
40
74
{
41
75
Ensure . IsNotNull ( stream , nameof ( stream ) ) ;
@@ -76,7 +110,7 @@ public static void ReadBytes(this Stream stream, IByteBuffer buffer, int offset,
76
110
}
77
111
}
78
112
79
- public static async Task ReadBytesAsync ( this Stream stream , byte [ ] buffer , int offset , int count , CancellationToken cancellationToken )
113
+ public static async Task ReadBytesAsync ( this Stream stream , byte [ ] buffer , int offset , int count , TimeSpan timeout , CancellationToken cancellationToken )
80
114
{
81
115
Ensure . IsNotNull ( stream , nameof ( stream ) ) ;
82
116
Ensure . IsNotNull ( buffer , nameof ( buffer ) ) ;
@@ -85,7 +119,7 @@ public static async Task ReadBytesAsync(this Stream stream, byte[] buffer, int o
85
119
86
120
while ( count > 0 )
87
121
{
88
- var bytesRead = await stream . ReadAsync ( buffer , offset , count , cancellationToken ) . ConfigureAwait ( false ) ;
122
+ var bytesRead = await stream . ReadAsync ( buffer , offset , count , timeout , cancellationToken ) . ConfigureAwait ( false ) ;
89
123
if ( bytesRead == 0 )
90
124
{
91
125
throw new EndOfStreamException ( ) ;
@@ -95,7 +129,7 @@ public static async Task ReadBytesAsync(this Stream stream, byte[] buffer, int o
95
129
}
96
130
}
97
131
98
- public static async Task ReadBytesAsync ( this Stream stream , IByteBuffer buffer , int offset , int count , CancellationToken cancellationToken )
132
+ public static async Task ReadBytesAsync ( this Stream stream , IByteBuffer buffer , int offset , int count , TimeSpan timeout , CancellationToken cancellationToken )
99
133
{
100
134
Ensure . IsNotNull ( stream , nameof ( stream ) ) ;
101
135
Ensure . IsNotNull ( buffer , nameof ( buffer ) ) ;
@@ -106,7 +140,7 @@ public static async Task ReadBytesAsync(this Stream stream, IByteBuffer buffer,
106
140
{
107
141
var backingBytes = buffer . AccessBackingBytes ( offset ) ;
108
142
var bytesToRead = Math . Min ( count , backingBytes . Count ) ;
109
- var bytesRead = await stream . ReadAsync ( backingBytes . Array , backingBytes . Offset , bytesToRead , cancellationToken ) . ConfigureAwait ( false ) ;
143
+ var bytesRead = await stream . ReadAsync ( backingBytes . Array , backingBytes . Offset , bytesToRead , timeout , cancellationToken ) . ConfigureAwait ( false ) ;
110
144
if ( bytesRead == 0 )
111
145
{
112
146
throw new EndOfStreamException ( ) ;
@@ -116,6 +150,38 @@ public static async Task ReadBytesAsync(this Stream stream, IByteBuffer buffer,
116
150
}
117
151
}
118
152
153
+
154
+ public static async Task WriteAsync ( this Stream stream , byte [ ] buffer , int offset , int count , TimeSpan timeout , CancellationToken cancellationToken )
155
+ {
156
+ var state = 1 ; // 1 == writing, 2 == done writing, 3 == timedout, 4 == cancelled
157
+
158
+ using ( new Timer ( _ => ChangeState ( 3 ) , null , timeout , Timeout . InfiniteTimeSpan ) )
159
+ using ( cancellationToken . Register ( ( ) => ChangeState ( 4 ) ) )
160
+ {
161
+ try
162
+ {
163
+ await stream . WriteAsync ( buffer , offset , count , cancellationToken ) . ConfigureAwait ( false ) ;
164
+ ChangeState ( 2 ) ;
165
+ }
166
+ catch when ( state >= 3 )
167
+ {
168
+ // a different exception will be thrown instead below
169
+ }
170
+
171
+ if ( state == 3 ) { throw new TimeoutException ( ) ; }
172
+ if ( state == 4 ) { throw new OperationCanceledException ( ) ; }
173
+ }
174
+
175
+ void ChangeState ( int to )
176
+ {
177
+ var from = Interlocked . CompareExchange ( ref state , to , 1 ) ;
178
+ if ( from == 1 && to >= 3 )
179
+ {
180
+ try { stream . Dispose ( ) ; } catch { } // disposing the stream aborts the write attempt
181
+ }
182
+ }
183
+ }
184
+
119
185
public static void WriteBytes ( this Stream stream , IByteBuffer buffer , int offset , int count , CancellationToken cancellationToken )
120
186
{
121
187
Ensure . IsNotNull ( stream , nameof ( stream ) ) ;
@@ -134,7 +200,7 @@ public static void WriteBytes(this Stream stream, IByteBuffer buffer, int offset
134
200
}
135
201
}
136
202
137
- public static async Task WriteBytesAsync ( this Stream stream , IByteBuffer buffer , int offset , int count , CancellationToken cancellationToken )
203
+ public static async Task WriteBytesAsync ( this Stream stream , IByteBuffer buffer , int offset , int count , TimeSpan timeout , CancellationToken cancellationToken )
138
204
{
139
205
Ensure . IsNotNull ( stream , nameof ( stream ) ) ;
140
206
Ensure . IsNotNull ( buffer , nameof ( buffer ) ) ;
@@ -145,7 +211,7 @@ public static async Task WriteBytesAsync(this Stream stream, IByteBuffer buffer,
145
211
{
146
212
var backingBytes = buffer . AccessBackingBytes ( offset ) ;
147
213
var bytesToWrite = Math . Min ( count , backingBytes . Count ) ;
148
- await stream . WriteAsync ( backingBytes . Array , backingBytes . Offset , bytesToWrite , cancellationToken ) . ConfigureAwait ( false ) ;
214
+ await stream . WriteAsync ( backingBytes . Array , backingBytes . Offset , bytesToWrite , timeout , cancellationToken ) . ConfigureAwait ( false ) ;
149
215
offset += bytesToWrite ;
150
216
count -= bytesToWrite ;
151
217
}
0 commit comments