libasync: replace executor Mutexes with RefCells
this will not run on multi-core.
This commit is contained in:
parent
5b95410244
commit
ea765fc529
|
@ -1,17 +1,16 @@
|
||||||
use core::{
|
use core::{
|
||||||
cell::{Cell, UnsafeCell},
|
cell::{RefCell, UnsafeCell},
|
||||||
future::Future,
|
future::Future,
|
||||||
mem::MaybeUninit,
|
mem::MaybeUninit,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::atomic::{self, AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||||
};
|
};
|
||||||
use alloc::{boxed::Box, collections::VecDeque as Deque};
|
use alloc::{boxed::Box, collections::VecDeque as Deque};
|
||||||
//use futures::future::FutureExt;
|
//use futures::future::FutureExt;
|
||||||
use pin_utils::pin_mut;
|
use pin_utils::pin_mut;
|
||||||
use libcortex_a9::mutex::Mutex;
|
|
||||||
// TODO: delete
|
// TODO: delete
|
||||||
use libboard_zynq::println;
|
//use libboard_zynq::println;
|
||||||
|
|
||||||
// NOTE `*const ()` is &AtomicBool
|
// NOTE `*const ()` is &AtomicBool
|
||||||
static VTABLE: RawWakerVTable = {
|
static VTABLE: RawWakerVTable = {
|
||||||
|
@ -35,16 +34,16 @@ static VTABLE: RawWakerVTable = {
|
||||||
///
|
///
|
||||||
/// This is a singleton
|
/// This is a singleton
|
||||||
pub struct Executor {
|
pub struct Executor {
|
||||||
in_block_on: Mutex<bool>,
|
in_block_on: RefCell<bool>,
|
||||||
tasks: Mutex<Deque<Task>>,
|
tasks: RefCell<Deque<Task>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Executor {
|
impl Executor {
|
||||||
/// Creates a new instance of the executor
|
/// Creates a new instance of the executor
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
in_block_on: Mutex::new(false),
|
in_block_on: RefCell::new(false),
|
||||||
tasks: Mutex::new(Deque::new()),
|
tasks: RefCell::new(Deque::new()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +53,7 @@ impl Executor {
|
||||||
// application will only call `block_on` once on an infinite task
|
// application will only call `block_on` once on an infinite task
|
||||||
// (`Future<Output = !>`)
|
// (`Future<Output = !>`)
|
||||||
{
|
{
|
||||||
let mut in_block_on = self.in_block_on.lock();
|
let mut in_block_on = self.in_block_on.borrow_mut();
|
||||||
if *in_block_on {
|
if *in_block_on {
|
||||||
panic!("nested `block_on`");
|
panic!("nested `block_on`");
|
||||||
}
|
}
|
||||||
|
@ -70,14 +69,17 @@ impl Executor {
|
||||||
if ready.load(Ordering::Relaxed) {
|
if ready.load(Ordering::Relaxed) {
|
||||||
ready.store(false, Ordering::Relaxed);
|
ready.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
|
// println!("run block_on");
|
||||||
let mut cx = Context::from_waker(&waker);
|
let mut cx = Context::from_waker(&waker);
|
||||||
if let Poll::Ready(val) = f.as_mut().poll(&mut cx) {
|
if let Poll::Ready(val) = f.as_mut().poll(&mut cx) {
|
||||||
break val;
|
break val;
|
||||||
}
|
}
|
||||||
|
// println!("ran block_on");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// println!("tasks: {}", self.tasks.borrow().len());
|
||||||
// advance other tasks
|
// advance other tasks
|
||||||
let next_task = self.tasks.lock().pop_front();
|
let next_task = self.tasks.borrow_mut().pop_front();
|
||||||
if let Some(mut task) = next_task {
|
if let Some(mut task) = next_task {
|
||||||
// NOTE we don't need a CAS operation here because `wake` invocations that come from
|
// NOTE we don't need a CAS operation here because `wake` invocations that come from
|
||||||
// interrupt handlers (the only source of 'race conditions' (!= data races)) are
|
// interrupt handlers (the only source of 'race conditions' (!= data races)) are
|
||||||
|
@ -94,14 +96,16 @@ impl Executor {
|
||||||
};
|
};
|
||||||
let mut cx = Context::from_waker(&waker);
|
let mut cx = Context::from_waker(&waker);
|
||||||
// this points into a `static` memory so it's already pinned
|
// this points into a `static` memory so it's already pinned
|
||||||
let r = unsafe {
|
// println!("run task");
|
||||||
|
let ready = unsafe {
|
||||||
Pin::new_unchecked(&mut *task.f)
|
Pin::new_unchecked(&mut *task.f)
|
||||||
.poll(&mut cx)
|
.poll(&mut cx)
|
||||||
.is_ready()
|
.is_ready()
|
||||||
};
|
};
|
||||||
if !r {
|
// println!("ran task, ready={:?}", r);
|
||||||
|
if !ready {
|
||||||
// Task is not finished, requeue
|
// Task is not finished, requeue
|
||||||
self.tasks.lock().push_back(task);
|
self.tasks.borrow_mut().push_back(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +114,7 @@ impl Executor {
|
||||||
// // interrupt ran (regardless of whether it generated a wake-up or not)
|
// // interrupt ran (regardless of whether it generated a wake-up or not)
|
||||||
// asm::wfe();
|
// asm::wfe();
|
||||||
};
|
};
|
||||||
*self.in_block_on.lock() = false;
|
self.in_block_on.replace(false);
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,13 +124,13 @@ impl Executor {
|
||||||
pub fn spawn(&self, f: impl Future + 'static) {
|
pub fn spawn(&self, f: impl Future + 'static) {
|
||||||
// NOTE(unsafe) only safe as long as `spawn` is never re-entered and this does not overlap
|
// NOTE(unsafe) only safe as long as `spawn` is never re-entered and this does not overlap
|
||||||
// with operation `(A)` (see `Task::block_on`)
|
// with operation `(A)` (see `Task::block_on`)
|
||||||
self.tasks.lock().push_back(Task::new(f));
|
self.tasks.borrow_mut().push_back(Task::new(f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Task {
|
pub struct Task {
|
||||||
ready: AtomicBool,
|
ready: AtomicBool,
|
||||||
f: Box<Future<Output = ()>>,
|
f: Box<dyn Future<Output = ()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
|
|
Loading…
Reference in New Issue