qubit_executor/executor/thread_per_task_executor.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9use std::thread;
10
11use qubit_function::Callable;
12
13use crate::{
14 TaskCompletionPair,
15 TaskHandle,
16 TaskRunner,
17};
18
19use super::Executor;
20
21/// Executes each task on a dedicated OS thread.
22///
23/// This executor does not manage lifecycle or maintain a queue. Each accepted
24/// task receives a [`TaskHandle`] that can be used to wait for the result.
25///
26/// # Semantics
27///
28/// * **One task, one thread** — each [`Executor::call`] or [`Executor::execute`]
29/// spawns a new [`std::thread::spawn`] worker. There is no pool and no
30/// submission queue.
31/// * **Blocking or async wait** — [`TaskHandle::get`] blocks the calling thread,
32/// while awaiting the handle uses a waker and does not block the polling
33/// thread.
34/// * **Completion probe** — [`TaskHandle::is_done`] reads an atomic flag set
35/// after the worker publishes the result; it does not retrieve the value
36/// (you still need [`TaskHandle::get`] for that).
37///
38/// # Examples
39///
40/// ```rust
41/// use std::io;
42///
43/// use qubit_executor::executor::{
44/// Executor,
45/// ThreadPerTaskExecutor,
46/// };
47///
48/// let executor = ThreadPerTaskExecutor;
49/// let handle = executor.call(|| Ok::<i32, io::Error>(40 + 2));
50///
51/// // Blocks the current thread until the spawned thread completes.
52/// let value = handle.get().expect("task should succeed");
53/// assert_eq!(value, 42);
54/// ```
55#[derive(Debug, Default, Clone, Copy)]
56pub struct ThreadPerTaskExecutor;
57
58impl Executor for ThreadPerTaskExecutor {
59 type Execution<R, E>
60 = TaskHandle<R, E>
61 where
62 R: Send + 'static,
63 E: std::fmt::Display + Send + 'static;
64
65 /// Spawns one OS thread for the callable and returns a handle to its result.
66 ///
67 /// # Parameters
68 ///
69 /// * `task` - Callable to run on a dedicated OS thread.
70 ///
71 /// # Returns
72 ///
73 /// A [`TaskHandle`] that can block or await the spawned task's final
74 /// result.
75 fn call<C, R, E>(&self, task: C) -> Self::Execution<R, E>
76 where
77 C: Callable<R, E> + Send + 'static,
78 R: Send + 'static,
79 E: std::fmt::Display + Send + 'static,
80 {
81 let (handle, completion) = TaskCompletionPair::new().into_parts();
82 thread::spawn(move || {
83 TaskRunner::new(task).run(completion);
84 });
85 handle
86 }
87}