I have a lazy_static! tokio runtime at the top of a library, which spawns+pins however many tokio tasks in structs of that library.
So to my understanding, all the wakers of that library use the runtime.
However, I then wake these wakers from different threads. Which leads to a few questions:
Because I subsequently poll from different threads, but initially the waker/contexts were spawned on the runtime initialized at start, does my program still suffer the bottleneck of all the wakers being on 1 thread?
Because wakers are replaced after subsequent polls (they return Pending), does it still use the context of the runtime that the initial poll was spawned on, or the runtime that last polls this future?
Can I use 1 runtime to spawn+pin a future, and use the SAME runtime for subsequent wake calls? I read that tokio contexts have a budget, and that budget is only refreshed if the future is polled subsequently from a DIFFERENT runtime.
For some context (of the problem statement), it is essentially in the form of:
I have 1,000 UDP sockets each with a packet of, lets say 1k bytes. I want to keep the sockets wrapped in a future that polls and re-polls over it for new data and processes the data.
The reason I want to pin the initial poll is that I can just throw the waker around to different threads / places rather than have them all re-poll this wrapped socket. The ultimate goal is to be able to poll different sockets from different threads (concurrently!) but I am just unsure if my initial method of initialization from the same runtime bottlenecks that goal. If it does, if someone could also give an answer to my third bullet point, it would help clear things up greatly.
No. If you are using the multithreaded tokio runtime, then tasks that are woken may be executed on any worker thread. If you are using the current-thread runtime, then they are executed on whichever threads exist that are doing block_on() calls.
In both cases, spawned tasks are not tied to any specific thread.
I'm not sure exactly what you're asking about, but a Futuremust use the Waker that it was given by the most recent call to its poll() method. Most futures should not care about knowing “runtimes”, but only the Waker they are given.
If you are implementing a Future that is woken up by something happening on another thread, this can be tricky to do correctly; AtomicWaker can help you implement it correctly.
The budget is about ensuring additional fairness. You usually don't have to worry about it unless you have a task that might run for a long time without ever yielding.
I'm not clear on exactly what you're describing, but it is unlikely that you need to do anything with Wakers explicitly. Wakers are needed if you are implementing
a nontrivial leaf future (e.g. one that does IO),
a channel, or
a future combinator (like select!())
from scratch, but most crates don't need to do that and can use async together with existing IO futures and combinators to do everything they need to do.