93996817 发表于 2020-08-08 22:14
polling - A simple and portable API for epoll/kqueue/wepoll so we don't have to write boring OS-specific code. Basically, this crate allows you to tell the OS "here's a bunch of RawFds, wake me up when one of them becomes readable or writable". async-io - Suppose we have a TcpStream and want to do stream.read(buf).await. How does that work? First we attempt a read operation - then, if the socket is not readable yet, we tell the OS to notify us when it becomes readable by waking the Waker associated with the current task, at which point we try again. That's exactly what async-io does - it provides a nice async API for the standard networking types. It talks to the OS using the polling crate. blocking - Some things don't work with epoll/kqueue/wepoll, but we'd like to interact with them in an async manner. This crate allows you to move a piece of code onto a background thread and then do .await to wait for it to complete. Unblock is a simple but powerful API that makes this easy. It's what makes async-fs trivial to implement. async-task - It just spawns futures, that's all. Think of it as conversion from a Future into a Task. In order to poll a future manually, you need to construct a Waker and Context, and that becomes really gross and unsafe code. This crate does the ugly work for you. But you probably won't have to use this crate - see multitask below. multitask - It's a "machine" onto which you can spawn futures, and then execute them one by one by calling tick() repeatedly. We don't care which future gets polled on every tick, we just care that the machine makes one step forward. This machine is the engine of executors. It does work-stealing and all that stuff through a simple API. async-executor - This crate takes a multitask machine and then makes it alive: it just calls tick() in a loop so you don't have to. So instead of ticking the machine step by step, now you can just do executor.run(main_future) and that's it. Pretty boring :) futures-lite - It's like futures but smaller and compiles faster. It also has some small differences in the APIs that I've personally found annoying. async-fs - Async filesystem APIs that look very much like std::fs. Built on top of blocking. Simple implementation. async-net - Async networking APIs that look very much like std::net. Built on top of async-io. Simple implementation.
Some have asked me what's the point of having all this in small crates.
Firstly, I like to make small and "finished" crates, declare them "perfect", publish v1.0 and move on. This gives me peace of mind because once something is "done", it requires none or very little maintenance. It's a way for 1 person to maintain a lot of code efficiently.
Secondly, by breaking all this into small pieces, it becomes possible to do things that async-std and tokio simply can't. For example, suppose you want to have tasks with high and low priorities. This is stupidly easy with multitask - make two machines for different priorities and just tick() them both on the same thread, making sure that the high-priority machine is ticked more often than the other.
But also, importantly, it's not enough to just break code in small crates - those small crates must have really good and thought-out APIs to be worth it.
Oh, one more thing. When the codebase is split up in small crates, reviewing unsafe code become much easier. Lots of those have #![forbid(unsafe_code)]. '''