Doing asynnchronous serial

Hello, I'm trying to do some async serial. I've seen futures, but I'm not sure on how to proceed. Should I create my own async serial crate, or can I use something that has already been implemented?

If I need to create a crate, I guess I need to use mio::unix::EventedFd and then create some sort of wrapper using Futures, right?

You're on the right track! I'm assuming that you're working with serial as a custom file descriptor, and you're wondering how to get that custom file descriptor (set to async mode) hooked up to futures and everything. To do that, you'd follow steps that look like:

  1. Verify your file descriptor works with epoll (assuming you're on Linux)
  2. Create a type and implement Evented for it. This will likely use EventedFd internally, probably like so. Remember that if you're passing ownership of the file descriptor to this type implementing Evented that you'll want to implement a destructor that closes it.
  3. When working with tokio-core, you'll want to then pass this type that implements Evented to the PollEvented type. This is what hooks up your file descriptor to the tokio-core event loop.
  4. If you interact with the file descriptor via read and write then you're done! The Read and Write trait implementations on PollEvented are all you'll need.
  5. If you need functionality other than read and write, then you'll need to implement your own methods that call poll_read, poll_write, need_read, and need_write manually (all methods on PollEvented). You can see an example of this in UdpSocket::recv_from.

Hope that helps!

8 Likes

Thanks Alex!!

I'm actually porting the code from the serial crate. I only need it for Linux (Raspberry Pi), so no need to port the Windows part. I've seen that the descriptor that is using is opened like this. It uses some flags (O_RDWR | O_NOCTTY | O_NONBLOCK) and then removes the O_NONBLOCK flag. Should the descriptor be opened the same way for async?

I would like to have an API so that I can do something like this:

let mut l = Core::new().unwrap();
let handle = l.handle();

let mut serial = AsyncSerial::open("/dev/tty1", Baud9600, &handle).unwrap();

Then, I would like to be able to use the serial so that when I can iterate through the data. Something like, when new data comes, it runs a function to convert that variable-length data to a Request, and computes a Response that is written back to the serial. I'm still not very familiar with async, but I've seen that with futures, processing can be chained, and in fact, I've seen that it should be possible to buffer Requests to process them in parallel.

Is there a trait I should be implementing to enable that functionality? Also, is there a "preferred" way to do it? don't think that using Read and Write directly would solve it.

The reason that the serial port is opened non-blocking is to cover the case
where the serial port was programmed to wait for carrier. If that's the
case and you open the serial port BLOCKING, then the open won't continue
until the carrier detect signal is detected (not all drivers necesarily
implement this).

By opening the serial port non-blocking you'll be able to open it
regardless of whether carrier detect is present or not. You can then leave
it non-blocking or switch it back to blocking, depending on how you want
the reads/writes to behave.

1 Like

From what @dhylands said sounds like opening nonblocking is good to keep, and then to use with async you'd probably just want to ensure it's in nonblocking mode before handing it off to tokio-core.

You should be able to use Io::framed I believe to take the file descriptor to a stream of requests and provide a way to write out responses.

If that doesn't work out though let me know!

I guess I need to implement std::io::Read and std::io::Write for that to work, right? Any particular way to implemented, or should I simply use libc::read()?

Oh yeah if you want to use Io::framed you'll have to implement Read and Write. That's just one option, though. You may want to also take a look at UdpSocket::framed which is similar but doesn't rely on the Read/Write traits.