saluki_core/runtime/
dedicated.rs1use std::{
4 future::Future,
5 io,
6 pin::Pin,
7 sync::{
8 atomic::{AtomicUsize, Ordering},
9 Arc,
10 },
11 task::{ready, Context, Poll},
12 thread::JoinHandle,
13};
14
15use saluki_common::sync::shutdown::ShutdownHandle;
16use saluki_error::{generic_error, GenericError};
17use tokio::sync::oneshot;
18
19use super::{
20 state::DataspaceRegistry,
21 supervisor::{Supervisor, SupervisorError},
22};
23
24#[derive(Clone, Debug)]
26pub struct RuntimeConfiguration {
27 worker_threads: usize,
29}
30
31impl RuntimeConfiguration {
32 pub const fn single_threaded() -> Self {
36 Self { worker_threads: 1 }
37 }
38
39 pub const fn multi_threaded(worker_threads: usize) -> Self {
43 Self { worker_threads }
44 }
45
46 pub(crate) fn worker_threads(&self) -> usize {
47 self.worker_threads
48 }
49
50 pub(crate) fn build(&self, supervisor_id: &str) -> io::Result<tokio::runtime::Runtime> {
52 let supervisor_id = supervisor_id.to_string();
53 let thread_id = Arc::new(AtomicUsize::new(0));
54
55 if self.worker_threads == 1 {
56 tokio::runtime::Builder::new_current_thread().enable_all().build()
57 } else {
58 tokio::runtime::Builder::new_multi_thread()
59 .enable_all()
60 .enable_alt_timer()
61 .worker_threads(self.worker_threads)
62 .thread_name_fn(move || {
63 let new_thread_id = thread_id.fetch_add(1, Ordering::SeqCst);
64 format!("{}-sup-{:02}", supervisor_id, new_thread_id)
65 })
66 .build()
67 }
68 }
69}
70
71#[derive(Clone, Debug, Default)]
73pub enum RuntimeMode {
74 #[default]
78 Ambient,
79
80 Dedicated(RuntimeConfiguration),
85}
86
87pub(crate) struct DedicatedRuntimeHandle {
91 supervisor_id: String,
92 init_rx: Option<oneshot::Receiver<Result<(), GenericError>>>,
93 result_rx: oneshot::Receiver<Result<(), SupervisorError>>,
94 thread_handle: Option<JoinHandle<()>>,
95}
96
97impl Future for DedicatedRuntimeHandle {
98 type Output = Result<(), SupervisorError>;
99
100 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
101 if let Some(init_rx) = self.init_rx.as_mut() {
106 let init_result = ready!(Pin::new(init_rx).poll(cx));
107 let maybe_init_error = match init_result {
108 Ok(Ok(())) => None,
109 Ok(Err(e)) => Some(e),
110 Err(_) => Some(generic_error!(
111 "no initialization result received; runtime creation likely panicked"
112 )),
113 };
114
115 self.init_rx = None;
116
117 if let Some(error) = maybe_init_error {
118 if let Some(handle) = self.thread_handle.take() {
120 let _ = handle.join();
121 }
122
123 return Poll::Ready(Err(SupervisorError::FailedToInitialize {
124 child_name: self.supervisor_id.clone(),
125 source: error.into(),
126 }));
127 }
128 }
129
130 let result = ready!(Pin::new(&mut self.result_rx).poll(cx)).unwrap_or_else(|_| Err(SupervisorError::Shutdown));
136
137 if let Some(handle) = self.thread_handle.take() {
139 let _ = handle.join();
140 }
141
142 Poll::Ready(result)
143 }
144}
145
146pub(crate) fn spawn_dedicated_runtime(
159 mut supervisor: Supervisor, config: RuntimeConfiguration, process_shutdown: ShutdownHandle,
160 dataspace: DataspaceRegistry,
161) -> Result<DedicatedRuntimeHandle, GenericError> {
162 let (init_tx, init_rx) = oneshot::channel();
163 let (result_tx, result_rx) = oneshot::channel();
164
165 let supervisor_id = supervisor.id().to_string();
166 let thread_name = format!("{}-sup-rt", supervisor_id);
167 let thread_handle = std::thread::Builder::new()
168 .name(thread_name.clone())
169 .spawn(move || {
170 let runtime = match config.build(supervisor.id()) {
172 Ok(rt) => rt,
173 Err(e) => {
174 let _ = init_tx.send(Err(generic_error!("Failed to build dedicated runtime: {}", e)));
175 return;
176 }
177 };
178
179 if init_tx.send(Ok(())).is_err() {
181 return;
183 }
184
185 let result = runtime.block_on(supervisor.run_with_shutdown_inner(process_shutdown, Some(dataspace)));
190 let _ = result_tx.send(result);
191 })
192 .map_err(|e| generic_error!("Failed to spawn dedicated runtime thread '{}': {}", thread_name, e))?;
193
194 Ok(DedicatedRuntimeHandle {
195 supervisor_id,
196 init_rx: Some(init_rx),
197 result_rx,
198 thread_handle: Some(thread_handle),
199 })
200}