@@ -18,6 +18,111 @@ extern "C" {
1818 fn rust_helper_put_task_struct ( task : * mut bindings:: task_struct ) ;
1919}
2020
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+
21126/// Function passed to `kthread_create_on_node` as the thread function pointer.
22127#[ no_mangle]
23128unsafe extern "C" fn rust_thread_func ( data : * mut c_types:: c_void ) -> c_types:: c_int {
@@ -103,6 +208,7 @@ impl Thread {
103208 /// let mut a = 1;
104209 ///
105210 /// let t = Thread::try_new(
211+ /// cstr!("rust-thread"),
106212 /// move || {
107213 /// let b = Box::try_new(42)?;
108214 ///
@@ -111,8 +217,7 @@ impl Thread {
111217 /// println!("Hello Rust Thread {}", a + b.as_ref());
112218 /// }
113219 /// Ok(())
114- /// },
115- /// cstr!("rust-thread")
220+ /// }
116221 /// )?;
117222 ///
118223 /// t.wake_up();
@@ -132,33 +237,11 @@ impl Thread {
132237 // `rust_thread_func` (the function that uses the closure) get executed.
133238 let boxed_fn: Box < dyn FnOnce ( ) -> KernelResult < ( ) > + ' static > = Box :: try_new ( f) ?;
134239
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) ?;
147243
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)
162245 }
163246
164247 /// Wakes up the thread.
0 commit comments