// 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.PollEvents PollEvents(int timeout, bool pollReadEvents, bool pollWriteEvents, out Interop.ErrorInfo?error) { if (!pollReadEvents && !pollWriteEvents) { Debug.Fail("This should not happen"); throw new Exception(); } Interop.PollEvents eventsToPoll = Interop.PollEvents.POLLERR; if (pollReadEvents) { eventsToPoll |= Interop.PollEvents.POLLIN; } if (pollWriteEvents) { eventsToPoll |= Interop.PollEvents.POLLOUT; } Interop.PollEvents events = Interop.PollEvents.POLLNONE; Interop.Error ret = Interop.Serial.Poll( _handle, eventsToPoll, timeout, out events); error = ret != Interop.Error.SUCCESS ? Interop.Sys.GetLastErrorInfo() : (Interop.ErrorInfo?)null; return(events); }
private 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.PollEvents events = PollEvents(1, pollReadEvents: hasPendingReads, pollWriteEvents: hasPendingWrites, out Interop.ErrorInfo? error); if (error.HasValue) { FinishPendingIORequests(error); break; } if (events.HasFlag(Interop.PollEvents.POLLNVAL) || events.HasFlag(Interop.PollEvents.POLLERR)) { // bad descriptor or some other error we can't handle FinishPendingIORequests(); break; } if (events.HasFlag(Interop.PollEvents.POLLIN)) { int bytesRead = DoIORequest(_readQueue, _processReadDelegate); _totalBytesRead += bytesRead; } if (events.HasFlag(Interop.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; } }