Skip to content

Commit

Permalink
SerialPortStream: BytesToWrite returns maximum of driver or internal …
Browse files Browse the repository at this point in the history
…buffers, not the sum

On Windows, the internal Overlapped I/O when writing may only return when the driver itself has actually sent the number of bytes to the hardware. This results in bytes being added twice.

Thus, we only provide the number of bytes from the driver or the internal buffer, whichever is larger.

Issue: DOTNET-425
  • Loading branch information
jcurl committed Oct 3, 2021
1 parent 73c763b commit fbedb30
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Bugfixes
* DOTNET-422: (Linux only) DiscardOutBuffer now clears the buffers
* DOTNET-423: (Linux only) Fix internal race condition when reading/writing
data that could lead to data corruption
* DOTNET-425: BytesToWrite tries not to count bytes twice

Source

Expand Down
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enhances portability and fixes bugs. See the end of these notes for differences.
* 7.1 Windows
* 7.1.1 Driver Specific Issues on Windows
* 7.1.1.1 Flow Control
* 7.1.1.2 BytesToWrite
* 7.2 Linux
* 7.2.1 Mono on non-Windows Platforms
* 7.2.2 Driver Specific Issues on Linux
Expand Down Expand Up @@ -296,6 +297,57 @@ test case now fails. This problem is not observable with com0com 3.0. You can
see the effect in logs, there is a TX-EMPTY event that occurs, which should
never be there if no data is ever sent.

##### 7.1.1.2 BytesToWrite

On Windows, the SerialPortStream returns the bigger of either the internal
write buffer, or the amount of data in the output queue of the driver. Drivers
don't report the number of bytes that are in the output queue before the next
write begins, and may return sooner. This leads to the effects:

###### CP2101 Driver

This driver indicates more bytes are in the output queue than what it will
return from the current ongoing write operation. This can cause some jumps
in the returned value.

[CP210x Universal Windows Driver](https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers)
v10.1.10 1/13/2021.

For example:

```text
BytesToWrite = 40960 (driver 12288)
RJCP.IO.Ports.SerialPortStream Verbose: 0 : COM5: SerialThread: ProcessWriteEvent: 1024 bytes
BytesToWrite = 40412 (driver 40412)
RJCP.IO.Ports.SerialPortStream Verbose: 0 : COM5: SerialThread: DoWriteEvent: WriteFile(736, 312385272, 39936, ...) == False
BytesToWrite = 40387 (driver 40387)
BytesToWrite = 40386 (driver 40386)
```

The internal buffer is 40kB, the driver returned it wrote 1024 bytes, but the
queue still has 40412 bytes (which is more than the 39936 bytes it should be).

It can also fluctuate without writes without calls to the OS in between.

```text
BytesToWrite = 40418 (driver 40418)
BytesToWrite = 40393 (driver 40393)
BytesToWrite = 40392 (driver 40392)
BytesToWrite = 40391 (driver 40391)
BytesToWrite = 40390 (driver 40390)
BytesToWrite = 40389 (driver 40389)
BytesToWrite = 40428 (driver 40428)
BytesToWrite = 40427 (driver 40427)
BytesToWrite = 40426 (driver 40426)
```

###### PL2303 RA

Generally this driver reports that it has zero bytes in the output queue, but
may sometimes report the number of bytes in the last `WriteFile()` call. This
is not a problem, but the number of bytes in the output queue is less than what
is still to be written, so a user may think it is complete, when it is not.

### 7.2 Linux

SerialPortStream was tested on Ubuntu 14.04 and Ubuntu 16.04. Feedback welcome
Expand Down
4 changes: 3 additions & 1 deletion code/SerialPortStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1184,7 +1184,9 @@ public int BytesToWrite
if (IsDisposed) throw new ObjectDisposedException(nameof(SerialPortStream));
if (m_Buffer == null) return 0;
lock (m_Buffer.WriteLock) {
return m_Buffer.Stream.BytesToWrite + m_NativeSerial.BytesToWrite;
int bytesToWrite = m_Buffer.WriteStream.BytesToWrite;
int driverBytes = m_NativeSerial.BytesToWrite;
return bytesToWrite > driverBytes ? bytesToWrite : driverBytes;
}
}
}
Expand Down

0 comments on commit fbedb30

Please sign in to comment.