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; } }
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; } }