Skip to main content

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}