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