Skip to content

Commit abe843f

Browse files
authored
Merge pull request #794 from wedsonaf/executor-common
rust: add `executor` module
2 parents d3c64e1 + 71dc964 commit abe843f

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

rust/kernel/kasync.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use core::{
88
task::{Context, Poll},
99
};
1010

11+
pub mod executor;
1112
#[cfg(CONFIG_NET)]
1213
pub mod net;
1314

rust/kernel/kasync/executor.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Kernel support for executing futures.
4+
5+
use crate::{
6+
sync::{LockClassKey, Ref, RefBorrow},
7+
types::PointerWrapper,
8+
Result,
9+
};
10+
use core::{
11+
future::Future,
12+
task::{RawWaker, RawWakerVTable, Waker},
13+
};
14+
15+
/// Spawns a new task to run in the given executor.
16+
///
17+
/// It also automatically defines a new lockdep lock class for executors (e.g., workqueue) that
18+
/// require one.
19+
#[macro_export]
20+
macro_rules! spawn_task {
21+
($executor:expr, $task:expr) => {{
22+
static CLASS: $crate::sync::LockClassKey = $crate::sync::LockClassKey::new();
23+
$crate::kasync::executor::Executor::spawn($executor, &CLASS, $task)
24+
}};
25+
}
26+
27+
/// A task spawned in an executor.
28+
pub trait Task {
29+
/// Synchronously stops the task.
30+
///
31+
/// It ensures that the task won't run again and releases resources needed to run the task
32+
/// (e.g., the closure is dropped). If the task is inflight, it waits for the task to block or
33+
/// complete before cleaning up and returning.
34+
///
35+
/// Callers must not call this from within the task itself as it will likely lead to a
36+
/// deadlock.
37+
fn sync_stop(self: Ref<Self>);
38+
}
39+
40+
/// An environment for executing tasks.
41+
pub trait Executor: Sync + Send {
42+
/// Starts executing a task defined by the given future.
43+
///
44+
/// Callers are encouraged to use the [`spawn_task`] macro because it automatically defines a
45+
/// new lock class key.
46+
fn spawn(
47+
self: RefBorrow<'_, Self>,
48+
lock_class_key: &'static LockClassKey,
49+
future: impl Future + 'static + Send,
50+
) -> Result<Ref<dyn Task>>
51+
where
52+
Self: Sized;
53+
54+
/// Stops the executor.
55+
///
56+
/// After it is called, attempts to spawn new tasks will result in an error and existing ones
57+
/// won't be polled anymore.
58+
fn stop(&self);
59+
}
60+
61+
/// A waker that is wrapped in [`Ref`] for its reference counting.
62+
///
63+
/// Types that implement this trait can get a [`Waker`] by calling [`ref_waker`].
64+
pub trait RefWake: Send + Sync {
65+
/// Wakes a task up.
66+
fn wake_by_ref(self: RefBorrow<'_, Self>);
67+
68+
/// Wakes a task up and consumes a reference.
69+
fn wake(self: Ref<Self>) {
70+
self.as_ref_borrow().wake_by_ref();
71+
}
72+
}
73+
74+
/// Creates a [`Waker`] from a [`Ref<T>`], where `T` implements the [`RefWake`] trait.
75+
pub fn ref_waker<T: 'static + RefWake>(w: Ref<T>) -> Waker {
76+
fn raw_waker<T: 'static + RefWake>(w: Ref<T>) -> RawWaker {
77+
let data = w.into_pointer();
78+
RawWaker::new(
79+
data.cast(),
80+
&RawWakerVTable::new(clone::<T>, wake::<T>, wake_by_ref::<T>, drop::<T>),
81+
)
82+
}
83+
84+
unsafe fn clone<T: 'static + RefWake>(ptr: *const ()) -> RawWaker {
85+
// SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`.
86+
let w = unsafe { Ref::<T>::borrow(ptr.cast()) };
87+
raw_waker(w.into())
88+
}
89+
90+
unsafe fn wake<T: 'static + RefWake>(ptr: *const ()) {
91+
// SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`.
92+
let w = unsafe { Ref::<T>::from_pointer(ptr.cast()) };
93+
w.wake();
94+
}
95+
96+
unsafe fn wake_by_ref<T: 'static + RefWake>(ptr: *const ()) {
97+
// SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`.
98+
let w = unsafe { Ref::<T>::borrow(ptr.cast()) };
99+
w.wake_by_ref();
100+
}
101+
102+
unsafe fn drop<T: 'static + RefWake>(ptr: *const ()) {
103+
// SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`.
104+
unsafe { Ref::<T>::from_pointer(ptr.cast()) };
105+
}
106+
107+
let raw = raw_waker(w);
108+
// SAFETY: The vtable of the raw waker satisfy the behaviour requirements of a waker.
109+
unsafe { Waker::from_raw(raw) }
110+
}
111+
112+
/// A handle to an executor that automatically stops it on drop.
113+
pub struct AutoStopHandle<T: Executor + ?Sized> {
114+
executor: Option<Ref<T>>,
115+
}
116+
117+
impl<T: Executor + ?Sized> AutoStopHandle<T> {
118+
/// Creates a new instance of an [`AutoStopHandle`].
119+
pub fn new(executor: Ref<T>) -> Self {
120+
Self {
121+
executor: Some(executor),
122+
}
123+
}
124+
125+
/// Detaches from the auto-stop handle.
126+
///
127+
/// That is, extracts the executor from the handle and doesn't stop it anymore.
128+
pub fn detach(mut self) -> Ref<T> {
129+
self.executor.take().unwrap()
130+
}
131+
132+
/// Returns the executor associated with the auto-stop handle.
133+
///
134+
/// This is so that callers can, for example, spawn new tasks.
135+
pub fn executor(&self) -> RefBorrow<'_, T> {
136+
self.executor.as_ref().unwrap().as_ref_borrow()
137+
}
138+
}
139+
140+
impl<T: Executor + ?Sized> Drop for AutoStopHandle<T> {
141+
fn drop(&mut self) {
142+
if let Some(ex) = self.executor.take() {
143+
ex.stop();
144+
}
145+
}
146+
}
147+
148+
impl<T: 'static + Executor> From<AutoStopHandle<T>> for AutoStopHandle<dyn Executor> {
149+
fn from(src: AutoStopHandle<T>) -> Self {
150+
Self::new(src.detach())
151+
}
152+
}

0 commit comments

Comments
 (0)