Skip to content

Commit 6a9da4b

Browse files
committed
rust: thread: Introduce ThreadFunc
[`Thread::ThreadFunc`] provides a generic way to create a thread without extra dynamic memory allocation. Signed-off-by: Boqun Feng <[email protected]>
1 parent 0638186 commit 6a9da4b

File tree

2 files changed

+202
-29
lines changed

2 files changed

+202
-29
lines changed

drivers/char/rust_example.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use alloc::boxed::Box;
1010
use alloc::sync::Arc;
11+
use core::fmt::Debug;
1112
use core::pin::Pin;
1213
use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
1314
use kernel::prelude::*;
@@ -16,7 +17,7 @@ use kernel::{
1617
file_operations::FileOperations,
1718
miscdev, mutex_init, spinlock_init,
1819
sync::{CondVar, Mutex, SpinLock},
19-
thread::{schedule, Thread},
20+
thread::{schedule, BoxArg, PrimitiveArg, Thread, ThreadFunc},
2021
thread_try_new,
2122
};
2223

@@ -213,6 +214,49 @@ impl KernelModule for RustExample {
213214
t1.stop().expect("Rust thread should exit abnormally");
214215
}
215216

217+
// Test threads (create with ThreadFunc and BoxArg).
218+
{
219+
let b = Box::try_new(42)?;
220+
221+
struct SimpleBoxFunc;
222+
223+
impl<T> ThreadFunc<Box<T>> for SimpleBoxFunc
224+
where
225+
T: Debug + Send,
226+
{
227+
type Arg = BoxArg;
228+
fn func(arg: Box<T>) -> KernelResult<()> {
229+
println!("I got {:?}", arg);
230+
Ok(())
231+
}
232+
}
233+
234+
let t1 = Thread::try_new_thread_func::<_, SimpleBoxFunc>(cstr!("rust-thread"), b)?;
235+
236+
t1.wake_up();
237+
238+
// Don't care the result value.
239+
}
240+
241+
// Test threads (create with ThreadFunc and PrimitiveArg).
242+
{
243+
struct I32Func;
244+
245+
impl ThreadFunc<i32> for I32Func {
246+
type Arg = PrimitiveArg;
247+
fn func(arg: i32) -> KernelResult<()> {
248+
println!("I got i32 {}", arg);
249+
Ok(())
250+
}
251+
}
252+
253+
let t1 = Thread::try_new_thread_func::<_, I32Func>(cstr!("rust-thread"), 43)?;
254+
255+
t1.wake_up();
256+
257+
// Don't care the result value.
258+
}
259+
216260
// Including this large variable on the stack will trigger
217261
// stack probing on the supported archs.
218262
// This will verify that stack probing does not lead to

rust/kernel/thread.rs

Lines changed: 157 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ 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-
2521
/// Generates a bridge function from Rust to C ABI.
2622
///
2723
/// `func` should be a `fn(arg: arg_type) -> KernelResult<()>` where arg_type
@@ -117,7 +113,7 @@ macro_rules! thread_try_new {
117113
}
118114

119115
// Uses an unused closure to check whether `$arg` is `Send`.
120-
let _ = move || { _arg };
116+
let _ = move || _arg;
121117

122118
Err(e)
123119
} else {
@@ -126,26 +122,122 @@ macro_rules! thread_try_new {
126122
}};
127123
}
128124

