// 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
        }
예제 #2
0
            private static void Cancel(object state)
            {
                // WARNING: This may potentially be called under a lock (during cancellation registration)

                FileStreamCompletionSource completionSource = state as FileStreamCompletionSource;

                Debug.Assert(completionSource != null, "Unknown state passed to cancellation");
                Debug.Assert(completionSource._overlapped != null && !completionSource.Task.IsCompleted, "IO should not have completed yet");

                // If the handle is still valid, attempt to cancel the IO
                if ((!completionSource._handle.IsInvalid) && (!Interop.mincore.CancelIoEx(completionSource._handle, completionSource._overlapped)))
                {
                    int errorCode = Marshal.GetLastWin32Error();

                    // ERROR_NOT_FOUND is returned if CancelIoEx cannot find the request to cancel.
                    // This probably means that the IO operation has completed.
                    if (errorCode != Interop.mincore.Errors.ERROR_NOT_FOUND)
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode);
                    }
                }
            }
예제 #3
0
            // 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
            }
        private unsafe Task WriteAsyncInternalCore(ReadOnlyMemory <byte> source, CancellationToken cancellationToken)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            // Create and store async stream class library specific data in the async result
            FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, source);
            NativeOverlapped *         intOverlapped    = completionSource.Overlapped;

            if (CanSeek)
            {
                // Make sure we set the length of the file appropriately.
                long len = Length;

                // Make sure we are writing to the position that we think we are
                VerifyOSHandlePosition();

                if (_filePosition + source.Length > len)
                {
                    SetLengthCore(_filePosition + source.Length);
                }

                // Now set the position to read from in the NativeOverlapped struct
                // For pipes, we should leave the offset fields set to 0.
                intOverlapped->OffsetLow  = (int)_filePosition;
                intOverlapped->OffsetHigh = (int)(_filePosition >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves.  This isn't threadsafe.
                SeekCore(_fileHandle, source.Length, SeekOrigin.Current);
            }

            // queue an async WriteFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.WriteFileNative(_fileHandle, source.Span, intOverlapped, out int errorCode);

            // WriteFile, the OS version, will return 0 on failure.  But
            // my WriteFileNative wrapper returns -1.  My wrapper will return
            // the following:
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
            // On async requests that completed sequentially, r==0
            // You will NEVER RELIABLY be able to get the number of bytes
            // written back from this call when using overlapped IO!  You must
            // not pass in a non-null lpNumBytesWritten to WriteFile when using
            // overlapped structures!  This is ByDesign NT behavior.
            if (r == -1)
            {
                // For pipes, when they are closed on the other side, they will come here.
                if (errorCode == ERROR_NO_DATA)
                {
                    // Not an error, but EOF. AsyncFSCallback will NOT be called.
                    // Completing TCS and return cached task allowing the GC to collect TCS.
                    completionSource.SetCompletedSynchronously(0);
                    return(Task.CompletedTask);
                }
                else if (errorCode != ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        SeekCore(_fileHandle, 0, SeekOrigin.Current);
                    }

                    completionSource.ReleaseNativeResource();

                    if (errorCode == ERROR_HANDLE_EOF)
                    {
                        ThrowHelper.ThrowEndOfFileException();
                    }
                    else
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                    }
                }
                else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
                {
                    // Only once the IO is pending do we register for cancellation
                    completionSource.RegisterForCancellation(cancellationToken);
                }
            }
            else
            {
                // Due to a workaround for a race condition in NT's ReadFile &
                // WriteFile routines, we will always be returning 0 from WriteFileNative
                // when we do async IO instead of the number of bytes written,
                // irregardless of whether the operation completed
                // synchronously or asynchronously.  We absolutely must not
                // set asyncResult._numBytes here, since will never have correct
                // results.
            }

            return(completionSource.Task);
        }
        private unsafe Task <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default)
        {
            if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }

            Debug.Assert(!_fileHandle.IsClosed, "!_handle.IsClosed");

            // Create and store async stream class library specific data in the async result
            FileStreamCompletionSource completionSource = FileStreamCompletionSource.Create(this, _preallocatedOverlapped, 0, destination);
            NativeOverlapped *         intOverlapped    = completionSource.Overlapped;

            // Calculate position in the file we should be at after the read is done
            if (CanSeek)
            {
                long len = Length;

                // Make sure we are reading from the position that we think we are
                VerifyOSHandlePosition();

                if (destination.Length > len - _filePosition)
                {
                    if (_filePosition <= len)
                    {
                        destination = destination.Slice(0, (int)(len - _filePosition));
                    }
                    else
                    {
                        destination = default;
                    }
                }

                // Now set the position to read from in the NativeOverlapped struct
                // For pipes, we should leave the offset fields set to 0.
                intOverlapped->OffsetLow  = unchecked ((int)_filePosition);
                intOverlapped->OffsetHigh = (int)(_filePosition >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves. This isn't threadsafe.

                // WriteFile should not update the file pointer when writing
                // in overlapped mode, according to MSDN.  But it does update
                // the file pointer when writing to a UNC path!
                // So changed the code below to seek to an absolute
                // location, not a relative one.  ReadFile seems consistent though.
                SeekCore(_fileHandle, destination.Length, SeekOrigin.Current);
            }

            // queue an async ReadFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination.Span, intOverlapped, out int errorCode);

            // ReadFile, the OS version, will return 0 on failure.  But
            // my ReadFileNative wrapper returns -1.  My wrapper will return
            // the following:
            // On error, r==-1.
            // On async requests that are still pending, r==-1 w/ errorCode==ERROR_IO_PENDING
            // on async requests that completed sequentially, r==0
            // You will NEVER RELIABLY be able to get the number of bytes
            // read back from this call when using overlapped structures!  You must
            // not pass in a non-null lpNumBytesRead to ReadFile when using
            // overlapped structures!  This is by design NT behavior.
            if (r == -1)
            {
                // For pipes, when they hit EOF, they will come here.
                if (errorCode == ERROR_BROKEN_PIPE)
                {
                    // Not an error, but EOF.  AsyncFSCallback will NOT be
                    // called.  Call the user callback here.

                    // We clear the overlapped status bit for this special case.
                    // Failure to do so looks like we are freeing a pending overlapped later.
                    intOverlapped->InternalLow = IntPtr.Zero;
                    completionSource.SetCompletedSynchronously(0);
                }
                else if (errorCode != ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        SeekCore(_fileHandle, 0, SeekOrigin.Current);
                    }

                    completionSource.ReleaseNativeResource();

                    if (errorCode == ERROR_HANDLE_EOF)
                    {
                        ThrowHelper.ThrowEndOfFileException();
                    }
                    else
                    {
                        throw Win32Marshal.GetExceptionForWin32Error(errorCode, _path);
                    }
                }
                else if (cancellationToken.CanBeCanceled) // ERROR_IO_PENDING
                {
                    // Only once the IO is pending do we register for cancellation
                    completionSource.RegisterForCancellation(cancellationToken);
                }
            }
            else
            {
                // Due to a workaround for a race condition in NT's ReadFile &
                // WriteFile routines, we will always be returning 0 from ReadFileNative
                // when we do async IO instead of the number of bytes read,
                // irregardless of whether the operation completed
                // synchronously or asynchronously.  We absolutely must not
                // set asyncResult._numBytes here, since will never have correct
                // results.
            }

            return(completionSource.Task);
        }