(Essentially, whether or not it was named for this reason-and I'm kind of assuming it wasn't, which is sad, because not enough people understand monads and I feel like it hurts a lot of mainstream programming languages. Everything I've seen about Rust seems to assume that there is a single task/promise type that comes from the standard library, meaning that it isn't clear to me how I could possibly do this kind of advanced scheduling work. In C++2a, I would model this by having a promise type that is used for my prioritize-able tasks and, to interface with existing APIs (such as the web request API) that are doing I/O scheduling I'd use await_transform to adapt their promise type into one of mine that lets me maintain my deadline across the I/O operation and then recover it in both of the subtasks that come back into my multi-threaded work queue. On your last paragraph, the thing I'm concerned by is where this extra priority information is stored and propogated, as the term "task" is interesting: isn't every single separate thing being awaited its own task? There isn't (in my mental model) a concept that maps into something like a "pseudo-thread" (but maybe Rust does something like this, requiring a very structured form of concurrency?), which would let me set a "pseudo-thread" property, right?Īs an example: if I am already in an asynchronous coroutine and I spawn of two asynchronous web requests as sub-tasks, the results of which will be processed potentially in parallel on various work queues, and then join those two tasks into a high-level join task that I wait on (so I want both of these things to be done before I continue), I'd want the background processing done on the results to be handled at the priority of this parent spawning task do I have to manually propagate this information? Then inside the task, you can gain a reference to yourself and modify your priority. You can also allow modifying this priority within the task: when you poll a task, set a thread-local variable to point to that task. When you write the executor, you control how new tasks are scheduled, and can add an API that allows specifying a task priority. The way I would approach the specific problem you mention is with a custom executor. But as I say in the post, we can and will add more features on a rolling basis. Some of it absolutely represents missing features in Rust's initial implementation. I admit to not fully understanding what each one of them is for, but my feeling is that some of it comes from approaching the problem differently. One benefit of C++ that you allude to is that there are a lot of extension points. I'll be covering what they do and don't do, as well as future work needed, in future blog posts. To be clear, the optimizations I'm talking about in the blog post are all implemented today. (Imagine, say, a recursive generator - it's impossible to inline inside of itself! When you recurse, you must allocate the new generator on the heap, inside a Box.) This means that in cases where you can't inline state, you must introduce an explicit indirection. In Rust, all the "state inlining" is explicitly done as part of the language. But one downside of this approach is that you could change your code and accidentally disable one of these optimizations. I don't want to spread FUD: my understanding is that the vast majority of these are optimized out by the compiler. In C++, technically every initialization of a sub-coroutine starts defaults to being a new heap allocation. One somewhat surface-level difference is that it implements co_yield in terms of co_await, which is the opposite of Rust implementing await in terms of yield.Īnother difference is that in Rust, all heap allocations of your generators/futures are explicit. The C++ proposal definitely attacks the problem from a different angle than Rust. Disclaimer: I'm not an expert on the proposal, but have looked at it some, and can offer my impressions here.
0 Comments
Leave a Reply. |