runtime/
installer.rs

1use std::{
2	cell::RefCell,
3	collections::HashMap,
4	fmt::{Debug, Formatter},
5};
6
7use crossbeam_channel::Sender;
8
9use crate::{Notifier, Status, ThreadStatuses};
10
11/// A thread installer that is passed to a `Threadable` when installing the threads into the `Runtime`
12pub struct Installer {
13	sender: Sender<(String, Status)>,
14	thread_statuses: ThreadStatuses,
15	ops: RefCell<HashMap<String, Box<dyn FnOnce() + Send>>>,
16}
17
18impl Installer {
19	pub(crate) fn new(thread_statuses: ThreadStatuses, sender: Sender<(String, Status)>) -> Self {
20		Self {
21			sender,
22			thread_statuses,
23			ops: RefCell::new(HashMap::new()),
24		}
25	}
26
27	pub(crate) fn into_ops(self) -> HashMap<String, Box<dyn FnOnce() + Send>> {
28		self.ops.take()
29	}
30
31	/// Spawn a new thread with a name. The installer function callback will be called with a `Notifier` and is
32	/// returns the thread function.
33	#[inline]
34	pub fn spawn<InstallFn, ThreadFn>(&self, name: &str, install: InstallFn)
35	where
36		InstallFn: FnOnce(Notifier) -> ThreadFn,
37		ThreadFn: FnOnce() + Send + 'static,
38	{
39		self.thread_statuses.register_thread(name, Status::New);
40		let sender = self.sender.clone();
41		let notifier = Notifier::new(name, sender);
42		let _previous = self
43			.ops
44			.borrow_mut()
45			.insert(String::from(name), Box::new(install(notifier)));
46	}
47}
48
49impl Debug for Installer {
50	#[inline]
51	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
52		f.debug_struct("Installer")
53			.field("sender", &self.sender)
54			.field("thread_statuses", &self.thread_statuses)
55			.finish_non_exhaustive()
56	}
57}
58
59#[cfg(test)]
60mod tests {
61	use std::sync::{
62		atomic::{AtomicBool, Ordering},
63		Arc,
64	};
65
66	use crossbeam_channel::unbounded;
67
68	use super::*;
69	use crate::Threadable;
70
71	struct Thread {
72		called: Arc<AtomicBool>,
73	}
74
75	impl Thread {
76		fn new() -> Self {
77			Self {
78				called: Arc::new(AtomicBool::new(false)),
79			}
80		}
81	}
82
83	impl Threadable for Thread {
84		fn install(&self, installer: &Installer) {
85			let called = Arc::clone(&self.called);
86			installer.spawn("name", |_| {
87				move || {
88					called.store(true, Ordering::Relaxed);
89				}
90			});
91		}
92	}
93
94	#[test]
95	fn test() {
96		let (sender, _receiver) = unbounded();
97		let installer = Installer::new(ThreadStatuses::new(), sender);
98
99		let thread = Thread::new();
100		thread.install(&installer);
101
102		let mut ops = installer.into_ops();
103		let func = ops.remove("name").unwrap();
104		func();
105
106		assert!(thread.called.load(Ordering::Acquire));
107	}
108
109	#[test]
110	fn debug() {
111		let (sender, _receiver) = unbounded();
112		let installer = Installer::new(ThreadStatuses::new(), sender);
113		assert_eq!(
114			format!("{installer:?}"),
115			"Installer { sender: Sender { .. }, thread_statuses: ThreadStatuses { statuses: Mutex { data: {} } }, .. }"
116		);
117	}
118}