summaryrefslogtreecommitdiff
path: root/rust/kernel/kasync/executor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel/kasync/executor.rs')
-rw-r--r--rust/kernel/kasync/executor.rs154
1 files changed, 154 insertions, 0 deletions
diff --git a/rust/kernel/kasync/executor.rs b/rust/kernel/kasync/executor.rs
new file mode 100644
index 000000000000..c8dc1cb26bf5
--- /dev/null
+++ b/rust/kernel/kasync/executor.rs
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Kernel support for executing futures.
+
+use crate::{
+ sync::{Arc, ArcBorrow, LockClassKey},
+ types::PointerWrapper,
+ Result,
+};
+use core::{
+ future::Future,
+ task::{RawWaker, RawWakerVTable, Waker},
+};
+
+pub mod workqueue;
+
+/// Spawns a new task to run in the given executor.
+///
+/// It also automatically defines a new lockdep lock class for executors (e.g., workqueue) that
+/// require one.
+#[macro_export]
+macro_rules! spawn_task {
+ ($executor:expr, $task:expr) => {{
+ static CLASS: $crate::sync::LockClassKey = $crate::sync::LockClassKey::new();
+ $crate::kasync::executor::Executor::spawn($executor, &CLASS, $task)
+ }};
+}
+
+/// A task spawned in an executor.
+pub trait Task {
+ /// Synchronously stops the task.
+ ///
+ /// It ensures that the task won't run again and releases resources needed to run the task
+ /// (e.g., the closure is dropped). If the task is inflight, it waits for the task to block or
+ /// complete before cleaning up and returning.
+ ///
+ /// Callers must not call this from within the task itself as it will likely lead to a
+ /// deadlock.
+ fn sync_stop(self: Arc<Self>);
+}
+
+/// An environment for executing tasks.
+pub trait Executor: Sync + Send {
+ /// Starts executing a task defined by the given future.
+ ///
+ /// Callers are encouraged to use the [`spawn_task`] macro because it automatically defines a
+ /// new lock class key.
+ fn spawn(
+ self: ArcBorrow<'_, Self>,
+ lock_class_key: &'static LockClassKey,
+ future: impl Future + 'static + Send,
+ ) -> Result<Arc<dyn Task>>
+ where
+ Self: Sized;
+
+ /// Stops the executor.
+ ///
+ /// After it is called, attempts to spawn new tasks will result in an error and existing ones
+ /// won't be polled anymore.
+ fn stop(&self);
+}
+
+/// A waker that is wrapped in [`Arc`] for its reference counting.
+///
+/// Types that implement this trait can get a [`Waker`] by calling [`ref_waker`].
+pub trait ArcWake: Send + Sync {
+ /// Wakes a task up.
+ fn wake_by_ref(self: ArcBorrow<'_, Self>);
+
+ /// Wakes a task up and consumes a reference.
+ fn wake(self: Arc<Self>) {
+ self.as_arc_borrow().wake_by_ref();
+ }
+}
+
+/// Creates a [`Waker`] from a [`Arc<T>`], where `T` implements the [`ArcWake`] trait.
+pub fn ref_waker<T: 'static + ArcWake>(w: Arc<T>) -> Waker {
+ fn raw_waker<T: 'static + ArcWake>(w: Arc<T>) -> RawWaker {
+ let data = w.into_pointer();
+ RawWaker::new(
+ data.cast(),
+ &RawWakerVTable::new(clone::<T>, wake::<T>, wake_by_ref::<T>, drop::<T>),
+ )
+ }
+
+ unsafe fn clone<T: 'static + ArcWake>(ptr: *const ()) -> RawWaker {
+ // SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`.
+ let w = unsafe { Arc::<T>::borrow(ptr.cast()) };
+ raw_waker(w.into())
+ }
+
+ unsafe fn wake<T: 'static + ArcWake>(ptr: *const ()) {
+ // SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`.
+ let w = unsafe { Arc::<T>::from_pointer(ptr.cast()) };
+ w.wake();
+ }
+
+ unsafe fn wake_by_ref<T: 'static + ArcWake>(ptr: *const ()) {
+ // SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`.
+ let w = unsafe { Arc::<T>::borrow(ptr.cast()) };
+ w.wake_by_ref();
+ }
+
+ unsafe fn drop<T: 'static + ArcWake>(ptr: *const ()) {
+ // SAFETY: The data stored in the raw waker is the result of a call to `into_pointer`.
+ unsafe { Arc::<T>::from_pointer(ptr.cast()) };
+ }
+
+ let raw = raw_waker(w);
+ // SAFETY: The vtable of the raw waker satisfy the behaviour requirements of a waker.
+ unsafe { Waker::from_raw(raw) }
+}
+
+/// A handle to an executor that automatically stops it on drop.
+pub struct AutoStopHandle<T: Executor + ?Sized> {
+ executor: Option<Arc<T>>,
+}
+
+impl<T: Executor + ?Sized> AutoStopHandle<T> {
+ /// Creates a new instance of an [`AutoStopHandle`].
+ pub fn new(executor: Arc<T>) -> Self {
+ Self {
+ executor: Some(executor),
+ }
+ }
+
+ /// Detaches from the auto-stop handle.
+ ///
+ /// That is, extracts the executor from the handle and doesn't stop it anymore.
+ pub fn detach(mut self) -> Arc<T> {
+ self.executor.take().unwrap()
+ }
+
+ /// Returns the executor associated with the auto-stop handle.
+ ///
+ /// This is so that callers can, for example, spawn new tasks.
+ pub fn executor(&self) -> ArcBorrow<'_, T> {
+ self.executor.as_ref().unwrap().as_arc_borrow()
+ }
+}
+
+impl<T: Executor + ?Sized> Drop for AutoStopHandle<T> {
+ fn drop(&mut self) {
+ if let Some(ex) = self.executor.take() {
+ ex.stop();
+ }
+ }
+}
+
+impl<T: 'static + Executor> From<AutoStopHandle<T>> for AutoStopHandle<dyn Executor> {
+ fn from(src: AutoStopHandle<T>) -> Self {
+ Self::new(src.detach())
+ }
+}