129-
/*
125+
/// Helper trait for thread arguments handling.
126+
///
127+
/// Basically, we just handle object "sending" manually, for a [`Send`] object,
128+
/// [`ThreadArg::from_arg`] is used to convert it into a pointer that kernel's
129+
/// thread creation function will accept (e.g. `kthread_create_on_node`). And
130+
/// later in the thread function, [`ThreadArg::from_raw`] is used to convert it
131+
/// back.
132+
///
133+
/// # Safety
134+
///
135+
/// Both [`ThreadArg::from_arg`] and [`ThreadArg::from_raw`] are marked as
136+
/// `unsafe` because they are only allowed to use in pair.
130137
pub trait ThreadArg<A: Send> {
131-
fn from_raw(ptr: *mut c_types::c_void) -> A;
132-
fn into_raw(arg: A) -> *mut c_types::c_void;
138+
/// Converts a raw pointer to a thread argument.
139+
///
140+
/// # Safety
141+
///
142+
/// This function is marked as unsafe because a) the caller need to make
143+
/// sure `ptr` is a valid pointer and b) it should be used in pair with
144+
/// ['ThreadArg::from_arg`].
145+
unsafe fn from_raw(ptr: *mut c_types::c_void) -> A;
146+
147+
/// Converts a thread argument to a raw poitner.
148+
///
149+
/// # Safety
150+
///
151+
/// This function is marked as unsafe because it should be used in pair with
152+
/// ['ThreadArg::into_raw`], otherwise memory leak or other UBs may happen.
153+
unsafe fn from_arg(arg: A) -> *mut c_types::c_void;
133154
}
155+
156+
/// Helper trait for generate a C ABI bridge function.
157+
///
158+
/// Pass an `impl` of this trait as the second type parameter to
159+
/// [`Thread::try_new_thread_func`], the new thread call [`ThreadFunc::func`]
160+
/// if the creation succeeds.
134161
pub trait ThreadFunc<A: Send> {
162+
type Arg: ThreadArg<A>;
135163
fn func(arg: A) -> KernelResult<()>;
136164
}
137165

138-
extern "C" fn bridge<A, T: ThreadFunc<A>>(data: *mut c_types::c_void) -> i32
139-
where A : Send
166+
/// Predefined argument handling structs.
167+
///
168+
/// FIXME: Maybe put them into a sub mod?
169+
170+
171+
/// Helper struct for Box<A: Send> thread argument handling.
172+
///
173+
/// A helper [`ThreadFunc`] whose `func` is: `fn(Box<A>) -> KernelResult<()>`,
174+
/// can use it as the [`ThreadFunc::Arg`] type.
175+
pub struct BoxArg;
176+
177+
impl<T> ThreadArg<Box<T>> for BoxArg
178+
where
179+
T: Send,
180+
{
181+
unsafe fn from_raw(ptr: *mut c_types::c_void) -> Box<T> {
182+
Box::from_raw(ptr as *mut T)
183+
}
184+
unsafe fn from_arg(arg: Box<T>) -> *mut c_types::c_void {
185+
Box::into_raw(arg) as *mut _
186+
}
187+
}
188+
189+
pub struct PrimitiveArg;
190+
191+
macro_rules! primitive_arg_struct {
192+
($t:ty) => {
193+
impl ThreadArg<$t> for PrimitiveArg {
194+
unsafe fn from_raw(ptr: *mut c_types::c_void) -> $t {
195+
ptr as usize as $t
196+
}
197+
unsafe fn from_arg(arg: $t) -> *mut c_types::c_void {
198+
arg as usize as *mut _
199+
}
200+
}
201+
};
202+
}
203+
primitive_arg_struct! {i8}
204+
primitive_arg_struct! {i16}
205+
primitive_arg_struct! {i32}
206+
primitive_arg_struct! {i64}
207+
primitive_arg_struct! {isize}
208+
primitive_arg_struct! {u8}
209+
primitive_arg_struct! {u16}
210+
primitive_arg_struct! {u32}
211+
primitive_arg_struct! {u64}
212+
primitive_arg_struct! {usize}
213+
214+
/// Bridge function from Rust ABI to C.
215+
extern "C" fn bridge<A, F: ThreadFunc<A>>(data: *mut c_types::c_void) -> i32
216+
where
217+
A: Send,
140218
{
141-
let arg = unsafe { core::intrinsics::transmute(data) };
142-
143-
match T::func(arg) {
219+
let arg = unsafe { F::Arg::from_raw(data) };
220+
221+
match F::func(arg) {
144222
Ok(_) => 0,
145223
Err(e) => e.to_kernel_errno(),
146224
}
147225
}
148-
*/
226+
227+
/// Helper struct for thread that just call its argument as a closure.
228+
///
229+
/// We use double boxing in the implementation, because 1) we need to make sure
230+
/// the thread argument live longer enough for the new thread to use (box #1),
231+
/// and `FnOnce()` is a fat pointer that doesn't fit in `*mut c_void` (box #2).
232+
struct ClosureCallFunc;
233+
234+
impl ThreadFunc<Box<Box<dyn FnOnce() -> KernelResult<()> + Send>>> for ClosureCallFunc {
235+
type Arg = BoxArg;
236+
fn func(arg: Box<Box<dyn FnOnce() -> KernelResult<()> + Send>>) -> KernelResult<()> {
237+
// Just calls it.
238+
arg()
239+
}
240+
}
149241

