@@ -18,6 +18,111 @@ extern "C" {
18
18
fn rust_helper_put_task_struct ( task : * mut bindings:: task_struct ) ;
19
19
}
20
20
21
+ fn call_as_closure ( closure : Box < Box < dyn FnOnce ( ) -> KernelResult < ( ) > > > ) -> KernelResult < ( ) > {
22
+ closure ( )
23
+ }
24
+
25
+ /// Generates a bridge function from Rust to C ABI.
26
+ ///
27
+ /// `func` should be a `fn(arg: arg_type) -> KernelResult<()>` where arg_type
28
+ /// is the same size as `*mut c_types::c_void` ([`core::intrinsics::transmute`]
29
+ /// checks that at the compile time.
30
+ #[ macro_export]
31
+ macro_rules! bridge {
32
+ ( $func: expr) => { {
33
+ unsafe extern "C" fn _func( data: * mut $crate:: c_types:: c_void) -> $crate:: c_types:: c_int {
34
+ let arg = core:: intrinsics:: transmute( data) ;
35
+ let f: fn ( _) -> _ = $func; // Makes sure `$func` is a function pointer
36
+
37
+ match f( arg) {
38
+ Ok ( ( ) ) => 0 ,
39
+ Err ( e) => e. to_kernel_errno( ) ,
40
+ }
41
+ }
42
+
43
+ _func
44
+ } } ;
45
+ }
46
+
47
+ /// Creates a new thread without extra memory allocation.
48
+ ///
49
+ /// This macro tasks a Rust function pointer `$func` and an argument `$arg`,
50
+ /// and creates a thread doing `$func($arg)`, the return value of $func is
51
+ /// [`KernelError<()>`].
52
+ ///
53
+ /// # Examples
54
+ ///
55
+ /// ```
56
+ /// use kernel::thread::{schedule, Thread};
57
+ /// use kernel::thread_try_new;
58
+ /// use alloc::sync::Arc;
59
+ /// use core::sync::atomic::{AtomicUsize, Ordering};
60
+ ///
61
+ /// let arc = Arc::try_new(AtomicUsize::new(0))?;
62
+ ///
63
+ /// let t = thread_try_new!(
64
+ /// cstr!("rust-thread"),
65
+ /// |x: Arc<AtomicUsize>| -> KernelResult<()> {
66
+ /// for _ in 0..10 {
67
+ /// x.fetch_add(1, Ordering::Release);
68
+ /// println!("x is {}", x.load(Ordering::Relaxed));
69
+ /// }
70
+ /// Ok(())
71
+ /// },
72
+ /// arc.clone()
73
+ /// )?;
74
+ ///
75
+ /// t.wake_up();
76
+ ///
77
+ /// while arc.load(Ordering::Acquire) != 10 {
78
+ /// schedule();
79
+ /// }
80
+ ///
81
+ /// println!("main thread: x is {}", arc.load(Ordering::Relaxed));
82
+ /// ```
83
+ ///
84
+ /// # Context
85
+ ///
86
+ /// This macro might sleep due to the memory allocation and waiting for
87
+ /// the completion in `kthread_create_on_node`. Therefore do not call this
88
+ /// in atomic contexts (i.e. preemption-off contexts).
89
+ #[ macro_export]
90
+ macro_rules! thread_try_new {
91
+ ( $name: expr, $func: expr, $arg: expr) => { {
92
+ // In case of failure, we need to `transmute` the `$arg` back, `_arg` is
93
+ // used here to inference the type of `$arg`, so that the `transmute`
94
+ // in the failure path knows the type.
95
+ let mut _arg = $arg;
96
+
97
+ // TYPE CHECK: `$arg` should be the same as `*mut c_void`, and
98
+ // `transmute` only works if two types are of the same size.
99
+ //
100
+ // SAFETY: In the bridge funciton, the `$arg` is `transmute` back.
101
+ let data = unsafe { core:: intrinsics:: transmute( _arg) } ;
102
+
103
+ // SAFTEY: a) the bridge function is a valid function pointer, and b)
104
+ // the bridge function `transmute` back what we just `transmute`.
105
+ let result =
106
+ unsafe { $crate:: thread:: Thread :: try_new_c_style( $name, $crate:: bridge!( $func) , data) } ;
107
+
108
+ if let Err ( e) = result {
109
+ // Creation fails, we need to `transmute` back the `$arg` because
110
+ // there is no new thread to own it, we should let the current
111
+ // thread own it.
112
+ //
113
+ // SAFETY: We `transmute` back waht we just `transmute`, and since
114
+ // the new thread is not created, so no one touches `data`.
115
+ unsafe {
116
+ _arg = core:: intrinsics:: transmute( data) ;
117
+ }
118
+
119
+ Err ( e)
120
+ } else {
121
+ result
122
+ }
123
+ } } ;
124
+ }
125
+
21
126
/// Function passed to `kthread_create_on_node` as the thread function pointer.
22
127
#[ no_mangle]
23
128
unsafe extern "C" fn rust_thread_func ( data : * mut c_types:: c_void ) -> c_types:: c_int {
@@ -103,6 +208,7 @@ impl Thread {
103
208
/// let mut a = 1;
104
209
///
105
210
/// let t = Thread::try_new(
211
+ /// cstr!("rust-thread"),
106
212
/// move || {
107
213
/// let b = Box::try_new(42)?;
108
214
///
@@ -111,8 +217,7 @@ impl Thread {
111
217
/// println!("Hello Rust Thread {}", a + b.as_ref());
112
218
/// }
113
219
/// Ok(())
114
- /// },
115
- /// cstr!("rust-thread")
220
+ /// }
116
221
/// )?;
117
222
///
118
223
/// t.wake_up();
@@ -132,33 +237,11 @@ impl Thread {
132
237
// `rust_thread_func` (the function that uses the closure) get executed.
133
238
let boxed_fn: Box < dyn FnOnce ( ) -> KernelResult < ( ) > + ' static > = Box :: try_new ( f) ?;
134
239
135
- // Double boxing here because `dyn FnOnce` is a fat pointer, and we can only
136
- // pass a `usize` as the `data` for `kthread_create_on_node`.
137
- //
138
- // We `Box::into_raw` from this side, and will `Box::from_raw` at the other
139
- // side to transfer the ownership of the boxed data.
140
- let double_box_ptr = Box :: into_raw ( Box :: try_new ( boxed_fn) ?) as * mut _ ;
141
-
142
- // SAFETY: a) `double_box_ptr` is a proper pointer (generated by `Box::into_raw`),
143
- // and if succeed, the new thread will get the ownership. And b) `rust_thread_func`
144
- // is provided by us and correctly handles the dereference of the `double_box_ptr`
145
- // (via `Box::from_raw`).
146
- let result = unsafe { Self :: try_new_c_style ( name, rust_thread_func, double_box_ptr) } ;
240
+ // Double boxing here because `dyn FnOnce` is a fat pointer, and we cannot
241
+ // `transmute` it to `*mut c_void`.
242
+ let double_box = Box :: try_new ( boxed_fn) ?;
147
243
148
- if let Err ( e) = result {
149
- // Creation fails, we need to get back the double boxed closure.
150
- //
151
- // SAFETY: `double_box_ptr` is a proper pointer generated by a `Box::into_raw()`
152
- // from a box created by us. If the thread creation fails, no one will reference
153
- // that pointer.
154
- unsafe {
155
- Box :: from_raw ( double_box_ptr) ;
156
- }
157
-
158
- Err ( e)
159
- } else {
160
- result
161
- }
244
+ thread_try_new ! ( name, call_as_closure, double_box)
162
245
}
163
246
164
247
/// Wakes up the thread.
0 commit comments