Beispiel #1
0
        // Will wait `timeout` miliseconds or until reading or writing is possible
        // If no operation is requested it will throw
        // Returns event which has happened
        private Interop.Sys.PollEvents PollEvents(int timeout, bool pollReadEvents, bool pollWriteEvents, out Interop.ErrorInfo?error)
        {
            if (!pollReadEvents && !pollWriteEvents)
            {
                // This should not happen
                Debug.Assert(false);
                throw new Exception();
            }

            Interop.Sys.PollEvents eventsToPoll = Interop.Sys.PollEvents.POLLERR;

            if (pollReadEvents)
            {
                eventsToPoll |= Interop.Sys.PollEvents.POLLIN;
            }

            if (pollWriteEvents)
            {
                eventsToPoll |= Interop.Sys.PollEvents.POLLOUT;
            }

            Interop.Sys.PollEvents events = Interop.Sys.PollEvents.POLLNONE;
            Interop.Error          ret    = Interop.Sys.Poll(
                _handle,
                eventsToPoll,
                timeout,
                out events);

            error = ret != Interop.Error.SUCCESS ? Interop.Sys.GetLastErrorInfo() : (Interop.ErrorInfo?)null;
            return(events);
        }
        internal unsafe int Read(byte[] array, int offset, int count, int timeout)
        {
            if (_handle == null)
            {
                throw new ObjectDisposedException(SR.Port_not_open);
            }
            if (array == null)
            {
                throw new ArgumentNullException(nameof(array));
            }
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNumRequired);
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNumRequired);
            }
            if (array.Length - offset < count)
            {
                throw new ArgumentException(SR.Argument_InvalidOffLen);
            }
            if (count == 0)
            {
                return(0);            // return immediately if no bytes requested; no need for overhead.
            }
            if (timeout != 0)
            {
                Interop.Sys.PollEvents events = Interop.Sys.PollEvents.POLLNONE;
                Interop.Sys.Poll(_handle, Interop.Sys.PollEvents.POLLIN | Interop.Sys.PollEvents.POLLERR, timeout, out events);

                if ((events & (Interop.Sys.PollEvents.POLLERR | Interop.Sys.PollEvents.POLLNVAL)) != 0)
                {
                    throw new IOException();
                }

                if ((events & Interop.Sys.PollEvents.POLLIN) == 0)
                {
                    throw new TimeoutException();
                }
            }

            int numBytes;

            fixed(byte *bufPtr = array)
            {
                numBytes = Interop.Sys.Read(_handle, bufPtr + offset, count);
            }

            if (numBytes < 0)
            {
                if (Interop.Error.EWOULDBLOCK == Interop.Sys.GetLastError())
                {
                    throw new TimeoutException();
                }
                throw new IOException();
            }

            return(numBytes);
        }
