Move Task into raw
This commit is contained in:
parent
0cc2c67194
commit
5c2bf3981e
@ -100,11 +100,12 @@ pub fn task(args: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
let result = quote! {
|
let result = quote! {
|
||||||
#visibility fn #name(#args) -> ::embassy::executor::SpawnToken<#impl_ty> {
|
#visibility fn #name(#args) -> ::embassy::executor::SpawnToken<#impl_ty> {
|
||||||
|
use ::embassy::executor::raw::Task;
|
||||||
#task_fn
|
#task_fn
|
||||||
type F = #impl_ty;
|
type F = #impl_ty;
|
||||||
const NEW_TASK: ::embassy::executor::Task<F> = ::embassy::executor::Task::new();
|
const NEW_TASK: Task<F> = Task::new();
|
||||||
static POOL: [::embassy::executor::Task<F>; #pool_size] = [NEW_TASK; #pool_size];
|
static POOL: [Task<F>; #pool_size] = [NEW_TASK; #pool_size];
|
||||||
unsafe { ::embassy::executor::Task::spawn(&POOL, move || task(#arg_names)) }
|
unsafe { Task::spawn(&POOL, move || task(#arg_names)) }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
result.into()
|
result.into()
|
||||||
|
@ -20,71 +20,9 @@ use crate::fmt::panic;
|
|||||||
use crate::interrupt::{Interrupt, InterruptExt};
|
use crate::interrupt::{Interrupt, InterruptExt};
|
||||||
use crate::time::Alarm;
|
use crate::time::Alarm;
|
||||||
|
|
||||||
// repr(C) is needed to guarantee that the raw::Task is located at offset 0
|
|
||||||
// This makes it safe to cast between raw::Task and Task pointers.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Task<F: Future + 'static> {
|
|
||||||
raw: raw::Task,
|
|
||||||
future: UninitCell<F>, // Valid if STATE_SPAWNED
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: Future + 'static> Task<F> {
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
raw: raw::Task::new(),
|
|
||||||
future: UninitCell::uninit(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn spawn(pool: &'static [Self], future: impl FnOnce() -> F) -> SpawnToken<F> {
|
|
||||||
for task in pool {
|
|
||||||
let state = raw::STATE_SPAWNED | raw::STATE_RUN_QUEUED;
|
|
||||||
if task
|
|
||||||
.raw
|
|
||||||
.state
|
|
||||||
.compare_exchange(0, state, Ordering::AcqRel, Ordering::Acquire)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
// Initialize the task
|
|
||||||
task.raw.poll_fn.write(Self::poll);
|
|
||||||
task.future.write(future());
|
|
||||||
|
|
||||||
return SpawnToken {
|
|
||||||
raw_task: Some(NonNull::new_unchecked(&task.raw as *const raw::Task as _)),
|
|
||||||
phantom: PhantomData,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SpawnToken {
|
|
||||||
raw_task: None,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn poll(p: NonNull<raw::Task>) {
|
|
||||||
let this = &*(p.as_ptr() as *const Task<F>);
|
|
||||||
|
|
||||||
let future = Pin::new_unchecked(this.future.as_mut());
|
|
||||||
let waker = waker::from_task(p);
|
|
||||||
let mut cx = Context::from_waker(&waker);
|
|
||||||
match future.poll(&mut cx) {
|
|
||||||
Poll::Ready(_) => {
|
|
||||||
this.future.drop_in_place();
|
|
||||||
this.raw
|
|
||||||
.state
|
|
||||||
.fetch_and(!raw::STATE_SPAWNED, Ordering::AcqRel);
|
|
||||||
}
|
|
||||||
Poll::Pending => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<F: Future + 'static> Sync for Task<F> {}
|
|
||||||
|
|
||||||
#[must_use = "Calling a task function does nothing on its own. You must pass the returned SpawnToken to Executor::spawn()"]
|
#[must_use = "Calling a task function does nothing on its own. You must pass the returned SpawnToken to Executor::spawn()"]
|
||||||
pub struct SpawnToken<F> {
|
pub struct SpawnToken<F> {
|
||||||
raw_task: Option<NonNull<raw::Task>>,
|
raw_task: Option<NonNull<raw::TaskHeader>>,
|
||||||
phantom: PhantomData<*mut F>,
|
phantom: PhantomData<*mut F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
use atomic_polyfill::{AtomicU32, Ordering};
|
use atomic_polyfill::{AtomicU32, Ordering};
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core::cmp::min;
|
use core::cmp::min;
|
||||||
|
use core::future::Future;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use core::pin::Pin;
|
||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use core::task::Waker;
|
use core::task::{Context, Poll, Waker};
|
||||||
|
|
||||||
use super::run_queue::{RunQueue, RunQueueItem};
|
use super::run_queue::{RunQueue, RunQueueItem};
|
||||||
use super::timer_queue::{TimerQueue, TimerQueueItem};
|
use super::timer_queue::{TimerQueue, TimerQueueItem};
|
||||||
use super::util::UninitCell;
|
use super::util::UninitCell;
|
||||||
use super::waker;
|
use super::waker;
|
||||||
|
use super::SpawnToken;
|
||||||
use crate::time::{Alarm, Instant};
|
use crate::time::{Alarm, Instant};
|
||||||
|
|
||||||
/// Task is spawned (has a future)
|
/// Task is spawned (has a future)
|
||||||
@ -19,16 +22,16 @@ pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
|||||||
/// Task is in the executor timer queue
|
/// Task is in the executor timer queue
|
||||||
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
||||||
|
|
||||||
pub struct Task {
|
pub struct TaskHeader {
|
||||||
pub(crate) state: AtomicU32,
|
pub(crate) state: AtomicU32,
|
||||||
pub(crate) run_queue_item: RunQueueItem,
|
pub(crate) run_queue_item: RunQueueItem,
|
||||||
pub(crate) expires_at: Cell<Instant>,
|
pub(crate) expires_at: Cell<Instant>,
|
||||||
pub(crate) timer_queue_item: TimerQueueItem,
|
pub(crate) timer_queue_item: TimerQueueItem,
|
||||||
pub(crate) executor: Cell<*const Executor>, // Valid if state != 0
|
pub(crate) executor: Cell<*const Executor>, // Valid if state != 0
|
||||||
pub(crate) poll_fn: UninitCell<unsafe fn(NonNull<Task>)>, // Valid if STATE_SPAWNED
|
pub(crate) poll_fn: UninitCell<unsafe fn(NonNull<TaskHeader>)>, // Valid if STATE_SPAWNED
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Task {
|
impl TaskHeader {
|
||||||
pub(crate) const fn new() -> Self {
|
pub(crate) const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: AtomicU32::new(0),
|
state: AtomicU32::new(0),
|
||||||
@ -64,10 +67,70 @@ impl Task {
|
|||||||
|
|
||||||
// We have just marked the task as scheduled, so enqueue it.
|
// We have just marked the task as scheduled, so enqueue it.
|
||||||
let executor = &*self.executor.get();
|
let executor = &*self.executor.get();
|
||||||
executor.enqueue(self as *const Task as *mut Task);
|
executor.enqueue(self as *const TaskHeader as *mut TaskHeader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// repr(C) is needed to guarantee that the Task is located at offset 0
|
||||||
|
// This makes it safe to cast between Task and Task pointers.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Task<F: Future + 'static> {
|
||||||
|
raw: TaskHeader,
|
||||||
|
future: UninitCell<F>, // Valid if STATE_SPAWNED
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: Future + 'static> Task<F> {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
raw: TaskHeader::new(),
|
||||||
|
future: UninitCell::uninit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn spawn(pool: &'static [Self], future: impl FnOnce() -> F) -> SpawnToken<F> {
|
||||||
|
for task in pool {
|
||||||
|
let state = STATE_SPAWNED | STATE_RUN_QUEUED;
|
||||||
|
if task
|
||||||
|
.raw
|
||||||
|
.state
|
||||||
|
.compare_exchange(0, state, Ordering::AcqRel, Ordering::Acquire)
|
||||||
|
.is_ok()
|
||||||
|
{
|
||||||
|
// Initialize the task
|
||||||
|
task.raw.poll_fn.write(Self::poll);
|
||||||
|
task.future.write(future());
|
||||||
|
|
||||||
|
return SpawnToken {
|
||||||
|
raw_task: Some(NonNull::new_unchecked(&task.raw as *const TaskHeader as _)),
|
||||||
|
phantom: PhantomData,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnToken {
|
||||||
|
raw_task: None,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn poll(p: NonNull<TaskHeader>) {
|
||||||
|
let this = &*(p.as_ptr() as *const Task<F>);
|
||||||
|
|
||||||
|
let future = Pin::new_unchecked(this.future.as_mut());
|
||||||
|
let waker = waker::from_task(p);
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
match future.poll(&mut cx) {
|
||||||
|
Poll::Ready(_) => {
|
||||||
|
this.future.drop_in_place();
|
||||||
|
this.raw.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel);
|
||||||
|
}
|
||||||
|
Poll::Pending => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<F: Future + 'static> Sync for Task<F> {}
|
||||||
|
|
||||||
pub struct Executor {
|
pub struct Executor {
|
||||||
run_queue: RunQueue,
|
run_queue: RunQueue,
|
||||||
timer_queue: TimerQueue,
|
timer_queue: TimerQueue,
|
||||||
@ -95,13 +158,13 @@ impl Executor {
|
|||||||
self.signal_ctx = signal_ctx;
|
self.signal_ctx = signal_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn enqueue(&self, item: *mut Task) {
|
unsafe fn enqueue(&self, item: *mut TaskHeader) {
|
||||||
if self.run_queue.enqueue(item) {
|
if self.run_queue.enqueue(item) {
|
||||||
(self.signal_fn)(self.signal_ctx)
|
(self.signal_fn)(self.signal_ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn spawn(&'static self, task: NonNull<Task>) {
|
pub unsafe fn spawn(&'static self, task: NonNull<TaskHeader>) {
|
||||||
let task = task.as_ref();
|
let task = task.as_ref();
|
||||||
task.executor.set(self);
|
task.executor.set(self);
|
||||||
self.enqueue(task as *const _ as _);
|
self.enqueue(task as *const _ as _);
|
||||||
@ -154,7 +217,7 @@ impl Executor {
|
|||||||
|
|
||||||
pub use super::waker::task_from_waker;
|
pub use super::waker::task_from_waker;
|
||||||
|
|
||||||
pub unsafe fn wake_task(task: NonNull<Task>) {
|
pub unsafe fn wake_task(task: NonNull<TaskHeader>) {
|
||||||
task.as_ref().enqueue();
|
task.as_ref().enqueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@ use atomic_polyfill::{AtomicPtr, Ordering};
|
|||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
use super::raw::Task;
|
use super::raw::TaskHeader;
|
||||||
|
|
||||||
pub(crate) struct RunQueueItem {
|
pub(crate) struct RunQueueItem {
|
||||||
next: AtomicPtr<Task>,
|
next: AtomicPtr<TaskHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunQueueItem {
|
impl RunQueueItem {
|
||||||
@ -28,7 +28,7 @@ impl RunQueueItem {
|
|||||||
/// current batch is completely processed, so even if a task enqueues itself instantly (for example
|
/// current batch is completely processed, so even if a task enqueues itself instantly (for example
|
||||||
/// by waking its own waker) can't prevent other tasks from running.
|
/// by waking its own waker) can't prevent other tasks from running.
|
||||||
pub(crate) struct RunQueue {
|
pub(crate) struct RunQueue {
|
||||||
head: AtomicPtr<Task>,
|
head: AtomicPtr<TaskHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RunQueue {
|
impl RunQueue {
|
||||||
@ -39,7 +39,7 @@ impl RunQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Enqueues an item. Returns true if the queue was empty.
|
/// Enqueues an item. Returns true if the queue was empty.
|
||||||
pub(crate) unsafe fn enqueue(&self, item: *mut Task) -> bool {
|
pub(crate) unsafe fn enqueue(&self, item: *mut TaskHeader) -> bool {
|
||||||
let mut prev = self.head.load(Ordering::Acquire);
|
let mut prev = self.head.load(Ordering::Acquire);
|
||||||
loop {
|
loop {
|
||||||
(*item).run_queue_item.next.store(prev, Ordering::Relaxed);
|
(*item).run_queue_item.next.store(prev, Ordering::Relaxed);
|
||||||
@ -55,7 +55,7 @@ impl RunQueue {
|
|||||||
prev.is_null()
|
prev.is_null()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn dequeue_all(&self, on_task: impl Fn(NonNull<Task>)) {
|
pub(crate) unsafe fn dequeue_all(&self, on_task: impl Fn(NonNull<TaskHeader>)) {
|
||||||
let mut task = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
|
let mut task = self.head.swap(ptr::null_mut(), Ordering::AcqRel);
|
||||||
|
|
||||||
while !task.is_null() {
|
while !task.is_null() {
|
||||||
|
@ -4,11 +4,11 @@ use core::cmp::min;
|
|||||||
use core::ptr;
|
use core::ptr;
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
|
|
||||||
use super::raw::{Task, STATE_TIMER_QUEUED};
|
use super::raw::{TaskHeader, STATE_TIMER_QUEUED};
|
||||||
use crate::time::Instant;
|
use crate::time::Instant;
|
||||||
|
|
||||||
pub(crate) struct TimerQueueItem {
|
pub(crate) struct TimerQueueItem {
|
||||||
next: Cell<*mut Task>,
|
next: Cell<*mut TaskHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimerQueueItem {
|
impl TimerQueueItem {
|
||||||
@ -20,7 +20,7 @@ impl TimerQueueItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct TimerQueue {
|
pub(crate) struct TimerQueue {
|
||||||
head: Cell<*mut Task>,
|
head: Cell<*mut TaskHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimerQueue {
|
impl TimerQueue {
|
||||||
@ -30,7 +30,7 @@ impl TimerQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn update(&self, p: NonNull<Task>) {
|
pub(crate) unsafe fn update(&self, p: NonNull<TaskHeader>) {
|
||||||
let task = p.as_ref();
|
let task = p.as_ref();
|
||||||
if task.expires_at.get() != Instant::MAX {
|
if task.expires_at.get() != Instant::MAX {
|
||||||
let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
|
let old_state = task.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
|
||||||
@ -54,7 +54,11 @@ impl TimerQueue {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn dequeue_expired(&self, now: Instant, on_task: impl Fn(NonNull<Task>)) {
|
pub(crate) unsafe fn dequeue_expired(
|
||||||
|
&self,
|
||||||
|
now: Instant,
|
||||||
|
on_task: impl Fn(NonNull<TaskHeader>),
|
||||||
|
) {
|
||||||
self.retain(|p| {
|
self.retain(|p| {
|
||||||
let task = p.as_ref();
|
let task = p.as_ref();
|
||||||
if task.expires_at.get() <= now {
|
if task.expires_at.get() <= now {
|
||||||
@ -66,7 +70,7 @@ impl TimerQueue {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn retain(&self, mut f: impl FnMut(NonNull<Task>) -> bool) {
|
pub(crate) unsafe fn retain(&self, mut f: impl FnMut(NonNull<TaskHeader>) -> bool) {
|
||||||
let mut prev = &self.head;
|
let mut prev = &self.head;
|
||||||
while !prev.get().is_null() {
|
while !prev.get().is_null() {
|
||||||
let p = NonNull::new_unchecked(prev.get());
|
let p = NonNull::new_unchecked(prev.get());
|
||||||
|
@ -2,7 +2,7 @@ use core::mem;
|
|||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use core::task::{RawWaker, RawWakerVTable, Waker};
|
use core::task::{RawWaker, RawWakerVTable, Waker};
|
||||||
|
|
||||||
use super::raw::Task;
|
use super::raw::TaskHeader;
|
||||||
|
|
||||||
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
|
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop);
|
||||||
|
|
||||||
@ -11,21 +11,21 @@ unsafe fn clone(p: *const ()) -> RawWaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn wake(p: *const ()) {
|
unsafe fn wake(p: *const ()) {
|
||||||
(*(p as *mut Task)).enqueue()
|
(*(p as *mut TaskHeader)).enqueue()
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn drop(_: *const ()) {
|
unsafe fn drop(_: *const ()) {
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn from_task(p: NonNull<Task>) -> Waker {
|
pub(crate) unsafe fn from_task(p: NonNull<TaskHeader>) -> Waker {
|
||||||
Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE))
|
Waker::from_raw(RawWaker::new(p.as_ptr() as _, &VTABLE))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub unsafe fn task_from_waker(waker: &Waker) -> NonNull<Task> {
|
pub unsafe fn task_from_waker(waker: &Waker) -> NonNull<TaskHeader> {
|
||||||
let hack: &WakerHack = mem::transmute(waker);
|
let hack: &WakerHack = mem::transmute(waker);
|
||||||
assert_eq!(hack.vtable, &VTABLE);
|
assert_eq!(hack.vtable, &VTABLE);
|
||||||
NonNull::new_unchecked(hack.data as *mut Task)
|
NonNull::new_unchecked(hack.data as *mut TaskHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WakerHack {
|
struct WakerHack {
|
||||||
|
@ -3,12 +3,12 @@ use core::task::Waker;
|
|||||||
|
|
||||||
use atomic_polyfill::{AtomicPtr, Ordering};
|
use atomic_polyfill::{AtomicPtr, Ordering};
|
||||||
|
|
||||||
use crate::executor::raw::{task_from_waker, wake_task, Task};
|
use crate::executor::raw::{task_from_waker, wake_task, TaskHeader};
|
||||||
|
|
||||||
/// Utility struct to register and wake a waker.
|
/// Utility struct to register and wake a waker.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WakerRegistration {
|
pub struct WakerRegistration {
|
||||||
waker: Option<NonNull<Task>>,
|
waker: Option<NonNull<TaskHeader>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WakerRegistration {
|
impl WakerRegistration {
|
||||||
@ -49,7 +49,7 @@ impl WakerRegistration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct AtomicWakerRegistration {
|
pub struct AtomicWakerRegistration {
|
||||||
waker: AtomicPtr<Task>,
|
waker: AtomicPtr<TaskHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AtomicWakerRegistration {
|
impl AtomicWakerRegistration {
|
||||||
|
Loading…
Reference in New Issue
Block a user