tokio-rs / tokio

A runtime for writing reliable asynchronous applications with Rust. Provides I/O, networking, scheduling, timers, ...
https://tokio.rs
MIT License
26.91k stars 2.48k forks source link

How to wait a Future in normal function #2289

Closed Sherlock-Holo closed 4 years ago

Sherlock-Holo commented 4 years ago

Version 0.2.13

├── tokio v0.2.13
│   └── tokio-macros v0.2.5
│   │   │   ├── tokio v0.2.13 (*)
│   │   │   └── tokio-util v0.2.0
│   │   │       └── tokio v0.2.13 (*)
│   │   ├── tokio v0.2.13 (*)
│   ├── tokio v0.2.13 (*)
│   ├── tokio-rustls v0.12.2
│   │   ├── tokio v0.2.13 (*)
│   ├── tokio-util v0.2.0 (*)
│   │   │   ├── tokio v0.2.13 (*)
│   │   │   ├── tokio v0.2.13 (*)
│   │   │   ├── tokio v0.2.13 (*)
│   │   │   ├── tokio v0.2.13 (*)
│   │   ├── tokio v0.2.13 (*)
│   │   │   ├── tokio v0.2.13 (*)
│   │   │   ├── tokio v0.2.13 (*)
│   │   │   ├── tokio v0.2.13 (*)

Platform

Linux SherlockHolo 5.5.7-arch1-1 #1 SMP PREEMPT Sat, 29 Feb 2020 19:06:02 +0000 x86_64 GNU/Linux

Description

Is there a way to wait a Future done in a normal function?

For example I need to run some async fn in Drop trait, if I create a new Runtime, wil panic and tell me I can't create a runtime in a runtime, for now I have to use futures::executor::block_on. But I have no way to tell tokio runtime that "current thread will be blocked for a long time, you shouldn't dispatch future to this thread any more".

Is there any tokio api to wait a Future done in a normal function, or tell tokio this thread is blocking?

Darksonn commented 4 years ago

Generally it's best to avoid this kind of thing, but it sounds like block_in_place does what you're looking for?

Alternatively consider spawning a task and immediately returning from drop().

Sherlock-Holo commented 4 years ago

@Darksonn However block_in_place only can run sync codes, not async codes. I need to run async codes but blocking thread like sync codes.

drop only have &mut self, it is so hard to spawn a task

Darksonn commented 4 years ago

You could still use futures::executor::block_on inside block_in_place? That will tell Tokio that the current thread will be blocked.

As for spawning inside drop, you can still use tokio::spawn if it was dropped inside an async fn. If you wish to detect whether you're inside an async fn, you can use Handle::try_current and just call spawn on the handle, and otherwise you're outside of async land, so you can just block?

Sherlock-Holo commented 4 years ago

block_in_place must in a spawned task so I can't do this.

tokio::spawn need future is Send, but it's so hard to move something from a &mut self, perhaps I can use Option and take(), then move in async block, but it will changes a lot of codes.

Darksonn commented 4 years ago

Generally long-running things in destructors are a bad idea. If at all possible, try to find a different solution. As for your spawned future, yes you will likely need an Option with take() to go that route.

Sherlock-Holo commented 4 years ago

What confusing me is why tokio doesn't provide like async_std::task::block_on. I think runtime just need to mark this thread is blocking and remove it from async thread pool. Is there anything make it hard to do it?

Darksonn commented 4 years ago

That's what futures::executor::block_on inside block_in_place does.

Sherlock-Holo commented 4 years ago

@Darksonn we can't use block_in_place in drop directly, like example it will panic with

thread 'main' panicked at 'can call blocking only when running in a spawned task', src/libcore/option.rs:1188:5.

Perhaps I misunderstand what you means? If you cloud give me an example codes I am very grateful

Darksonn commented 4 years ago

Apparently you can't block inside the "main task" of Tokio. There isn't really much you can do about that, except just blocking and hoping the impact on the scheduler is not too bad.

Sherlock-Holo commented 4 years ago

Yes, so if we want to do this job beautiful and simple, in my option, we still need an api like async_std::task::block_on.

actually I have tried mix async_std and tokio, most codes use async_std api, of couse used async_std::task::block_on, then create a tokio runtime and block it by futures::future::pending to start tokio reactor because I used tonic, but it make codes so chaos and so hard to maintaining

Darksonn commented 4 years ago

I don't know why block_in_place doesn't work in the main task, but I don't think there needs to be a beautiful and simple way to block inside destructors. I recommend trying to get tokio::spawn to work.

Sherlock-Holo commented 4 years ago

you are right, blocking in destructors is a bad idea, we should try to avoid this.

In other places I need to do a async job in a normal method such as codes, for now it may block some futures which are dispatch to this thread. This method only run once so it may won't cause big problems, but if have an api async_std::task::block_on, it may solve some problems in the future

Darksonn commented 4 years ago

I mean generally you shouldn't call blocking code from async function. Perhaps your normal function should be async, or at least have an async counterpart?

Sherlock-Holo commented 4 years ago

I think make the normal function have an async version is the best way, this will reduce a lot of problems 🤣. For now I have to use futures::executor::block_on to do this job, in the future this function should have an async version or I fork and modify it :-)

hawkw commented 4 years ago

I don't know why block_in_place doesn't work in the main task, but I don't think there needs to be a beautiful and simple way to block inside destructors. I recommend trying to get tokio::spawn to work.

I believe block_in_place doesn't work in the main task because it causes the calling worker thread to transition to being a blocking worker and be replaced by a new worker thread. This doesn't work in the main task because the main task is not running on a worker thread. spawn_blocking should work, though?

Darksonn commented 4 years ago

Hey @jonhoo, did #2410 enable use of block_in_place in the main block_on task?

jonhoo commented 4 years ago

Yes, it should have!