150242
/// Function passed to `kthread_create_on_node` as the thread function pointer.
151243
#[no_mangle]
@@ -221,32 +313,69 @@ impl Thread {
221313
Ok(Thread { task })
222314
}
223315

224-
/*
225-
pub fn try_new_thread_func<A, T: ThreadFunc<A>>(name: CStr, arg: A) -> KernelResult<Self>
226-
where A: Send
316+
/// Creates a new thread without extra dynamic memory allocaiton
317+
///
318+
/// # Examples
319+
///
320+
/// ```
321+
/// use kernel::thread::{Thread, ThreadFunc, BoxArg};
322+
/// use alloc::boxed::Box;
323+
/// use core::fmt::Debug;
324+
///
325+
/// struct SimpleBoxFunc;
326+
///
327+
/// impl<T> ThreadFunc<Box<T>> for SimpleBoxFunc
328+
/// where T: Send + Debug
329+
/// {
330+
/// type Arg = BoxArg;
331+
/// fn func(b: Box<T>) -> KernelResult<()> {
332+
/// println!("I got {:?}", b);
333+
/// }
334+
/// }
335+
///
336+
/// let t = Thread::try_new_thread_func::<_, SimpleBoxFunc>(
337+
/// cstr!("rust-thread"),
338+
/// Box::try_new(42)?
339+
/// )?;
340+
///
341+
/// t.wake_up();
342+
/// ```
343+
///
344+
/// # Context
345+
///
346+
/// This function might sleep due to the memory allocation and waiting for
347+
/// the completion in `kthread_create_on_node`. Therefore do not call this
348+
/// in atomic contexts (i.e. preemption-off contexts).
349+
pub fn try_new_thread_func<A, F: ThreadFunc<A>>(name: CStr, arg: A) -> KernelResult<Self>
350+
where
351+
A: Send,
227352
{
228-
let data = unsafe { core::intrinsics::transmute(arg) };
353+
// SAFETY: We don't dereference the pointer we get from `from_arg`, and
354+
// we will consume the pointer with 'from_raw` either in the new thread
355+
// or the error handling below.
356+
let data = unsafe { F::Arg::from_arg(arg) };
229357

230-
let result = unsafe { Self::try_new_c_style(name, bridge::<A,T>, data) };
358+
// SAFETY: `bridge::<A,F>` is a proper function pointer to a C
359+
// function, and [`ThreadArg::from_raw`] will be used in it to consume
360+
// the raw pointer in the new thread.
361+
let result = unsafe { Self::try_new_c_style(name, bridge::<A, F>, data) };
231362

232363
if let Err(e) = result {
233-
// Creation fails, we need to `transmute` back the `arg` because
234-
// there is no new thread to own it, we should let the current
235-
// thread own it.
364+
// Creation fails, we need to consume the raw pointer `data` because
365+
// there is no new thread to own the underlying object, we should
366+
// let the current thread own it.
236367
//
237-
// SAFETY: We `transmute` back waht we just `transmute`, and since
368+
// SAFETY: We `from_raw` back what we just `from_arg`, and since
238369
// the new thread is not created, so no one touches `data`.
239370
unsafe {
240-
core::intrinsics::transmute::<_, A>(data);
371+
F::Arg::from_raw(data);
241372
}
242373

243374
Err(e)
244375
} else {
245376
result
246377
}
247-
248378
}
249-
*/
250379

251380
/// Creates a new thread.
252381
///
@@ -286,13 +415,13 @@ impl Thread {
286415
{
287416
// Allocate closure here, because this function maybe returns before
288417
// `rust_thread_func` (the function that uses the closure) get executed.
289-
let boxed_fn: Box<dyn FnOnce() -> KernelResult<()> + 'static> = Box::try_new(f)?;
418+
let boxed_fn: Box<dyn FnOnce() -> KernelResult<()> + 'static + Send> = Box::try_new(f)?;
290419

291420
// Double boxing here because `dyn FnOnce` is a fat pointer, and we cannot
292421
// `transmute` it to `*mut c_void`.
293422
let double_box = Box::try_new(boxed_fn)?;
294423

295-
thread_try_new!(name, call_as_closure, double_box)
424+
Self::try_new_thread_func::<_, ClosureCallFunc>(name, double_box)
296425
}
297426

298427
/// Wakes up the thread.

0 commit comments

Comments
 (0)