Skip to main content

qubit_executor/executor/
thread_per_task_executor.rs

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