3
3
4
4
using System . IO . Pipes ;
5
5
using System . Linq ;
6
+ using System . Runtime . CompilerServices ;
6
7
using System . Security . Cryptography ;
7
8
using System . Threading ;
8
9
using System . Threading . Tasks ;
@@ -96,7 +97,9 @@ public async Task ReadToAnEmptyBufferReturnsZeroWhenDataIsAvailable()
96
97
97
98
Assert . Equal ( 0 , RandomAccess . Read ( readHandle , Array . Empty < byte > ( ) , fileOffset : 0 ) ) ; // what we test
98
99
byte [ ] buffer = new byte [ content . Length * 2 ] ;
99
- Assert . Equal ( content . Length , RandomAccess . Read ( readHandle , buffer , fileOffset : 0 ) ) ; // what is required for the above write to succeed
100
+
101
+ ReadExactly ( readHandle , buffer , content . Length ) ; // what is required for the above write to succeed
102
+
100
103
Assert . Equal ( content , buffer . AsSpan ( 0 , content . Length ) . ToArray ( ) ) ;
101
104
102
105
await write ;
@@ -118,12 +121,11 @@ public async Task ReadToAnEmptyBufferReturnsZeroWhenDataIsAvailableAsync()
118
121
Assert . Equal ( 0 , await readToEmpty ) ;
119
122
120
123
byte [ ] buffer = new byte [ content . Length * 2 ] ;
121
- Task < int > readToNonEmpty = RandomAccess . ReadAsync ( readHandle , buffer , fileOffset : 0 ) . AsTask ( ) ; // what is required for the above write to succeed
124
+ Task readToNonEmpty = ReadExactlyAsync ( readHandle , buffer , content . Length ) ; // what is required for the above write to succeed
122
125
123
126
await Task . WhenAll ( readToNonEmpty , write ) ;
124
127
125
- Assert . Equal ( content . Length , readToNonEmpty . Result ) ;
126
- Assert . Equal ( content , buffer . AsSpan ( 0 , readToNonEmpty . Result ) . ToArray ( ) ) ;
128
+ Assert . Equal ( content , buffer . AsSpan ( 0 , content . Length ) . ToArray ( ) ) ;
127
129
}
128
130
}
129
131
@@ -146,7 +148,7 @@ public async Task CanReadToStackAllocatedMemory()
146
148
void ReadToStackAllocatedBuffer ( SafeFileHandle handle , byte [ ] array )
147
149
{
148
150
Span < byte > buffer = stackalloc byte [ array . Length * 2 ] ;
149
- Assert . Equal ( array . Length , RandomAccess . Read ( handle , buffer , fileOffset : 0 ) ) ;
151
+ ReadExactly ( handle , buffer , array . Length ) ;
150
152
Assert . Equal ( array , buffer . Slice ( 0 , array . Length ) . ToArray ( ) ) ;
151
153
}
152
154
}
@@ -161,11 +163,11 @@ public async Task CanWriteFromStackAllocatedMemory()
161
163
{
162
164
byte [ ] content = RandomNumberGenerator . GetBytes ( BufferSize ) ;
163
165
byte [ ] buffer = new byte [ content . Length * 2 ] ;
164
- Task < int > read = RandomAccess . ReadAsync ( readHandle , buffer , fileOffset : 0 ) . AsTask ( ) ;
166
+ Task read = ReadExactlyAsync ( readHandle , buffer , content . Length ) ;
165
167
166
168
WriteFromStackAllocatedBuffer ( writeHandle , content ) ;
167
169
168
- Assert . Equal ( content . Length , await read ) ;
170
+ await read ;
169
171
Assert . Equal ( content , buffer . AsSpan ( 0 , content . Length ) . ToArray ( ) ) ;
170
172
}
171
173
@@ -190,8 +192,8 @@ public async Task FileOffsetsAreIgnored_AsyncWrite_SyncRead()
190
192
byte [ ] buffer = new byte [ content . Length * 2 ] ;
191
193
int readFromOffset456 = RandomAccess . Read ( readHandle , buffer , fileOffset : 456 ) ;
192
194
193
- Assert . Equal ( content . Length , readFromOffset456 ) ;
194
- Assert . Equal ( content , buffer . AsSpan ( 0 , readFromOffset456 ) . ToArray ( ) ) ;
195
+ Assert . InRange ( readFromOffset456 , 1 , content . Length ) ;
196
+ Assert . Equal ( content . Take ( readFromOffset456 ) , buffer . AsSpan ( 0 , readFromOffset456 ) . ToArray ( ) ) ;
195
197
196
198
await writeToOffset123 ;
197
199
}
@@ -211,8 +213,9 @@ public async Task FileOffsetsAreIgnored_AsyncRead_SyncWrite()
211
213
212
214
RandomAccess . Write ( writeHandle , content , fileOffset : 123 ) ;
213
215
214
- Assert . Equal ( content . Length , readFromOffset456 . Result ) ;
215
- Assert . Equal ( content , buffer . AsSpan ( 0 , readFromOffset456 . Result ) . ToArray ( ) ) ;
216
+ int bytesRead = await readFromOffset456 ;
217
+ Assert . InRange ( bytesRead , 1 , content . Length ) ;
218
+ Assert . Equal ( content . Take ( bytesRead ) , buffer . AsSpan ( 0 , readFromOffset456 . Result ) . ToArray ( ) ) ;
216
219
}
217
220
}
218
221
@@ -231,8 +234,8 @@ public async Task FileOffsetsAreIgnoredAsync()
231
234
232
235
await Task . WhenAll ( readFromOffset456 , writeToOffset123 ) ;
233
236
234
- Assert . Equal ( content . Length , readFromOffset456 . Result ) ;
235
- Assert . Equal ( content , buffer . AsSpan ( 0 , readFromOffset456 . Result ) . ToArray ( ) ) ;
237
+ Assert . InRange ( readFromOffset456 . Result , 1 , content . Length ) ;
238
+ Assert . Equal ( content . Take ( readFromOffset456 . Result ) , buffer . AsSpan ( 0 , readFromOffset456 . Result ) . ToArray ( ) ) ;
236
239
}
237
240
}
238
241
@@ -247,13 +250,13 @@ public async Task PartialReadsAreSupported()
247
250
byte [ ] content = RandomNumberGenerator . GetBytes ( BufferSize ) ;
248
251
ValueTask write = RandomAccess . WriteAsync ( writeHandle , content , fileOffset : 0 ) ;
249
252
250
- byte [ ] buffer = new byte [ content . Length * 2 ] ;
253
+ byte [ ] buffer = new byte [ BufferSize ] ;
251
254
252
- for ( int i = 0 ; i < content . Length ; i ++ )
255
+ for ( int i = 0 ; i < BufferSize ; i ++ )
253
256
{
254
257
Assert . Equal ( 1 , RandomAccess . Read ( readHandle , buffer . AsSpan ( i , 1 ) , fileOffset : 0 ) ) ;
255
258
}
256
- Assert . Equal ( content , buffer . AsSpan ( 0 , content . Length ) . ToArray ( ) ) ;
259
+ Assert . Equal ( content , buffer ) ;
257
260
258
261
await write ;
259
262
}
@@ -270,10 +273,13 @@ public async Task PartialReadsAreSupportedAsync()
270
273
byte [ ] content = RandomNumberGenerator . GetBytes ( BufferSize ) ;
271
274
ValueTask write = RandomAccess . WriteAsync ( writeHandle , content , fileOffset : 0 ) ;
272
275
273
- byte [ ] buffer = new byte [ content . Length * 2 ] ;
274
- Assert . Equal ( 1 , await RandomAccess . ReadAsync ( readHandle , buffer . AsMemory ( 0 , 1 ) , fileOffset : 0 ) ) ;
275
- Assert . Equal ( 2 , await RandomAccess . ReadAsync ( readHandle , buffer . AsMemory ( 1 ) , fileOffset : 0 ) ) ;
276
- Assert . Equal ( content , buffer . AsSpan ( 0 , 3 ) . ToArray ( ) ) ;
276
+ byte [ ] buffer = new byte [ BufferSize ] ;
277
+
278
+ for ( int i = 0 ; i < BufferSize ; i ++ )
279
+ {
280
+ Assert . Equal ( 1 , await RandomAccess . ReadAsync ( readHandle , buffer . AsMemory ( i , 1 ) , fileOffset : 0 ) ) ;
281
+ }
282
+ Assert . Equal ( content , buffer ) ;
277
283
}
278
284
}
279
285
@@ -289,14 +295,13 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_SyncReads()
289
295
Task write = RandomAccess . WriteAsync ( writeHandle , vectors , fileOffset : 123 ) . AsTask ( ) ;
290
296
byte [ ] buffer = new byte [ VectorsByteCount * 2 ] ;
291
297
292
- int read = 0 ;
293
-
298
+ int bytesRead = 0 ;
294
299
do
295
300
{
296
- read += RandomAccess . Read ( readHandle , buffer . AsSpan ( read ) , fileOffset : 456 ) ;
297
- } while ( read != VectorsByteCount ) ;
301
+ bytesRead += RandomAccess . Read ( readHandle , buffer . AsSpan ( bytesRead ) , fileOffset : 456 ) ;
302
+ } while ( bytesRead != VectorsByteCount ) ;
298
303
299
- Assert . Equal ( vectors . SelectMany ( vector => vector . ToArray ( ) ) , buffer . AsSpan ( 0 , read ) . ToArray ( ) ) ;
304
+ Assert . Equal ( vectors . SelectMany ( vector => vector . ToArray ( ) ) , buffer . AsSpan ( 0 , bytesRead ) . ToArray ( ) ) ;
300
305
301
306
await write ;
302
307
}
@@ -315,10 +320,10 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_SyncRead()
315
320
byte [ ] buffer = new byte [ VectorsByteCount * 2 ] ;
316
321
317
322
Memory < byte > [ ] writableVectors = GenerateVectors ( VectorCount , BufferSize ) ;
318
- long read = RandomAccess . Read ( readHandle , writableVectors , fileOffset : 456 ) ;
323
+ int bytesRead = ( int ) RandomAccess . Read ( readHandle , writableVectors , fileOffset : 456 ) ;
319
324
320
- Assert . Equal ( VectorsByteCount , read ) ;
321
- Assert . Equal ( readOnlyVectors . SelectMany ( vector => vector . ToArray ( ) ) , writableVectors . SelectMany ( vector => vector . ToArray ( ) ) ) ;
325
+ Assert . InRange ( bytesRead , 1 , VectorsByteCount ) ;
326
+ AssertEqual ( readOnlyVectors , writableVectors , bytesRead ) ;
322
327
323
328
await write ;
324
329
}
@@ -338,8 +343,9 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_AsyncRead()
338
343
339
344
Memory < byte > [ ] writableVectors = GenerateVectors ( VectorCount , BufferSize ) ;
340
345
341
- Assert . Equal ( VectorsByteCount , await RandomAccess . ReadAsync ( readHandle , writableVectors , fileOffset : 456 ) ) ;
342
- Assert . Equal ( readOnlyVectors . SelectMany ( vector => vector . ToArray ( ) ) , writableVectors . SelectMany ( vector => vector . ToArray ( ) ) ) ;
346
+ long bytesRead = await RandomAccess . ReadAsync ( readHandle , writableVectors , fileOffset : 456 ) ;
347
+ Assert . InRange ( bytesRead , 1 , VectorsByteCount ) ;
348
+ AssertEqual ( readOnlyVectors , writableVectors , ( int ) bytesRead ) ;
343
349
344
350
await write ;
345
351
}
@@ -359,8 +365,9 @@ public async Task MultipleBuffersAreSupported_AsyncRead_SyncWrite()
359
365
byte [ ] content = RandomNumberGenerator . GetBytes ( VectorsByteCount ) ;
360
366
RandomAccess . Write ( writeHandle , content , fileOffset : 123 ) ;
361
367
362
- Assert . Equal ( VectorsByteCount , await read ) ;
363
- Assert . Equal ( content , writableVectors . SelectMany ( vector => vector . ToArray ( ) ) ) ;
368
+ int bytesRead = ( int ) await read ;
369
+ Assert . InRange ( bytesRead , 1 , VectorsByteCount ) ;
370
+ Assert . Equal ( content . Take ( bytesRead ) , writableVectors . SelectMany ( vector => vector . ToArray ( ) ) . Take ( bytesRead ) ) ;
364
371
}
365
372
}
366
373
@@ -379,9 +386,7 @@ private static SafeFileHandle GetFileHandle(PipeStream pipeStream, bool isAsync)
379
386
if ( OperatingSystem . IsWindows ( ) && isAsync )
380
387
{
381
388
// Currently it's impossible to duplicate an async safe handle that has already been bound to Thread Pool: https://github.com/dotnet/runtime/issues/28585
382
- // I am opened for ideas on how we could solve it without an ugly reflection hack..
383
- ThreadPoolBoundHandle threadPoolBinding = ( ThreadPoolBoundHandle ) typeof ( PipeStream ) . GetField ( "_threadPoolBinding" , Reflection . BindingFlags . NonPublic | Reflection . BindingFlags . Instance ) . GetValue ( pipeStream ) ;
384
- typeof ( SafeFileHandle ) . GetProperty ( "ThreadPoolBinding" , Reflection . BindingFlags . NonPublic | Reflection . BindingFlags . Instance ) . GetSetMethod ( true ) . Invoke ( serverHandle , new object [ ] { threadPoolBinding } ) ;
389
+ FileThreadPoolBoundHandle ( serverHandle ) = PipeThreadPoolBoundHandle ( pipeStream ) ;
385
390
}
386
391
387
392
return serverHandle ;
@@ -390,6 +395,35 @@ private static SafeFileHandle GetFileHandle(PipeStream pipeStream, bool isAsync)
390
395
{
391
396
pipeStream . SafePipeHandle . SetHandleAsInvalid ( ) ;
392
397
}
398
+
399
+ [ UnsafeAccessor ( UnsafeAccessorKind . Field , Name = "_threadPoolBinding" ) ]
400
+ extern static ref ThreadPoolBoundHandle PipeThreadPoolBoundHandle ( PipeStream @this ) ;
401
+
402
+ [ UnsafeAccessor ( UnsafeAccessorKind . Field , Name = "<ThreadPoolBinding>k__BackingField" ) ]
403
+ extern static ref ThreadPoolBoundHandle FileThreadPoolBoundHandle ( SafeFileHandle @this ) ;
404
+ }
405
+
406
+ private static void ReadExactly ( SafeFileHandle readHandle , Span < byte > buffer , int expectedByteCount )
407
+ {
408
+ int bytesRead = 0 ;
409
+ do
410
+ {
411
+ bytesRead += RandomAccess . Read ( readHandle , buffer . Slice ( bytesRead ) , fileOffset : 0 ) ; // fileOffset NOT set to bytesRead on purpose
412
+ } while ( bytesRead != expectedByteCount ) ;
393
413
}
414
+
415
+ private static async Task ReadExactlyAsync ( SafeFileHandle readHandle , byte [ ] buffer , int expectedByteCount )
416
+ {
417
+ int bytesRead = 0 ;
418
+ do
419
+ {
420
+ bytesRead += await RandomAccess . ReadAsync ( readHandle , buffer . AsMemory ( bytesRead ) , fileOffset : 0 ) ; // fileOffset NOT set to bytesRead on purpose
421
+ } while ( bytesRead != expectedByteCount ) ;
422
+ }
423
+
424
+ private static void AssertEqual ( ReadOnlyMemory < byte > [ ] readOnlyVectors , Memory < byte > [ ] writableVectors , int byteCount )
425
+ => Assert . Equal (
426
+ readOnlyVectors . SelectMany ( vector => vector . ToArray ( ) ) . Take ( byteCount ) ,
427
+ writableVectors . SelectMany ( vector => vector . ToArray ( ) ) . Take ( byteCount ) ) ;
394
428
}
395
429
}
0 commit comments