I am using serial-rs on Linux (both Ubuntu and OpenWRT), so this mostly only applies to serial-unix.
I use the Calloop event loop for event-driven systems. Under the hood (on Linux), Calloop basically uses epoll() with whatever file descriptors it extracts from things that implement AsRawFd. The general contract of epoll() in edge-triggered mode (which is what I tend to use) is:
- wait until
epoll_await() tells you to write
- try to write N bytes from a buffer
- keep trying this until
write() returns -1 and errno is EAGAIN
- go back to
epoll_await()
This relies on the fd being opened in non-blocking mode. In blocking mode, you will never get EAGAIN, write() will simply block until all bytes are written.
In tty.rs you do indeed open the fd as non-blocking. But very shortly after, you clear this flag. This makes it impossible to use serial and enjoy non-blocking writes.
So for example, if I try to write N bytes from a buffer, this call eventually comes down to your implementation of io::Write. You call ppoll() with the timeout and then attempt the write. But even if the timeout is zero, the write will potentially block until all bytes are written.
The workaround we have for this at the moment is to write one byte at a time. That way any time spent blocking on write() is quite short and potentially, but not always, detected beforehand by ppoll().
Versions: Rust 1.53, serial 0.4.0,
Tried on: Ubuntu 20.10 and 21.04, OpenWRT (ramips + musl).
I am using
serial-rson Linux (both Ubuntu and OpenWRT), so this mostly only applies toserial-unix.I use the Calloop event loop for event-driven systems. Under the hood (on Linux), Calloop basically uses
epoll()with whatever file descriptors it extracts from things that implementAsRawFd. The general contract ofepoll()in edge-triggered mode (which is what I tend to use) is:epoll_await()tells you to writewrite()returns-1and errno isEAGAINepoll_await()This relies on the fd being opened in non-blocking mode. In blocking mode, you will never get
EAGAIN,write()will simply block until all bytes are written.In
tty.rsyou do indeed open the fd as non-blocking. But very shortly after, you clear this flag. This makes it impossible to useserialand enjoy non-blocking writes.So for example, if I try to write N bytes from a buffer, this call eventually comes down to your implementation of
io::Write. You callppoll()with the timeout and then attempt the write. But even if the timeout is zero, the write will potentially block until all bytes are written.The workaround we have for this at the moment is to write one byte at a time. That way any time spent blocking on
write()is quite short and potentially, but not always, detected beforehand byppoll().Versions: Rust 1.53, serial 0.4.0,
Tried on: Ubuntu 20.10 and 21.04, OpenWRT (ramips + musl).