Beispiel #1
0
        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)
                    {
                        // We want to 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.Sys.PollEvent *events = stackalloc Interop.Sys.PollEvent[2];
                        events[0] = new Interop.Sys.PollEvent
                        {
                            FileDescriptor = (int)_handle.DangerousGetHandle(),
                            Events         = Interop.Sys.PollEvents.POLLIN
                        };
                        events[1] = new Interop.Sys.PollEvent
                        {
                            FileDescriptor = (int)cancellation.Poll.DangerousGetHandle(),
                            Events         = Interop.Sys.PollEvents.POLLIN
                        };

                        // Some systems (at least OS X) appear to have a race condition in poll with FIFOs where the poll can
                        // end up not noticing writes of greater than the internal buffer size.  Restarting the poll causes it
                        // to notice. To deal with that, we loop around poll, first starting with a small timeout and backing off
                        // to a larger one.  This ensures we'll at least eventually notice such changes in these corner
                        // cases, while not adding too much overhead on systems that don't suffer from the problem.
                        const int InitialMsTimeout = 30, MaxMsTimeout = 2000;

                        for (int timeout = InitialMsTimeout; ; timeout = Math.Min(timeout * 2, MaxMsTimeout))
                        {
                            // Do the poll.
                            uint signaledFdCount;
                            Interop.CheckIo(Interop.Sys.Poll(events, 2, timeout, &signaledFdCount));

                            cancellationToken.ThrowIfCancellationRequested();

                            if (signaledFdCount != 0)
                            {
                                // Our pipe is ready.  Break out of the loop to read from it.  The fd may have been signaled due to
                                // POLLIN (data available), POLLHUP (hang-up), POLLERR (some error on the stream), etc... any such
                                // data will be propagated to us when we do the actual read.
                                break;
                            }
                        }

                        // Read it.
                        int result = CheckPipeCall(Interop.Sys.Read(_handle, bufPtr + offset, count));

                        Debug.Assert(result >= 0 && result <= count, "Expected 0 <= result <= count bytes, got " + result);

                        // return what we read.
                        return(result);
                    }
                }
                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) =>
                        {
                            // We want to 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.Sys.PollFD *fds = stackalloc Interop.Sys.PollFD[2];
                            fds[0] = new Interop.Sys.PollFD {
                                FD = fd, Events = Interop.Sys.PollFlags.POLLIN, REvents = 0
                            };
                            fds[1] = new Interop.Sys.PollFD {
                                FD = (int)cancellationFd, Events = Interop.Sys.PollFlags.POLLIN, REvents = 0
                            };

                            // Some systems (at least OS X) appear to have a race condition in poll with FIFOs where the poll can
                            // end up not noticing writes of greater than the internal buffer size.  Restarting the poll causes it
                            // to notice. To deal with that, we loop around poll, first starting with a small timeout and backing off
                            // to a larger one.  This ensures we'll at least eventually notice such changes in these corner
                            // cases, while not adding too much overhead on systems that don't suffer from the problem.
                            const int InitialMsTimeout = 30, MaxMsTimeout = 2000;
                            for (int timeout = InitialMsTimeout; ; timeout = Math.Min(timeout * 2, MaxMsTimeout))
                            {
                                // Do the poll.
                                int signaledFdCount;
                                while (Interop.CheckIo(signaledFdCount = Interop.Sys.Poll(fds, 2, timeout)))
                                {
                                    ;
                                }
                                if (cancellationToken.IsCancellationRequested)
                                {
                                    // Cancellation occurred.  Bail by returning the cancellation sentinel.
                                    return(CancellationSentinel);
                                }
                                else if (signaledFdCount == 0)
                                {
                                    // Timeout occurred.  Loop around to poll again.
                                    continue;
                                }
                                else
                                {
                                    // Our pipe is ready.  Break out of the loop to read from it.
                                    Debug.Assert((fds[0].REvents & Interop.Sys.PollFlags.POLLIN) != 0, "Expected revents on read fd to have POLLIN set");
                                    break;
                                }
                            }

                            // Read it.
                            Debug.Assert((fds[0].REvents & Interop.Sys.PollFlags.POLLIN) != 0);
                            int result = Interop.Sys.Read(fd, (byte *)ptr, 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();
                    }
                }
            }
        }
Beispiel #3
0
        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();
                    }
                }
            }
        }