1use std::sync::mpsc::{Receiver, Sender};
10use std::sync::{Once, RwLock};
11use std::thread::JoinHandle;
12
13use crate::rcu::context::RcuContext;
14use crate::rcu::flavor::RcuFlavor;
15
16pub type RcuCleanup<C> = Box<dyn FnOnce(&C) + Send + 'static>;
18
19pub type RcuCleanupMut<C> = Box<dyn FnOnce(&mut C) + Send + 'static>;
21
22type ContextFn<C> = Box<dyn FnOnce() -> C + Send>;
23
24enum Command<C> {
25 Execute(RcuCleanup<C>),
26 ExecuteMut(RcuCleanupMut<C>),
27 Barrier(Sender<()>),
28 Shutdown,
29}
30
31struct Thread<C> {
32 commands: Receiver<Command<C>>,
33}
34
35impl<C> Thread<C>
36where
37 C: 'static,
38{
39 fn start(context: ContextFn<C>, commands: Receiver<Command<C>>) -> JoinHandle<()> {
40 std::thread::Builder::new()
41 .name(format!(
42 "urcu::cleanup::{}",
43 std::any::type_name::<C>()
44 .split("::")
45 .last()
46 .unwrap()
47 .replace("RcuContext", "")
48 .to_lowercase()
49 ))
50 .spawn(move || Self { commands }.run(context))
51 .unwrap()
52 }
53
54 fn run(self, context: ContextFn<C>) {
55 log::debug!("launching cleanup thread");
56
57 let mut context = context();
58
59 loop {
60 match self.commands.recv() {
61 Ok(Command::Execute(callback)) => callback(&context),
62 Ok(Command::ExecuteMut(callback)) => callback(&mut context),
63 Ok(Command::Shutdown) => break,
64 Ok(Command::Barrier(sender)) => {
65 if let Err(e) = sender.send(()) {
66 log::error!("failed to execute cleanup barrier: {:?}", e);
67 }
68 }
69 Err(e) => {
70 log::error!("failed to get cleanup command: {:?}", e);
71 break;
72 }
73 }
74 }
75
76 log::debug!("shutting down cleanup thread");
77 }
78}
79
80struct ThreadHandle<C> {
81 thread: Option<JoinHandle<()>>,
82 callbacks: Sender<Command<C>>,
83}
84
85impl<C> ThreadHandle<C>
86where
87 C: RcuContext + 'static,
88{
89 fn create(instance: &RwLock<Option<Self>>, context: ContextFn<C>) -> RcuCleaner<C> {
90 RcuCleaner(
91 instance
92 .write()
93 .unwrap()
94 .get_or_insert_with(|| {
95 let (tx, rx) = std::sync::mpsc::channel();
96
97 Self {
98 thread: Some(Thread::start(context, rx)),
99 callbacks: tx,
100 }
101 })
102 .callbacks
103 .clone(),
104 )
105 }
106
107 fn try_get(instance: &RwLock<Option<Self>>) -> Option<RcuCleaner<C>> {
108 instance
109 .read()
110 .unwrap()
111 .as_ref()
112 .map(|handle| RcuCleaner(handle.callbacks.clone()))
113 }
114
115 fn get(instance: &RwLock<Option<Self>>, context: ContextFn<C>) -> RcuCleaner<C> {
116 Self::try_get(instance).unwrap_or_else(|| Self::create(instance, context))
117 }
118
119 fn delete(instance: &RwLock<Option<Self>>) {
120 instance.write().unwrap().take();
121 }
122}
123
124impl<C> Drop for ThreadHandle<C> {
125 fn drop(&mut self) {
126 log::trace!("sending shutdown command");
127
128 if let Err(e) = self.callbacks.send(Command::Shutdown) {
129 log::error!("failed to send shutdown command: {:?}", e);
130 return;
131 }
132
133 if let Some(handle) = self.thread.take() {
134 if let Err(e) = handle.join() {
135 log::error!("failed to join cleanup thread: {:?}", e);
136 }
137 }
138 }
139}
140
141pub struct RcuCleaner<C>(Sender<Command<C>>);
142
143impl<C> RcuCleaner<C> {
144 pub fn send(&self, callback: RcuCleanup<C>) -> &Self {
145 let command = Command::Execute(callback);
146 if let Err(e) = self.0.send(command) {
147 log::error!("failed to send execute command: {:?}", e);
148 }
149
150 self
151 }
152
153 pub fn send_mut(&self, callback: RcuCleanupMut<C>) -> &Self {
154 let command = Command::ExecuteMut(callback);
155 if let Err(e) = self.0.send(command) {
156 log::error!("failed to send execute command: {:?}", e);
157 }
158
159 self
160 }
161
162 pub fn barrier(&self) -> &Self {
163 let (tx, rx) = std::sync::mpsc::channel();
164
165 let command = Command::Barrier(tx);
166 if let Err(e) = self.0.send(command) {
167 log::error!("failed to send barrier command: {:?}", e);
168 } else if let Err(e) = rx.recv() {
169 log::error!("failed to wait for barrier: {:?}", e);
170 } else {
171 log::trace!("finished barrier command");
172 }
173
174 self
175 }
176}
177
178macro_rules! impl_cleanup_for_context {
179 ($flavor:ident, $context:ident) => {
180 static REGISTER_ATEXIT: Once = Once::new();
181 static INSTANCE: RwLock<Option<ThreadHandle<$context<true, true>>>> = RwLock::new(None);
182
183 impl RcuCleaner<$flavor> {
184 extern "C" fn delete() {
185 ThreadHandle::<$context<true, true>>::delete(&INSTANCE);
186 }
187
188 pub fn get() -> RcuCleaner<$context<true, true>> {
189 REGISTER_ATEXIT.call_once(|| unsafe {
190 assert_eq!(libc::atexit(Self::delete), 0);
191 });
192
193 let context = Box::new(|| {
194 $flavor::rcu_context_builder()
195 .with_read_context()
196 .with_defer_context()
197 .register_thread()
198 .unwrap()
199 });
200
201 ThreadHandle::<$context<true, true>>::get(&INSTANCE, context)
202 }
203 }
204 };
205}
206
207#[cfg(feature = "flavor-bp")]
208mod bp {
209 use super::*;
210
211 use crate::rcu::context::RcuContextBp;
212 use crate::rcu::flavor::RcuFlavorBp;
213
214 impl_cleanup_for_context!(RcuFlavorBp, RcuContextBp);
215}
216
217#[cfg(feature = "flavor-mb")]
218mod mb {
219 use super::*;
220
221 use crate::rcu::context::RcuContextMb;
222 use crate::rcu::flavor::RcuFlavorMb;
223
224 impl_cleanup_for_context!(RcuFlavorMb, RcuContextMb);
225}
226
227#[cfg(feature = "flavor-memb")]
228mod memb {
229 use super::*;
230
231 use crate::rcu::context::RcuContextMemb;
232 use crate::rcu::flavor::RcuFlavorMemb;
233
234 impl_cleanup_for_context!(RcuFlavorMemb, RcuContextMemb);
235}
236
237#[cfg(feature = "flavor-qsbr")]
238mod qsbr {
239 use super::*;
240
241 use crate::rcu::context::RcuContextQsbr;
242 use crate::rcu::flavor::RcuFlavorQsbr;
243
244 impl_cleanup_for_context!(RcuFlavorQsbr, RcuContextQsbr);
245}