1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*******************************************************************************
*
* Copyright (c) 2025 - 2026 Haixing Hu.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0.
*
******************************************************************************/
use Callable;
use ;
use crateensure_tokio_runtime_entered;
/// Executes callable tasks on Tokio's blocking task pool.
///
/// `TokioExecutor` implements [`Executor`] by submitting work to Tokio's
/// blocking task pool and returning the standard tracked task handle.
///
/// # Semantics
///
/// * **`call` schedules work immediately** — [`Executor::call`] runs
/// [`tokio::task::spawn_blocking`] **synchronously** before it returns. A
/// Tokio runtime must **already be active** on the current thread when `call`
/// runs (for example inside an `async` block executed under
/// [`Runtime::block_on`](tokio::runtime::Runtime::block_on) or
/// [`#[tokio::main]`](https://docs.rs/tokio/latest/tokio/attr.main.html)).
/// Calling `call` first and only then entering a runtime is rejected with
/// [`SubmissionError::WorkerSpawnFailed`].
/// * **Any normal Tokio entry point works** — you are **not** restricted to
/// [`Builder::new_current_thread`](tokio::runtime::Builder::new_current_thread);
/// a multi-thread [`Runtime`](tokio::runtime::Runtime) or an async handler in
/// a server is fine, as long as `call` happens while that runtime is running.
/// * **Await the returned tracked task on Tokio** — the returned
/// [`TrackedTask`] implements [`IntoFuture`](std::future::IntoFuture), so it
/// can be awaited inside a Tokio-driven async context after submission
/// succeeds.
/// * **Blocking pool** — the closure runs on Tokio's *blocking* thread pool, not
/// on the core async worker threads, so heavy synchronous work does not
/// starve other async tasks on the runtime.
/// * **Standard tracked-task cancellation** — the returned [`TrackedTask`]
/// can cancel the user callable before it starts, but it does not own Tokio's
/// [`AbortHandle`](tokio::task::AbortHandle). If the Tokio blocking queue has
/// already accepted the wrapper closure, that wrapper may still wait for a
/// blocking thread and then observe the cancelled tracked state without
/// running the user callable. Use [`TokioExecutorService`](crate::TokioExecutorService)
/// and [`TokioBlockingTaskHandle`](crate::TokioBlockingTaskHandle) when
/// queued Tokio blocking work must be aborted directly.
/// * **Compared to
/// [`ThreadPerTaskExecutor`](qubit_executor::executor::ThreadPerTaskExecutor)** —
/// this type **reuses** Tokio-managed blocking threads (bounded pool) instead
/// of one new [`std::thread`] per task, and can return a handle that is either
/// awaited or read with blocking `get`.
///
/// # Examples
///
/// The following uses a single-thread [`Runtime`](tokio::runtime::Runtime) only to keep the snippet
/// self-contained; [`#[tokio::main]`](https://docs.rs/tokio/latest/tokio/attr.main.html)
/// or a multi-thread runtime are equally valid.
///
/// ```rust
/// use std::io;
///
/// use qubit_tokio_executor::{
/// Executor,
/// TokioExecutor,
/// };
///
/// # fn main() -> io::Result<()> {
/// tokio::runtime::Builder::new_current_thread()
/// .enable_all()
/// .build()?
/// .block_on(async {
/// let executor = TokioExecutor;
/// let value = executor
/// .call(|| Ok::<i32, io::Error>(40 + 2))
/// .expect("executor should accept callable")
/// .await
/// .expect("callable should complete successfully");
/// assert_eq!(value, 42);
/// Ok::<(), io::Error>(())
/// })?;
/// # Ok(())
/// # }
/// ```
;