Wednesday, May 25, 2011

Unbuffered Overlapped IO in .NET (continued)

See the first article about overlapped IO.
As promised, I've created Buffered version of OverlappedStream. It has a byte[] buffer instead of unmanaged IntPtr, as there are no restrictions on memory alignment when FILE_FLAG_NO_BUFFERING is not used.
Also, in unbuffered version both WriteFileGather and ReadFileScatted functions were implemented. The restrictions for buffer size and alignment are different for Gather/Scatter operations: buffers should be of system page size and have the same alignment. OverlappedStreamUnbuffered.SystemPageSize property returns this value and it has to be used for buffer allocations.

Some examples

OverlappedStreamUnbuffered stream = new OverlappedStreamUnbuffered(

IntPtr[] buffers = new IntPtr[BUFFERS];
for (int i = 0; i < buffers.Length; i++)
 // allocate aligned buffers with VirtualAlloc or custom class
 buffers[i] = UnmanagedBufferPool.Alloc((int)OverlappedStreamUnbuffered.SystemPageSize);

IAsyncResult ar = stream.BeginReadScatter(buffers, startAddr, null, null);

uint bytesRead = OverlappedStream.EndOperation(ar, throwOnError);
// it is also possible to use more common syntax:
// stream.EndRead(ar);

Final notes

Asynchronous file access is often tricky, same as any multithreaded programming. One of non-obvious behaviours is that thread exit aborts pending IO operations started in this thread. It is well described here:
Therefore always wait for completion of all async operations even if they are initiated from a thread pool thread.

Full source code can be found on

1 comment:

  1. Hey Michael. I am trying to get OverlappedStreamUnbuffered.BeginWriteGather to work but I get the error 87. Error 87 means that memory is not pinned+aligned...but I use GCHandle.Alloc(bytearr, GCHandleType.Pinned) and AddrOfPinnedObject() but it does not seem to work. Question: how does you unmanagedpool pin byte arrays?