private static void IOCallback(uint errorCode, uint numBytes, NativeOverlapped *pOverlapped)
            {
                ValueTaskSource valueTaskSource = (ValueTaskSource)ThreadPoolBoundHandle.GetNativeOverlappedState(pOverlapped) !;

                Debug.Assert(valueTaskSource._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 != Interop.Errors.ERROR_BROKEN_PIPE && errorCode != Interop.Errors.ERROR_NO_DATA)
                {
                    packedResult = ((ulong)TaskSourceCodes.ResultError | errorCode);
                }
                else
                {
                    packedResult = ((ulong)TaskSourceCodes.ResultSuccess | numBytes);
                }

                // Stow the result so that other threads can observe it
                // And, if no other thread is registering cancellation, continue
                if (Interlocked.Exchange(ref valueTaskSource._result, (long)packedResult) == TaskSourceCodes.NoResult)
                {
                    // Successfully set the state, attempt to take back the callback
                    if (Interlocked.Exchange(ref valueTaskSource._result, TaskSourceCodes.CompletedCallback) != TaskSourceCodes.NoResult)
                    {
                        // Successfully got the callback, finish the callback
                        valueTaskSource.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 void TryToReuse(ValueTaskSource source)
        {
            source._source.Reset();

            if (Interlocked.CompareExchange(ref _reusableValueTaskSource, source, null) is not null)
            {
                source._preallocatedOverlapped.Dispose();
            }
        }
コード例 #3
0
        private ValueTaskCompletionSource(ValueTaskSource <T> source)
        {
            Debug.Assert(source != null);
            Debug.Assert(!source !.Exhausted);

            var token = source.Token;

            _source = source;
            _token  = token;
            Task    = new ValueTask <T>(source, token);
        }
コード例 #4
0
        public InputAsyncEnumerator(Socket socket, CancellationToken cancellationToken)
        {
            this.socket            = socket;
            this.cancellationToken = cancellationToken;

            socketEventArgs = new SocketAsyncEventArgs();
            socketEventArgs.SetBuffer();
            socketEventArgs.Completed += HandleResult;
            context = new AsyncEnumeratorContext {
                Buffer = socketEventArgs.Buffer
            };
            taskSource = new ValueTaskSource <SocketError>(cancellationToken);
        }
コード例 #5
0
        private unsafe ValueTask WriteAsyncInternal(ReadOnlyMemory <byte> source, CancellationToken cancellationToken)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

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

            // valueTaskSource is not null when:
            // - First time calling WriteAsync in buffered mode
            // - Second+ time calling WriteAsync, both buffered or unbuffered
            // - On buffered flush, when source memory is also the internal buffer
            // valueTaskSource is null when:
            // - First time calling WriteAsync in unbuffered mode
            ValueTaskSource   valueTaskSource = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);
            NativeOverlapped *intOverlapped   = valueTaskSource.Configure(source);

            long positionBefore = _filePosition;

            if (CanSeek)
            {
                // 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)positionBefore;
                intOverlapped->OffsetHigh = (int)(positionBefore >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves, but only in memory.  This isn't threadsafe.
                _filePosition += source.Length;
                UpdateLengthOnChangePosition();
            }

            // queue an async WriteFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.WriteFileNative(_fileHandle, source.Span, false, 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 == Interop.Errors.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.
                    valueTaskSource.ReleaseNativeResource();
                    TryToReuse(valueTaskSource);
                    return(ValueTask.CompletedTask);
                }
                else if (errorCode != Interop.Errors.ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        _filePosition = positionBefore;
                    }

                    valueTaskSource.ReleaseNativeResource();
                    TryToReuse(valueTaskSource);

                    if (errorCode == Interop.Errors.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
                    valueTaskSource.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(new ValueTask(valueTaskSource, valueTaskSource.Version));
        }
コード例 #6
0
        private unsafe ValueTask <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default)
        {
            if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }

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

            // valueTaskSource is not null when:
            // - First time calling ReadAsync in buffered mode
            // - Second+ time calling ReadAsync, both buffered or unbuffered
            // - On buffered flush, when source memory is also the internal buffer
            // valueTaskSource is null when:
            // - First time calling ReadAsync in unbuffered mode
            ValueTaskSource   valueTaskSource = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);
            NativeOverlapped *intOverlapped   = valueTaskSource.Configure(destination);

            // Calculate position in the file we should be at after the read is done
            long positionBefore = _filePosition;

            if (CanSeek)
            {
                long len = Length;

                if (positionBefore + destination.Length > len)
                {
                    if (positionBefore <= len)
                    {
                        destination = destination.Slice(0, (int)(len - positionBefore));
                    }
                    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)positionBefore);
                intOverlapped->OffsetHigh = (int)(positionBefore >> 32);

                // When using overlapped IO, the OS is not supposed to
                // touch the file pointer location at all.  We will adjust it
                // ourselves, but only in memory. This isn't threadsafe.
                _filePosition += destination.Length;
            }

            // queue an async ReadFile operation and pass in a packed overlapped
            int r = FileStreamHelpers.ReadFileNative(_fileHandle, destination.Span, false, 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 == Interop.Errors.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;
                    valueTaskSource.ReleaseNativeResource();
                    TryToReuse(valueTaskSource);
                    return(new ValueTask <int>(0));
                }
                else if (errorCode != Interop.Errors.ERROR_IO_PENDING)
                {
                    if (!_fileHandle.IsClosed && CanSeek)  // Update Position - It could be anywhere.
                    {
                        _filePosition = positionBefore;
                    }

                    valueTaskSource.ReleaseNativeResource();
                    TryToReuse(valueTaskSource);

                    if (errorCode == Interop.Errors.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
                    valueTaskSource.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(new ValueTask <int>(valueTaskSource, valueTaskSource.Version));
        }
コード例 #7
0
        private unsafe ValueTask WriteAsyncInternal(ReadOnlyMemory <byte> source, CancellationToken cancellationToken)
        {
            if (!CanWrite)
            {
                ThrowHelper.ThrowNotSupportedException_UnwritableStream();
            }

            // Rent the reusable ValueTaskSource, or create a new one to use if we couldn't get one (which
            // should only happen on first use or if the FileStream is being used concurrently).
            ValueTaskSource vts = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);

            try
            {
                NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(source);
                Debug.Assert(vts._memoryHandle.Pointer != null);

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

                    // When using overlapped IO, the OS is not supposed to
                    // touch the file pointer location at all.  We will adjust it
                    // ourselves, but only in memory.  This isn't threadsafe.
                    _filePosition += source.Length;
                    UpdateLengthOnChangePosition();
                }

                // Queue an async WriteFile operation.
                if (Interop.Kernel32.WriteFile(_fileHandle, (byte *)vts._memoryHandle.Pointer, source.Length, IntPtr.Zero, nativeOverlapped) == 0)
                {
                    // The operation failed, or it's pending.
                    int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(_fileHandle);
                    if (errorCode == Interop.Errors.ERROR_IO_PENDING)
                    {
                        // Common case: IO was initiated, completion will be handled by callback.
                        // Register for cancellation now that the operation has been initiated.
                        vts.RegisterForCancellation(cancellationToken);
                    }
                    else
                    {
                        // Error. Callback will not be invoked.
                        vts.Dispose();
                        return(errorCode == Interop.Errors.ERROR_NO_DATA ? // EOF on a pipe. IO callback will not be called.
                               ValueTask.CompletedTask :
                               ValueTask.FromException(HandleIOError(positionBefore, errorCode)));
                    }
                }
            }
            catch
            {
                vts.Dispose();
                throw;
            }

            // Completion handled by callback.
            vts.FinishedScheduling();
            return(new ValueTask(vts, vts.Version));
        }
