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}