2 ms·
There are two ways how async IO can work: * Readiness based: You use `select` (or preferably its successors) to figure out which file descriptor has data avail
by CodesInChaos 5y ago
There are two ways how async IO can work:
* Readiness based: You use `select` (or preferably its successors) to figure out which file descriptor has data available. Then you synchronously read the data, which completes instantly because there is data available. This is the traditional way on Linux, and also the way Rust's async works. This approach doesn't run into any lifetime issues, because you only need to provide the memory (`&mut [u8]`) to a sync call once data is available. This approach works well from sequential streams, like sockets or pipes, but isn't a natural match for random access file IO.
* Completion based: You trigger an IO operation specifying the target memory. The system notifies you when it completes. This approach needs you to keep the memory available until the operation finishes. This is the traditional way on Windows (IO completion ports) and also used by the more recent io_uring on Linux. This one runs into the lifetime issues that caused the bug in the linked article. Thus in Rust it can't safely be used with a `&mut [u8]`, you have to transfer ownership of the buffer to the IO library, so it can keep it alive until the operation completes. The ringbahn crate is an example of this approach in Rust.
- yxhuvud 5y agoGood writeup. One thing to note is that Go, Crystal and other systems based on green threads tend to not run into the problem as they have separate stacks for different execution contexts.
- oshiar53-0 5y agoNote that what you're referring to as "readiness-based I/O" is not asynchronous I/O in the strictiest sense--it's actually non-blocking, synchronous I/O. I find this article helpful in clearing up confusion of asynchronous vs non-blocking I/O: http://blog.omega-prime.co.uk/?p=155 http://blog.omega-prime.co.uk/?p=155