コード例 #8
0
        private unsafe ValueTask <int> ReadAsyncInternal(Memory <byte> destination, CancellationToken cancellationToken = default)
        {
            if (!CanRead)
            {
                ThrowHelper.ThrowNotSupportedException_UnreadableStream();
            }

            // Rent the reusable ValueTaskSource, or create a new one to use if we couldn't get one (which
            // should only happen on first use or if the FileStream is being used concurrently).
            ValueTaskSource vts = Interlocked.Exchange(ref _reusableValueTaskSource, null) ?? new ValueTaskSource(this);

            try
            {
                NativeOverlapped *nativeOverlapped = vts.PrepareForOperation(destination);
                Debug.Assert(vts._memoryHandle.Pointer != null);

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

                    if (positionBefore + destination.Length > len)
                    {
                        destination = positionBefore <= len?
                                      destination.Slice(0, (int)(len - positionBefore)) :
                                          default;
                    }

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

                    // When using overlapped IO, the OS is not supposed to
                    // touch the file pointer location at all.  We will adjust it
                    // ourselves, but only in memory. This isn't threadsafe.
                    _filePosition += destination.Length;
                }

                // Queue an async ReadFile operation.
                if (Interop.Kernel32.ReadFile(_fileHandle, (byte *)vts._memoryHandle.Pointer, destination.Length, IntPtr.Zero, nativeOverlapped) == 0)
                {
                    // The operation failed, or it's pending.
                    int errorCode = FileStreamHelpers.GetLastWin32ErrorAndDisposeHandleIfInvalid(_fileHandle);
                    switch (errorCode)
                    {
                    case Interop.Errors.ERROR_IO_PENDING:
                        // Common case: IO was initiated, completion will be handled by callback.
                        // Register for cancellation now that the operation has been initiated.
                        vts.RegisterForCancellation(cancellationToken);
                        break;

                    case Interop.Errors.ERROR_BROKEN_PIPE:
                        // EOF on a pipe. Callback will not be called.
                        // We clear the overlapped status bit for this special case (failure
                        // to do so looks like we are freeing a pending overlapped later).
                        nativeOverlapped->InternalLow = IntPtr.Zero;
                        vts.Dispose();
                        return(ValueTask.FromResult(0));

                    default:
                        // Error. Callback will not be called.
                        vts.Dispose();
                        return(ValueTask.FromException <int>(HandleIOError(positionBefore, errorCode)));
                    }
                }
            }
            catch
            {
                vts.Dispose();
                throw;
            }

            // Completion handled by callback.
            vts.FinishedScheduling();
            return(new ValueTask <int>(vts, vts.Version));
        }