private unsafe int ReadCoreWithCancellation(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { DebugAssertReadWriteArgs(buffer, offset, count, _handle); Debug.Assert(cancellationToken.CanBeCanceled, "ReadCoreNoCancellation should be used if cancellation can't happen"); // Register for a cancellation request. This will throw if cancellation has already been requested, // and otherwise will write to the cancellation pipe if/when cancellation has been requested. using (DescriptorCancellationRegistration cancellation = RegisterForCancellation(cancellationToken)) { bool gotRef = false; try { cancellation.Poll.DangerousAddRef(ref gotRef); fixed (byte* bufPtr = buffer) { const int CancellationSentinel = -42; int rv = (int)SysCall(_handle, (fd, ptr, len, cancellationFd) => { // Wait for data to be available on either the pipe we want to read from // or on the cancellation pipe, which would signal a cancellation request. Interop.libc.pollfd* fds = stackalloc Interop.libc.pollfd[2]; fds[0] = new Interop.libc.pollfd { fd = fd, events = Interop.libc.PollFlags.POLLIN, revents = 0 }; fds[1] = new Interop.libc.pollfd { fd = (int)cancellationFd, events = Interop.libc.PollFlags.POLLIN, revents = 0 }; while (Interop.CheckIo(Interop.libc.poll(fds, 2, -1))) ; // If we woke up because of a cancellation request, bail. if ((fds[1].revents & Interop.libc.PollFlags.POLLIN) != 0) { return CancellationSentinel; } // Otherwise, we woke up because data is available on the pipe. Read it. Debug.Assert((fds[0].revents & Interop.libc.PollFlags.POLLIN) != 0); long result = (long)Interop.libc.read(fd, (byte*)ptr, (IntPtr)len); Debug.Assert(result <= len); return result; }, (IntPtr)(bufPtr + offset), count, cancellation.Poll.DangerousGetHandle()); Debug.Assert(rv >= 0 || rv == CancellationSentinel); // If cancellation was requested, waking up the read, throw. if (rv == CancellationSentinel) { Debug.Assert(cancellationToken.IsCancellationRequested); throw new OperationCanceledException(cancellationToken); } // Otherwise return what we read. return rv; } } finally { if (gotRef) cancellation.Poll.DangerousRelease(); } } }
private unsafe int ReadCoreWithCancellation(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { DebugAssertReadWriteArgs(buffer, offset, count, _handle); Debug.Assert(cancellationToken.CanBeCanceled, "ReadCoreNoCancellation should be used if cancellation can't happen"); // Register for a cancellation request. This will throw if cancellation has already been requested, // and otherwise will write to the cancellation pipe if/when cancellation has been requested. using (DescriptorCancellationRegistration cancellation = RegisterForCancellation(cancellationToken)) { bool gotRef = false; try { cancellation.Poll.DangerousAddRef(ref gotRef); fixed(byte *bufPtr = buffer) { const int CancellationSentinel = -42; int rv = (int)SysCall(_handle, (fd, ptr, len, cancellationFd) => { // Wait for data to be available on either the pipe we want to read from // or on the cancellation pipe, which would signal a cancellation request. Interop.libc.pollfd *fds = stackalloc Interop.libc.pollfd[2]; fds[0] = new Interop.libc.pollfd { fd = fd, events = Interop.libc.PollFlags.POLLIN, revents = 0 }; fds[1] = new Interop.libc.pollfd { fd = (int)cancellationFd, events = Interop.libc.PollFlags.POLLIN, revents = 0 }; while (Interop.CheckIo(Interop.libc.poll(fds, 2, -1))) { ; } // If we woke up because of a cancellation request, bail. if ((fds[1].revents & Interop.libc.PollFlags.POLLIN) != 0) { return(CancellationSentinel); } // Otherwise, we woke up because data is available on the pipe. Read it. Debug.Assert((fds[0].revents & Interop.libc.PollFlags.POLLIN) != 0); long result = (long)Interop.libc.read(fd, (byte *)ptr, (IntPtr)len); Debug.Assert(result <= len); return(result); }, (IntPtr)(bufPtr + offset), count, cancellation.Poll.DangerousGetHandle()); Debug.Assert(rv >= 0 || rv == CancellationSentinel); // If cancellation was requested, waking up the read, throw. if (rv == CancellationSentinel) { Debug.Assert(cancellationToken.IsCancellationRequested); throw new OperationCanceledException(cancellationToken); } // Otherwise return what we read. return(rv); } } finally { if (gotRef) { cancellation.Poll.DangerousRelease(); } } } }