Skip to content

Support non-blocking operation #64

@detly

Description

@detly

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions