1use std::time::Duration;
12
13#[derive(Debug, thiserror::Error)]
14pub enum RuntimeError {
15 #[error("failed to spawn dedicated tokio thread")]
16 ThreadSpawnFailed,
17 #[error("failed to receive runtime handle from dedicated thread")]
18 HandleRecvFailed,
19}
20
21pub struct CoreRuntime {
22 handle: tokio::runtime::Handle,
23 shutdown_tx: std::sync::mpsc::Sender<()>,
24 thread: Option<std::thread::JoinHandle<()>>,
25}
26
27impl CoreRuntime {
28 pub fn new() -> Result<Self, RuntimeError> {
29 let (handle_tx, handle_rx) = std::sync::mpsc::channel();
30 let (shutdown_tx, shutdown_rx) = std::sync::mpsc::channel::<()>();
31 let thread = std::thread::Builder::new()
32 .name("mesh-client-tokio".to_string())
33 .spawn(move || {
34 let rt = tokio::runtime::Builder::new_multi_thread()
35 .enable_all()
36 .worker_threads(2)
37 .thread_name("mesh-core-worker")
38 .build()
39 .expect("tokio runtime build");
40 handle_tx.send(rt.handle().clone()).expect("send handle");
41 let _ = shutdown_rx.recv();
42 rt.shutdown_timeout(Duration::from_secs(5));
43 })
44 .map_err(|_| RuntimeError::ThreadSpawnFailed)?;
45 let handle = handle_rx
46 .recv()
47 .map_err(|_| RuntimeError::HandleRecvFailed)?;
48 Ok(Self {
49 handle,
50 shutdown_tx,
51 thread: Some(thread),
52 })
53 }
54
55 pub fn handle(&self) -> &tokio::runtime::Handle {
56 &self.handle
57 }
58}
59
60impl Drop for CoreRuntime {
61 fn drop(&mut self) {
62 let _ = self.shutdown_tx.send(());
63 if let Some(thread) = self.thread.take() {
64 let _ = thread.join();
65 }
66 }
67}