urcu/rcu/callback.rs
1use std::ffi::c_void;
2use std::marker::PhantomData;
3use std::ptr::NonNull;
4
5use container_of::container_of;
6use urcu_sys::RcuHead;
7
8/// This trait defines a callback to be invoked after the next RCU grace period.
9///
10/// #### Implementation
11///
12/// Each flavor have a RCU linked list of `(callback, head)` pointers. Each RCU
13/// threads can queue callbacks in this list. A helper thread will pop callbacks
14/// and execute them with `callback(head)`.
15///
16/// #### Safety
17///
18/// When [`RcuCall::configure`] is called, you must deliberately leak your type
19/// (e.g. [`Box::into_raw`]) to prevent the memory from being freed. Upon execution
20/// of the callback, you must get back ownership (e.g. [`Box::from_raw`]) and properly
21/// free up memory. For an example, see [`RcuCallFn`].
22///
23/// Unlike [`RcuDefer`], we resulting pointer must be an [`RcuHead`] inside
24/// your data. You can use [`container_of!`] to get back the type implementing this
25/// trait.
26pub unsafe trait RcuCall {
27 /// Configures the callback for execution.
28 fn configure<F>(self: Box<Self>, func: F)
29 where
30 F: FnOnce(NonNull<RcuHead>, unsafe extern "C" fn(head: *mut RcuHead));
31}
32
33/// Defines a callback executed after the next RCU grace period.
34pub struct RcuCallFn<F> {
35 func: F,
36 head: RcuHead,
37}
38
39impl<F> RcuCallFn<F> {
40 /// Create a simple RCU callback.
41 pub fn new(func: F) -> Box<Self> {
42 Box::new(Self {
43 func,
44 head: Default::default(),
45 })
46 }
47
48 unsafe extern "C" fn rcu_callback(head_ptr: *mut RcuHead)
49 where
50 F: FnOnce(),
51 {
52 // SAFETY: The pointers should always be valid.
53 let node = Box::from_raw(container_of!(head_ptr, Self, head));
54
55 (node.func)();
56 }
57}
58
59/// #### Safety
60///
61/// The memory of [`Box<Self>`] is properly reclaimed upon the RCU callback.
62unsafe impl<F> RcuCall for RcuCallFn<F>
63where
64 F: FnOnce(),
65{
66 fn configure<P>(self: Box<Self>, func: P)
67 where
68 P: FnOnce(NonNull<RcuHead>, unsafe extern "C" fn(head: *mut RcuHead)),
69 {
70 let node_ptr = Box::into_raw(self);
71 let node = unsafe { &mut *node_ptr };
72
73 unsafe {
74 func(NonNull::new_unchecked(&mut node.head), Self::rcu_callback);
75 }
76 }
77}
78
79/// #### Safety
80///
81/// The callback can be sent to another thread if the reference implements [`Send`].
82unsafe impl<F> Send for RcuCallFn<F> where F: Send {}
83
84/// This trait defines a callback to be invoked after the next RCU grace period.
85///
86/// #### Implementation
87///
88/// Each RCU thread have an array of `(callback, data)` pointers. When the next RCU
89/// grace period finishes, the thread goes over each of its entry and execute
90/// `callback(data)`.
91///
92/// #### Safety
93///
94/// When [`RcuDefer::configure`] is called, you must deliberately leak your type
95/// (e.g. [`Box::into_raw`]) to prevent the memory from being freed. Upon execution
96/// of the callback, you must get back ownership (e.g. [`Box::from_raw`]) and properly
97/// free up memory. For an example, see [`RcuDeferFn`].
98pub unsafe trait RcuDefer {
99 /// Configures the callback for execution.
100 fn configure<F>(self: Box<Self>, func: F)
101 where
102 F: FnOnce(NonNull<c_void>, unsafe extern "C" fn(head: *mut c_void));
103}
104
105/// Defines a defer callback executed after the next RCU grace period.
106pub struct RcuDeferFn<F, C> {
107 func: F,
108 // Also prevents Send+Sync auto-trait implementations.
109 _context: PhantomData<*mut C>,
110}
111
112/// #### Safety
113///
114/// The memory of [`Box<Self>`] is properly reclaimed upon the RCU callback.
115impl<F, C> RcuDeferFn<F, C> {
116 /// Creates a callback.
117 pub fn new(func: F) -> Box<Self> {
118 Box::new(Self {
119 func,
120 _context: PhantomData,
121 })
122 }
123
124 unsafe extern "C" fn callback(ptr: *mut c_void)
125 where
126 F: FnOnce(),
127 {
128 // SAFETY: The pointers should always be valid.
129 let node = Box::from_raw(ptr as *mut Self);
130
131 (node.func)();
132 }
133}
134
135unsafe impl<F, C> RcuDefer for RcuDeferFn<F, C>
136where
137 F: FnOnce(),
138{
139 fn configure<P>(self: Box<Self>, func: P)
140 where
141 P: FnOnce(NonNull<c_void>, unsafe extern "C" fn(head: *mut c_void)),
142 {
143 let ptr = Box::into_raw(self) as *mut c_void;
144
145 // SAFETY: The pointer is never null.
146 unsafe { func(NonNull::new_unchecked(ptr), Self::callback) }
147 }
148}