33
44using System . IO . Pipes ;
55using System . Linq ;
6+ using System . Runtime . CompilerServices ;
67using System . Security . Cryptography ;
78using System . Threading ;
89using System . Threading . Tasks ;
@@ -96,7 +97,9 @@ public async Task ReadToAnEmptyBufferReturnsZeroWhenDataIsAvailable()
9697
9798 Assert . Equal ( 0 , RandomAccess . Read ( readHandle , Array . Empty < byte > ( ) , fileOffset : 0 ) ) ; // what we test
9899 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+
100103 Assert . Equal ( content , buffer . AsSpan ( 0 , content . Length ) . ToArray ( ) ) ;
101104
102105 await write ;
@@ -118,12 +121,11 @@ public async Task ReadToAnEmptyBufferReturnsZeroWhenDataIsAvailableAsync()
118121 Assert . Equal ( 0 , await readToEmpty ) ;
119122
120123 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
122125
123126 await Task . WhenAll ( readToNonEmpty , write ) ;
124127
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 ( ) ) ;
127129 }
128130 }
129131
@@ -146,7 +148,7 @@ public async Task CanReadToStackAllocatedMemory()
146148 void ReadToStackAllocatedBuffer ( SafeFileHandle handle , byte [ ] array )
147149 {
148150 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 ) ;
150152 Assert . Equal ( array , buffer . Slice ( 0 , array . Length ) . ToArray ( ) ) ;
151153 }
152154 }
@@ -161,11 +163,11 @@ public async Task CanWriteFromStackAllocatedMemory()
161163 {
162164 byte [ ] content = RandomNumberGenerator . GetBytes ( BufferSize ) ;
163165 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 ) ;
165167
166168 WriteFromStackAllocatedBuffer ( writeHandle , content ) ;
167169
168- Assert . Equal ( content . Length , await read ) ;
170+ await read ;
169171 Assert . Equal ( content , buffer . AsSpan ( 0 , content . Length ) . ToArray ( ) ) ;
170172 }
171173
@@ -190,8 +192,8 @@ public async Task FileOffsetsAreIgnored_AsyncWrite_SyncRead()
190192 byte [ ] buffer = new byte [ content . Length * 2 ] ;
191193 int readFromOffset456 = RandomAccess . Read ( readHandle , buffer , fileOffset : 456 ) ;
192194
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 ( ) ) ;
195197
196198 await writeToOffset123 ;
197199 }
@@ -211,8 +213,9 @@ public async Task FileOffsetsAreIgnored_AsyncRead_SyncWrite()
211213
212214 RandomAccess . Write ( writeHandle , content , fileOffset : 123 ) ;
213215
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 ( ) ) ;
216219 }
217220 }
218221
@@ -231,8 +234,8 @@ public async Task FileOffsetsAreIgnoredAsync()
231234
232235 await Task . WhenAll ( readFromOffset456 , writeToOffset123 ) ;
233236
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 ( ) ) ;
236239 }
237240 }
238241
@@ -247,13 +250,13 @@ public async Task PartialReadsAreSupported()
247250 byte [ ] content = RandomNumberGenerator . GetBytes ( BufferSize ) ;
248251 ValueTask write = RandomAccess . WriteAsync ( writeHandle , content , fileOffset : 0 ) ;
249252
250- byte [ ] buffer = new byte [ content . Length * 2 ] ;
253+ byte [ ] buffer = new byte [ BufferSize ] ;
251254
252- for ( int i = 0 ; i < content . Length ; i ++ )
255+ for ( int i = 0 ; i < BufferSize ; i ++ )
253256 {
254257 Assert . Equal ( 1 , RandomAccess . Read ( readHandle , buffer . AsSpan ( i , 1 ) , fileOffset : 0 ) ) ;
255258 }
256- Assert . Equal ( content , buffer . AsSpan ( 0 , content . Length ) . ToArray ( ) ) ;
259+ Assert . Equal ( content , buffer ) ;
257260
258261 await write ;
259262 }
@@ -270,10 +273,13 @@ public async Task PartialReadsAreSupportedAsync()
270273 byte [ ] content = RandomNumberGenerator . GetBytes ( BufferSize ) ;
271274 ValueTask write = RandomAccess . WriteAsync ( writeHandle , content , fileOffset : 0 ) ;
272275
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 ) ;
277283 }
278284 }
279285
@@ -289,14 +295,13 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_SyncReads()
289295 Task write = RandomAccess . WriteAsync ( writeHandle , vectors , fileOffset : 123 ) . AsTask ( ) ;
290296 byte [ ] buffer = new byte [ VectorsByteCount * 2 ] ;
291297
292- int read = 0 ;
293-
298+ int bytesRead = 0 ;
294299 do
295300 {
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 ) ;
298303
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 ( ) ) ;
300305
301306 await write ;
302307 }
@@ -315,10 +320,10 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_SyncRead()
315320 byte [ ] buffer = new byte [ VectorsByteCount * 2 ] ;
316321
317322 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 ) ;
319324
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 ) ;
322327
323328 await write ;
324329 }
@@ -338,8 +343,9 @@ public async Task MultipleBuffersAreSupported_AsyncWrite_AsyncRead()
338343
339344 Memory < byte > [ ] writableVectors = GenerateVectors ( VectorCount , BufferSize ) ;
340345
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 ) ;
343349
344350 await write ;
345351 }
@@ -359,8 +365,9 @@ public async Task MultipleBuffersAreSupported_AsyncRead_SyncWrite()
359365 byte [ ] content = RandomNumberGenerator . GetBytes ( VectorsByteCount ) ;
360366 RandomAccess . Write ( writeHandle , content , fileOffset : 123 ) ;
361367
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 ) ) ;
364371 }
365372 }
366373
@@ -379,9 +386,7 @@ private static SafeFileHandle GetFileHandle(PipeStream pipeStream, bool isAsync)
379386 if ( OperatingSystem . IsWindows ( ) && isAsync )
380387 {
381388 // 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 ) ;
385390 }
386391
387392 return serverHandle ;
@@ -390,6 +395,35 @@ private static SafeFileHandle GetFileHandle(PipeStream pipeStream, bool isAsync)
390395 {
391396 pipeStream . SafePipeHandle . SetHandleAsInvalid ( ) ;
392397 }
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 ) ;
393413 }
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 ) ) ;
394428 }
395429}
0 commit comments