Task::spawn failing when using Tide
andrewbanchich opened this issue · 8 comments
I just switched over a server from using warp
to using tide
, and noticed that calling Task::spawn
fails now with:
thread 'async-std/runtime' panicked at '`Task::spawn()` must be called from an `Executor` or `LocalExecutor`', /Users/andrewbanchich/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/src/libstd/macros.rs:13:23
Everything I'm calling that is async is started within smol::run
smol::run(async {
tide::log::start();
let mut app = tide::new();
app.at("/example").post(example_handler);
app.listen(listen_addr).await.unwrap();
})
example_handler
is what is using Task::spawn
.
Am I doing anything wrong here?
Isn't that what smol::Task::spawn
should be doing itself?
I'm not sure what the right solution is here. Here are some design choices for executors:
-
There is no global executor (this is the current situation). Instead, you start one with
smol::block_on()
orsmol::run()
and then must callTask::spawn()
within the executor. Otherwise, you get the panic because we don't know which executor to spawn the task onto, because there could be multiple executors to choose from. -
There is a global executor, and the first time it is initialized, an executor thread pool is created. This way
Task::spawn()
will never panic because it can spawn onto the current executor, or onto the global executor if spawned outside an executor. This is similar to howasync-std
works. -
There is a global executor, but it does not create a thread pool. It only serves as a task queue for tasks spawned outside an executor. Then, other executors like
smol::run()
will occasionally grab some tasks from the global executor. This is similar to how smol v0.1 worked.
Any preferences or opinions? I feel the current panic is unfortunate and is very much like tokio's infamous panic when using its primitives outside the runtime.
That's what I'm confused about: I am currently using smol::run()
and spawning a Task
within that, and it's giving me the panic I posted.
Right :) The problem is that tasks are actually not spawned within smol::run()
.
When you start Tide, it listens for incoming connections and creates a task with async_std::task::spawn()
for each connection. Inside this task spawned onto async-std's executor, Tide calls your example_handler
. So now we're inside async-std's executor, not inside smol::run()
! Then, if we do Task::spawn()
there, we get a panic because we're outside the smol's executor.
It's all very subtle and confusing, which is why I think the current situation is far from ideal.
You can try adding async_executor to your dependencies, getting a Spawner using async_exexutor::Spawner::current and using this to spawn tasks (it will end up in the same executor as Task::spawn
It's still panicking for me:
thread 'async-std/runtime' panicked at '`Spawner::current()` must be called from an `Executor`'
@stjepang Thanks for the explanation! Yeah, that isn't how I imagined it currently works.
@Keruspe Ah, I see what you were saying now. I updated my code to use OnceCell
and created a Spawner
in the context of smol::run()
and set the OnceCell
to that, then accessed it throughout the rest of my program.
That works! Thank you very much!
Some ideas on how to fix this issue in #202
Feedback would be very helpful!