]>
git.proxmox.com Git - proxmox-backup.git/blob - src/tools/runtime.rs
1 //! Helpers for quirks of the current tokio runtime.
3 use std
::cell
::RefCell
;
4 use std
::future
::Future
;
6 use lazy_static
::lazy_static
;
7 use tokio
::runtime
::{self, Runtime}
;
10 static HAS_RUNTIME
: RefCell
<bool
> = RefCell
::new(false);
11 static IN_TOKIO
: RefCell
<bool
> = RefCell
::new(false);
14 fn is_in_tokio() -> bool
{
15 IN_TOKIO
.with(|v
| *v
.borrow())
18 fn has_runtime() -> bool
{
19 HAS_RUNTIME
.with(|v
| *v
.borrow())
22 struct RuntimeGuard(bool
);
26 Self(HAS_RUNTIME
.with(|v
| {
27 let old
= *v
.borrow();
28 *v
.borrow_mut() = true;
34 impl Drop
for RuntimeGuard
{
36 HAS_RUNTIME
.with(|v
| {
37 *v
.borrow_mut() = self.0;
43 static ref RUNTIME
: Runtime
= {
44 runtime
::Builder
::new()
47 .on_thread_start(|| IN_TOKIO
.with(|v
| *v
.borrow_mut() = true))
49 .expect("failed to spawn tokio runtime")
53 /// Get or create the current main tokio runtime.
55 /// This makes sure that tokio's worker threads are marked for us so that we know whether we
56 /// can/need to use `block_in_place` in our `block_on` helper.
57 pub fn get_runtime() -> &'
static Runtime
{
61 /// Associate the current newly spawned thread with the main tokio runtime.
62 pub fn enter_runtime
<R
>(f
: impl FnOnce() -> R
) -> R
{
63 let _guard
= RuntimeGuard
::enter();
64 get_runtime().enter(f
)
67 /// Block on a synchronous piece of code.
68 pub fn block_in_place
<R
>(fut
: impl FnOnce() -> R
) -> R
{
70 // we are in an actual tokio worker thread, block it:
71 tokio
::task
::block_in_place(fut
)
73 // we're not inside a tokio worker, so just run the code:
78 /// Block on a future in this thread.
79 pub fn block_on
<R
, F
>(fut
: F
) -> R
82 F
: Future
<Output
= R
> + Send
,
86 // inside a tokio worker we need to tell tokio that we're about to really block:
87 tokio
::task
::block_in_place(move || futures
::executor
::block_on(fut
))
88 } else if has_runtime() {
89 // we're already associated with a runtime, but we're not a worker-thread, we can just
90 // block this thread directly
91 // This is not strictly necessary, but it's a bit quicker tha the else branch below.
92 futures
::executor
::block_on(fut
)
94 // not a worker thread, not associated with a runtime, make sure we have a runtime (spawn
95 // it on demand if necessary), then enter it:
96 enter_runtime(move || futures
::executor
::block_on(fut
))
101 fn block_on_impl<F>(mut fut: F) -> F::Output
104 F::Output: Send + 'static,
106 let (tx, rx) = tokio::sync::oneshot::channel();
107 let fut_ptr = &mut fut as *mut F as usize; // hack to not require F to be 'static
108 tokio::spawn(async move {
109 let fut: F = unsafe { std::ptr::read(fut_ptr as *mut F) };
113 .expect("failed to send block_on result to channel")
116 futures::executor::block_on(async move {
117 rx.await.expect("failed to receive block_on result from channel")
119 std::mem::forget(fut);
123 /// This used to be our tokio main entry point. Now this just calls out to `block_on` for
124 /// compatibility, which will perform all the necessary tasks on-demand anyway.
125 pub fn main
<F
>(fut
: F
) -> F
::Output
128 F
::Output
: Send
+ '
static,