Beispiel #3
0
        private unsafe void IOLoop()
        {
            bool eofReceived = false;
            // we do not care about bytes we got before - only about changes
            // loop just got started which means we just got request
            bool lastIsIdle           = false;
            int  ticksWhenIdleStarted = 0;

            while (IsOpen && !eofReceived && !_ioLoopFinished)
            {
                bool hasPendingReads  = !_readQueue.IsEmpty;
                bool hasPendingWrites = !_writeQueue.IsEmpty;

                bool hasPendingIO = hasPendingReads || hasPendingWrites;
                bool isIdle       = _dataReceived == null && !hasPendingIO;

                if (!hasPendingIO)
                {
                    if (isIdle)
                    {
                        if (!lastIsIdle)
                        {
                            // we've just started idling
                            ticksWhenIdleStarted = Environment.TickCount;
                        }
                        else if (Environment.TickCount - ticksWhenIdleStarted > IOLoopIdleTimeout)
                        {
                            // we are already idling for a while
                            // let's stop the loop until there is some work to do

                            lock (_ioLoopLock)
                            {
                                // double check we are done under lock
                                if (_dataReceived == null && _readQueue.IsEmpty && _writeQueue.IsEmpty)
                                {
                                    _ioLoop = null;
                                    break;
                                }
                                else
                                {
                                    // to make sure timer restarts
                                    lastIsIdle = false;
                                    continue;
                                }
                            }
                        }
                    }

                    Thread.Sleep(1);
                }
                else
                {
                    Interop.Sys.PollEvents events = PollEvents(1,
                                                               pollReadEvents: hasPendingReads,
                                                               pollWriteEvents: hasPendingWrites,
                                                               out Interop.ErrorInfo? error);

                    if (error.HasValue)
                    {
                        FinishPendingIORequests(error);
                        break;
                    }

                    if (events.HasFlag(Interop.Sys.PollEvents.POLLNVAL) ||
                        events.HasFlag(Interop.Sys.PollEvents.POLLERR))
                    {
                        // bad descriptor or some other error we can't handle
                        FinishPendingIORequests();
                        break;
                    }

                    if (events.HasFlag(Interop.Sys.PollEvents.POLLIN))
                    {
                        int bytesRead = DoIORequest(_readQueue, _processReadDelegate);
                        _totalBytesRead += bytesRead;
                    }

                    if (events.HasFlag(Interop.Sys.PollEvents.POLLOUT))
                    {
                        DoIORequest(_writeQueue, _processWriteDelegate);
                    }
                }

                // check if there is any new data (either already read or in the driver input)
                // this event is private and handled inside of SerialPort
                // which then throttles it with the threshold
                long totalBytesAvailable = TotalBytesAvailable;
                if (totalBytesAvailable > _lastTotalBytesAvailable)
                {
                    _lastTotalBytesAvailable = totalBytesAvailable;
                    RaiseDataReceivedChars();
                }

                lastIsIdle = isIdle;
            }
        }
        internal unsafe void Write(byte[] array, int offset, int count, int timeout)
        {
            if (_inBreak)
            {
                throw new InvalidOperationException(SR.In_Break_State);
            }
            if (array == null)
            {
                throw new ArgumentNullException(nameof(array));
            }
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedPosNum);
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedPosNum);
            }
            if (count == 0)
            {
                return;             // no need to expend overhead in creating asyncResult, etc.
            }
            if (array.Length - offset < count)
            {
                throw new ArgumentException(SR.Argument_InvalidOffLen);
            }

            // check for open handle, though the port is always supposed to be open
            if (_handle == null)
            {
                throw new ObjectDisposedException(SR.Port_not_open);
            }

            int numBytes = 0;

            while (count > 0)
            {
                if (timeout > 0)
                {
                    Interop.Sys.PollEvents events = Interop.Sys.PollEvents.POLLNONE;
                    Interop.Sys.Poll(_handle, Interop.Sys.PollEvents.POLLOUT | Interop.Sys.PollEvents.POLLERR, timeout, out events);

                    if ((events & (Interop.Sys.PollEvents.POLLERR | Interop.Sys.PollEvents.POLLNVAL)) != 0)
                    {
                        throw new IOException();
                    }

                    if ((events & Interop.Sys.PollEvents.POLLOUT) == 0)
                    {
                        throw new TimeoutException(SR.Write_timed_out);
                    }
                }

                fixed(byte *bufPtr = array)
                {
                    numBytes = Interop.Sys.Write(_handle, bufPtr + offset, count);
                }

                if (numBytes == -1)
                {
                    throw new IOException();
                }

                if (numBytes == 0)
                {
                    throw new TimeoutException(SR.Write_timed_out);
                }
                count  -= numBytes;
                offset += numBytes;
            }
        }
        private unsafe void IOLoop()
        {
            bool eofReceived = false;
            // we do not care about bytes we got before - only about changes
            // loop just got started which means we just got request
            bool lastIsIdle           = false;
            int  ticksWhenIdleStarted = 0;

            Signals lastSignals = _pinChanged != null?Interop.Termios.TermiosGetAllSignals(_handle) : Signals.Error;

            bool IsNoEventRegistered() => _dataReceived == null && _pinChanged == null;

            while (IsOpen && !eofReceived && !_ioLoopFinished)
            {
                bool hasPendingReads  = !_readQueue.IsEmpty;
                bool hasPendingWrites = !_writeQueue.IsEmpty;

                bool hasPendingIO = hasPendingReads || hasPendingWrites;
                bool isIdle       = IsNoEventRegistered() && !hasPendingIO;

                if (!hasPendingIO)
                {
                    if (isIdle)
                    {
                        if (!lastIsIdle)
                        {
                            // we've just started idling
                            ticksWhenIdleStarted = Environment.TickCount;
                        }
                        else if (Environment.TickCount - ticksWhenIdleStarted > IOLoopIdleTimeout)
                        {
                            // we are already idling for a while
                            // let's stop the loop until there is some work to do

                            lock (_ioLoopLock)
                            {
                                // double check we are done under lock
                                if (IsNoEventRegistered() && _readQueue.IsEmpty && _writeQueue.IsEmpty)
                                {
                                    _ioLoop = null;
                                    break;
                                }
                                else
                                {
                                    // to make sure timer restarts
                                    lastIsIdle = false;
                                    continue;
                                }
                            }
                        }
                    }

                    Thread.Sleep(1);
                }
                else
                {
                    Interop.Sys.PollEvents events = PollEvents(1,
                                                               pollReadEvents: hasPendingReads,
                                                               pollWriteEvents: hasPendingWrites,
                                                               out Interop.ErrorInfo? error);

                    if (error.HasValue)
                    {
                        FinishPendingIORequests(error);
                        break;
                    }

                    if (events.HasFlag(Interop.Sys.PollEvents.POLLNVAL) ||
                        events.HasFlag(Interop.Sys.PollEvents.POLLERR))
                    {
                        // bad descriptor or some other error we can't handle
                        FinishPendingIORequests();
                        break;
                    }

                    if (events.HasFlag(Interop.Sys.PollEvents.POLLIN))
                    {
                        int bytesRead = DoIORequest(_readQueue, _processReadDelegate);
                        _totalBytesRead += bytesRead;
                    }

                    if (events.HasFlag(Interop.Sys.PollEvents.POLLOUT))
                    {
                        DoIORequest(_writeQueue, _processWriteDelegate);
                    }
                }

                // check if there is any new data (either already read or in the driver input)
                // this event is private and handled inside of SerialPort
                // which then throttles it with the threshold
                long totalBytesAvailable = TotalBytesAvailable;
                if (totalBytesAvailable > _lastTotalBytesAvailable)
                {
                    _lastTotalBytesAvailable = totalBytesAvailable;
                    RaiseDataReceivedChars();
                }

                if (_pinChanged != null)
                {
                    // Checking for changes could technically speaking be done by waiting with ioctl+TIOCMIWAIT
                    // This would require spinning new thread and also would potentially trigger events when
                    // user didn't have time to respond.
                    // Diffing seems like a better solution.
                    Signals current = Interop.Termios.TermiosGetAllSignals(_handle);

                    // There is no really good action we can take when this errors so just ignore
                    // a sinle event.
                    if (current != Signals.Error && lastSignals != Signals.Error)
                    {
                        Signals changed = current ^ lastSignals;
                        if (changed != Signals.None)
                        {
                            SerialPinChange pinChanged = SignalsToPinChanges(changed);
                            RaisePinChanged(pinChanged);
                        }
                    }

                    lastSignals = current;
                }

                lastIsIdle = isIdle;
            }
        }