Tokio current_thread::Handle::spawn requires Send?

I’m trying to work with tokio for some IO and I’m using tokio::runtime::current_thread::Handle.
But when I try to spawn a future, it requires Send, as indicated in the docs here:
I was imagining that the purpose of the current_thread model was that I could get I/O without handling Send issues.
Can anyone help me understand why this is the case?


I think the intent behind current_thread::Handle is it allows you to spawn a future onto the associated runtime from a different thread, which requires moving that future.

Is there a reason you can’t use current_thread::Runtime::spawn (which doesn’t require Send)? Are you trying to spawn a future from a place where you don’t have access to the current_thread::Runtime?

One curious thing I noticed while looking at this is current_thread::Handle::spawn takes &self, whereas current_thread::Runtime::spawn takes &mut self. If one wanted to keep the Runtime shared using an Rc (so that its spawn could be called), it would need to be wrapped in a RefCell due to the &mut self requirement. I wonder why that difference vs Handle::spawn exists …

1 Like

Oh thank you for the clarification. I was sure I saw that the current_thread::Runtime didn’t need Send, but I had missed that the Handle and Runtime are different. I recall that there used to be the Handle and Remote type, but I guess they’ve consolidated into just the Handle that acts like a Remote.
I’ll look into whether I can use Runtime::spawn(). The hangup with that is that I’m waiting on Runtime::block_on() while I’m currently trying to call Handle::spawn() so I can’t also borrow the RefCell to call Runtime::spawn(). I’ll see if there’s a way around that.
Thanks for the tip @vitalyd!

Yeah, I’m actually unsure of how you’re supposed to do this with the current API (admittedly, I’ve not kept fully up-to-date on all the new tokio stuff). On the surface, it would seem like you’d want a CurrentThreadHandle, which is !Send and whose spawn doesn’t require a future to be Send. But I may very well be missing something crucial.

cc @carllerche as he’ll know one way or another

1 Like

I traced it down a few layers and it appears that the current_thread types eventually depend on the same executor as the thread pool types which correctly require Send. Specifically the CurrentThread implements tokio_executor::Executor::spawn.

So the current_thread types are more like just a thread pool with only one thread.

Yeah, the Executor trait requires Send (and boxed futures for spawn!). This means you can’t (easily, with builtin API) abstract over Send vs !Send executors - only Send ones, which eliminates the current_thread runtime.

But, if you’re using the concrete current_thread types, it shouldn’t be an issue.

1 Like

ok, this helped. I switched to spawning the future with the tokio_current_thread module instead.
It’s now tokio_current_thread::TaskExecutor::current().spawn_local(fut) and that compiles fine.

Thanks @vitalyd!

1 Like

Ah, the “current thread” stuff was split out into its own tokio_current_thread crate - good to know!