Esempio n. 1
0
        private static unsafe void AddToPollArray(Interop.Sys.PollEvent* arr, int arrLength, IList socketList, ref int arrOffset, Interop.Sys.PollEvents events)
        {
            if (socketList == null)
                return;

            int listCount = socketList.Count;
            for (int i = 0; i < listCount; i++)
            {
                if (arrOffset >= arrLength)
                {
                    Debug.Fail("IList.Count must have been faulty, returning a negative value and/or returning a different value across calls.");
                    throw new ArgumentOutOfRangeException(nameof(socketList));
                }

                Socket socket = socketList[i] as Socket;
                if (socket == null)
                {
                    throw new ArgumentException(SR.Format(SR.net_sockets_select, socket?.GetType().FullName ?? "null", typeof(Socket).FullName));
                }

                int fd = (int)socket.SafeHandle.DangerousGetHandle();
                arr[arrOffset++] = new Interop.Sys.PollEvent { Events = events, FileDescriptor = fd };
            }
        }
Esempio n. 2
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();
                    }
                }
            }
        }
Esempio n. 3
0
        public static unsafe SocketError Select(IList checkRead, IList checkWrite, IList checkError, int microseconds)
        {
            int checkReadInitialCount = checkRead != null ? checkRead.Count : 0;
            int checkWriteInitialCount = checkWrite != null ? checkWrite.Count : 0;
            int checkErrorInitialCount = checkError != null ? checkError.Count : 0;
            int count = checked(checkReadInitialCount + checkWriteInitialCount + checkErrorInitialCount);
            Debug.Assert(count > 0, $"Expected at least one entry.");

            // Rather than using the select syscall, we use poll.  While this has a mismatch in API from Select and
            // requires some translation, it avoids the significant limitation of select only working with file descriptors
            // less than FD_SETSIZE, and thus failing arbitrarily depending on the file descriptor value assigned
            // by the system.  Since poll then expects an array of entries, we try to allocate the array on the stack,
            // only falling back to allocating it on the heap if it's deemed too big.

            const int StackThreshold = 80; // arbitrary limit to avoid too much space on stack
            if (count < StackThreshold)
            {
                Interop.Sys.PollEvent* eventsOnStack = stackalloc Interop.Sys.PollEvent[count];
                return SelectViaPoll(
                    checkRead, checkReadInitialCount,
                    checkWrite, checkWriteInitialCount,
                    checkError, checkErrorInitialCount,
                    eventsOnStack, count, microseconds);
            }
            else
            {
                var eventsOnHeap = new Interop.Sys.PollEvent[count];
                fixed (Interop.Sys.PollEvent* eventsOnHeapPtr = eventsOnHeap)
                {
                    return SelectViaPoll(
                        checkRead, checkReadInitialCount,
                        checkWrite, checkWriteInitialCount,
                        checkError, checkErrorInitialCount,
                        eventsOnHeapPtr, count, microseconds);
                }
            }
        }
Esempio n. 4
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.
                                Debug.Assert((events[0].TriggeredEvents & Interop.Sys.PollEvents.POLLIN) != 0, "Expected revents on read fd to have POLLIN set");
                                break;
                            }
                        }

                        // Read it.
                        Debug.Assert((events[0].TriggeredEvents & Interop.Sys.PollEvents.POLLIN) != 0);
                        int result = CheckPipeCall(Interop.Sys.Read(_handle, bufPtr + offset, count));
                        Debug.Assert(result <= count);

                        Debug.Assert(result >= 0);

                        // return what we read.
                        return result;
                    }
                }
                finally
                {
                    if (gotRef)
                        cancellation.Poll.DangerousRelease();
                }
            }
        }