reifydb_runtime/pool/
native.rs1#![allow(clippy::disallowed_types)]
5
6use std::{future::Future, sync::Arc, time::Duration};
7
8use rayon::{ThreadPool, ThreadPoolBuilder};
9use reifydb_value::reifydb_assertions;
10use tokio::{
11 runtime::{self, Handle, Runtime},
12 task::JoinHandle,
13};
14
15use super::PoolConfig;
16use crate::sync::mutex::Mutex;
17
18struct PoolsInner {
19 system: Arc<ThreadPool>,
20 query: Arc<ThreadPool>,
21 commit: Arc<ThreadPool>,
22 background: Arc<ThreadPool>,
23 tokio_handle: Option<Handle>,
24 tokio: Mutex<Option<Runtime>>,
25}
26
27impl PoolsInner {
28 fn take_tokio(&self) -> Option<Runtime> {
29 self.tokio.lock().take()
30 }
31}
32
33impl Drop for PoolsInner {
34 fn drop(&mut self) {
35 if let Some(rt) = self.take_tokio() {
36 if runtime::Handle::try_current().is_err() {
37 rt.shutdown_timeout(Duration::from_secs(5));
38 } else {
39 rt.shutdown_background();
40 }
41 }
42 }
43}
44
45#[derive(Clone)]
46pub struct Pools {
47 inner: Arc<PoolsInner>,
48}
49
50impl Default for Pools {
51 fn default() -> Self {
52 Self::new(PoolConfig::default())
53 }
54}
55
56impl Pools {
57 pub fn new(config: PoolConfig) -> Self {
58 let system = Self::build_pool(config.system_threads, "system-pool");
59 let query = Self::build_pool(config.query_threads, "query-pool");
60 let commit = Self::build_pool(config.commit_threads, "commit-pool");
61 let background = Self::build_pool(config.background_threads, "background-pool");
62 let (tokio_handle, tokio) = Self::build_async_runtime(config.async_threads);
63
64 Self {
65 inner: Arc::new(PoolsInner {
66 system,
67 query,
68 commit,
69 background,
70 tokio_handle,
71 tokio,
72 }),
73 }
74 }
75
76 fn build_pool(threads: usize, name_prefix: &'static str) -> Arc<ThreadPool> {
77 Arc::new(
78 ThreadPoolBuilder::new()
79 .num_threads(threads)
80 .thread_name(move |i| format!("{name_prefix}-{i}"))
81 .build()
82 .unwrap_or_else(|_| panic!("failed to build {name_prefix} thread pool")),
83 )
84 }
85
86 #[inline]
87 fn build_async_runtime(threads: usize) -> (Option<Handle>, Mutex<Option<Runtime>>) {
88 let (tokio_handle, tokio) = if threads > 0 {
89 let rt = runtime::Builder::new_multi_thread()
90 .worker_threads(threads)
91 .thread_name("async")
92 .enable_all()
93 .build()
94 .expect("failed to build tokio runtime");
95 let handle = rt.handle().clone();
96 (Some(handle), Mutex::new(Some(rt)))
97 } else {
98 (None, Mutex::new(None))
99 };
100
101 reifydb_assertions! {
102 let handle_present = tokio_handle.is_some();
103 let runtime_present = tokio.lock().is_some();
104 assert!(
105 handle_present == runtime_present,
106 "async handle/runtime presence must agree (handle_present={handle_present}, runtime_present={runtime_present}); \
107 a handle without its runtime makes tokio_handle() dispatch onto a runtime that Drop never shuts down (thread leak), \
108 and a runtime without a handle makes spawn()/block_on() panic via the expect in tokio_handle()"
109 );
110 }
111
112 (tokio_handle, tokio)
113 }
114
115 pub fn shutdown(&self) {
116 if let Some(rt) = self.inner.take_tokio() {
117 if runtime::Handle::try_current().is_err() {
118 rt.shutdown_timeout(Duration::from_secs(5));
119 } else {
120 rt.shutdown_background();
121 }
122 }
123 }
124
125 pub fn system_pool(&self) -> &Arc<ThreadPool> {
126 &self.inner.system
127 }
128
129 pub fn system_thread_count(&self) -> usize {
130 self.inner.system.current_num_threads()
131 }
132
133 pub fn query_pool(&self) -> &Arc<ThreadPool> {
134 &self.inner.query
135 }
136
137 pub fn query_thread_count(&self) -> usize {
138 self.inner.query.current_num_threads()
139 }
140
141 pub fn commit_pool(&self) -> &Arc<ThreadPool> {
142 &self.inner.commit
143 }
144
145 pub fn commit_thread_count(&self) -> usize {
146 self.inner.commit.current_num_threads()
147 }
148
149 pub fn background_pool(&self) -> &Arc<ThreadPool> {
150 &self.inner.background
151 }
152
153 pub fn background_thread_count(&self) -> usize {
154 self.inner.background.current_num_threads()
155 }
156
157 fn tokio_handle(&self) -> Handle {
158 self.inner.tokio_handle.clone().expect("no tokio runtime configured (async_threads = 0)")
159 }
160
161 pub fn handle(&self) -> Handle {
162 self.tokio_handle()
163 }
164
165 pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
166 where
167 F: Future + Send + 'static,
168 F::Output: Send + 'static,
169 {
170 self.tokio_handle().spawn(future)
171 }
172
173 pub fn block_on<F>(&self, future: F) -> F::Output
174 where
175 F: Future,
176 {
177 self.tokio_handle().block_on(future)
178 }
179}