Skip to content

Commit 9a87c30

Browse files
authored
Merge pull request torvalds#775 from wedsonaf/task-spawn
rust: introduce `Task::spawn`
2 parents d3ddae3 + 57ddcd6 commit 9a87c30

File tree

1 file changed

+88
-2
lines changed

1 file changed

+88
-2
lines changed

rust/kernel/task.rs

+88-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44
//!
55
//! C header: [`include/linux/sched.h`](../../../../include/linux/sched.h).
66
7-
use crate::{bindings, ARef, AlwaysRefCounted};
8-
use core::{cell::UnsafeCell, marker::PhantomData, ops::Deref, ptr};
7+
use crate::{
8+
bindings, c_str, c_types, error::from_kernel_err_ptr, types::PointerWrapper, ARef,
9+
AlwaysRefCounted, Result, ScopeGuard,
10+
};
11+
use alloc::boxed::Box;
12+
use core::{cell::UnsafeCell, fmt, marker::PhantomData, ops::Deref, ptr};
913

1014
/// Wraps the kernel's `struct task_struct`.
1115
///
@@ -101,6 +105,88 @@ impl Task {
101105
// SAFETY: By the type invariant, we know that `self.0` is valid.
102106
unsafe { bindings::signal_pending(self.0.get()) != 0 }
103107
}
108+
109+
/// Starts a new kernel thread and runs it.
110+
///
111+
/// # Examples
112+
///
113+
/// Launches 10 threads and waits for them to complete.
114+
///
115+
/// ```
116+
/// use kernel::task::Task;
117+
/// use kernel::sync::{CondVar, Mutex};
118+
/// use core::sync::atomic::{AtomicU32, Ordering};
119+
///
120+
/// kernel::init_static_sync! {
121+
/// static COUNT: Mutex<u32> = 0;
122+
/// static COUNT_IS_ZERO: CondVar;
123+
/// }
124+
///
125+
/// fn threadfn() {
126+
/// pr_info!("Running from thread {}\n", Task::current().pid());
127+
/// let mut guard = COUNT.lock();
128+
/// *guard -= 1;
129+
/// if *guard == 0 {
130+
/// COUNT_IS_ZERO.notify_all();
131+
/// }
132+
/// }
133+
///
134+
/// // Set count to 10 and spawn 10 threads.
135+
/// *COUNT.lock() = 10;
136+
/// for i in 0..10 {
137+
/// Task::spawn(fmt!("test{i}"), threadfn).unwrap();
138+
/// }
139+
///
140+
/// // Wait for count to drop to zero.
141+
/// let mut guard = COUNT.lock();
142+
/// while (*guard != 0) {
143+
/// COUNT_IS_ZERO.wait(&mut guard);
144+
/// }
145+
/// ```
146+
pub fn spawn<T: FnOnce() + Send + 'static>(
147+
name: fmt::Arguments<'_>,
148+
func: T,
149+
) -> Result<ARef<Task>> {
150+
unsafe extern "C" fn threadfn<T: FnOnce() + Send + 'static>(
151+
arg: *mut c_types::c_void,
152+
) -> c_types::c_int {
153+
// SAFETY: The thread argument is always a `Box<T>` because it is only called via the
154+
// thread creation below.
155+
let bfunc = unsafe { Box::<T>::from_pointer(arg) };
156+
bfunc();
157+
0
158+
}
159+
160+
let arg = Box::try_new(func)?.into_pointer();
161+
162+
// SAFETY: `arg` was just created with a call to `into_pointer` above.
163+
let guard = ScopeGuard::new(|| unsafe {
164+
Box::<T>::from_pointer(arg);
165+
});
166+
167+
// SAFETY: The function pointer is always valid (as long as the module remains loaded).
168+
// Ownership of `raw` is transferred to the new thread (if one is actually created), so it
169+
// remains valid. Lastly, the C format string is a constant that require formatting as the
170+
// one and only extra argument.
171+
let ktask = from_kernel_err_ptr(unsafe {
172+
bindings::kthread_create_on_node(
173+
Some(threadfn::<T>),
174+
arg as _,
175+
bindings::NUMA_NO_NODE,
176+
c_str!("%pA").as_char_ptr(),
177+
&name as *const _ as *const c_types::c_void,
178+
)
179+
})?;
180+
181+
// SAFETY: Since the kthread creation succeeded and we haven't run it yet, we know the task
182+
// is valid.
183+
let task = unsafe { &*(ktask as *const Task) }.into();
184+
185+
// SAFETY: Since the kthread creation succeeded, we know `ktask` is valid.
186+
unsafe { bindings::wake_up_process(ktask) };
187+
guard.dismiss();
188+
Ok(task)
189+
}
104190
}
105191

106192
// SAFETY: The type invariants guarantee that `Task` is always ref-counted.

0 commit comments

Comments
 (0)