// When doing IO asynchronously (i.e. _isAsync==true), this callback is // called by a free thread in the threadpool when the IO operation // completes. internal static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped *pOverlapped) { // Extract the completion source from the overlapped. The state in the overlapped // will either be a FileStreamStrategy (in the case where the preallocated overlapped was used), // in which case the operation being completed is its _currentOverlappedOwner, or it'll // be directly the FileStreamCompletionSource that's completing (in the case where the preallocated // overlapped was already in use by another operation). object?state = ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped); Debug.Assert(state is IFileStreamCompletionSourceStrategy || state is FileStreamCompletionSource); FileStreamCompletionSource completionSource = state switch { IFileStreamCompletionSourceStrategy strategy => strategy.CurrentOverlappedOwner !, // must be owned _ => (FileStreamCompletionSource)state }; Debug.Assert(completionSource != null); Debug.Assert(completionSource._overlapped == pOverlapped, "Overlaps don't match"); // Handle reading from & writing to closed pipes. While I'm not sure // this is entirely necessary anymore, maybe it's possible for // an async read on a pipe to be issued and then the pipe is closed, // returning this error. This may very well be necessary. ulong packedResult; if (errorCode != 0 && errorCode != ERROR_BROKEN_PIPE && errorCode != ERROR_NO_DATA) { packedResult = ((ulong)ResultError | errorCode); } else { packedResult = ((ulong)ResultSuccess | numBytes); } // Stow the result so that other threads can observe it // And, if no other thread is registering cancellation, continue if (NoResult == Interlocked.Exchange(ref completionSource._result, (long)packedResult)) { // Successfully set the state, attempt to take back the callback if (Interlocked.Exchange(ref completionSource._result, CompletedCallback) != NoResult) { // Successfully got the callback, finish the callback completionSource.CompleteCallback(packedResult); } // else: Some other thread stole the result, so now it is responsible to finish the callback } // else: Some other thread is registering a cancellation, so it *must* finish the callback }
// When doing IO asynchronously (ie, _isAsync==true), this callback is // called by a free thread in the threadpool when the IO operation // completes. unsafe private static void AsyncFSCallback(uint errorCode, uint numBytes, NativeOverlapped *pOverlapped) { // Unpack overlapped Overlapped overlapped = Threading.Overlapped.Unpack(pOverlapped); // Extract async result from overlapped FileStreamCompletionSource completionSource = (FileStreamCompletionSource)overlapped.AsyncResult; Debug.Assert(completionSource._overlapped == pOverlapped, "Overlaps don't match"); // Handle reading from & writing to closed pipes. While I'm not sure // this is entirely necessary anymore, maybe it's possible for // an async read on a pipe to be issued and then the pipe is closed, // returning this error. This may very well be necessary. ulong packedResult; if (errorCode != 0 && errorCode != Win32FileStream.ERROR_BROKEN_PIPE && errorCode != Win32FileStream.ERROR_NO_DATA) { packedResult = ((ulong)ResultError | errorCode); } else { packedResult = ((ulong)ResultSuccess | numBytes); } // Stow the result so that other threads can observe it // And, if no other thread is registering cancellation, continue if (NoResult == Interlocked.Exchange(ref completionSource._result, (long)packedResult)) { // Successfully set the state, attempt to take back the callback if (Interlocked.Exchange(ref completionSource._result, CompletedCallback) != NoResult) { // Successfully got the callback, finish the callback completionSource.CompleteCallback(packedResult); } // else: Some other thread stole the result, so now it is responsible to finish the callback } // else: Some other thread is registering a cancellation, so it *must* finish the callback }