mirror of
https://github.com/rtic-rs/rtic.git
synced 2024-12-01 16:04:33 +01:00
executor update for less unsafe and more clear
This commit is contained in:
parent
d6d58b0eb8
commit
5688a5d332
3 changed files with 34 additions and 30 deletions
|
@ -44,16 +44,15 @@ pub fn codegen(app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
|
|
||||||
for name in channel.tasks.iter() {
|
for name in channel.tasks.iter() {
|
||||||
let exec_name = util::internal_task_ident(name, "EXEC");
|
let exec_name = util::internal_task_ident(name, "EXEC");
|
||||||
|
// TODO: Fix cfg
|
||||||
// let task = &app.software_tasks[name];
|
// let task = &app.software_tasks[name];
|
||||||
// let cfgs = &task.cfgs;
|
// let cfgs = &task.cfgs;
|
||||||
|
|
||||||
stmts.push(quote!(
|
stmts.push(quote!(
|
||||||
if #exec_name.check_and_clear_pending() {
|
|
||||||
#exec_name.poll(|| {
|
#exec_name.poll(|| {
|
||||||
#exec_name.set_pending();
|
#exec_name.set_pending();
|
||||||
#pend_interrupt
|
#pend_interrupt
|
||||||
});
|
});
|
||||||
}
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,11 +156,8 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> {
|
pub fn #internal_spawn_ident(#(#input_args,)*) -> Result<(), #input_ty> {
|
||||||
if #exec_name.try_reserve() {
|
|
||||||
// This unsafe is protected by `try_reserve`, see its documentation for details
|
if #exec_name.spawn(|| #name(unsafe { #name::Context::new() } #(,#input_untupled)*) ) {
|
||||||
unsafe {
|
|
||||||
#exec_name.spawn_unchecked(#name(#name::Context::new() #(,#input_untupled)*));
|
|
||||||
}
|
|
||||||
|
|
||||||
#pend_interrupt
|
#pend_interrupt
|
||||||
|
|
||||||
|
@ -168,6 +165,7 @@ pub fn codegen(ctxt: Context, app: &App, analysis: &Analysis) -> TokenStream2 {
|
||||||
} else {
|
} else {
|
||||||
Err(#input_tupled)
|
Err(#input_tupled)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ unsafe fn waker_drop(_: *const ()) {
|
||||||
|
|
||||||
/// Executor for an async task.
|
/// Executor for an async task.
|
||||||
pub struct AsyncTaskExecutor<F: Future> {
|
pub struct AsyncTaskExecutor<F: Future> {
|
||||||
// `task` is proteced by the `running` flag.
|
// `task` is protected by the `running` flag.
|
||||||
task: UnsafeCell<MaybeUninit<F>>,
|
task: UnsafeCell<MaybeUninit<F>>,
|
||||||
running: AtomicBool,
|
running: AtomicBool,
|
||||||
pending: AtomicBool,
|
pending: AtomicBool,
|
||||||
|
@ -40,6 +40,7 @@ unsafe impl<F: Future> Sync for AsyncTaskExecutor<F> {}
|
||||||
|
|
||||||
impl<F: Future> AsyncTaskExecutor<F> {
|
impl<F: Future> AsyncTaskExecutor<F> {
|
||||||
/// Create a new executor.
|
/// Create a new executor.
|
||||||
|
#[inline(always)]
|
||||||
pub const fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
task: UnsafeCell::new(MaybeUninit::uninit()),
|
task: UnsafeCell::new(MaybeUninit::uninit()),
|
||||||
|
@ -49,45 +50,51 @@ impl<F: Future> AsyncTaskExecutor<F> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if there is an active task in the executor.
|
/// Check if there is an active task in the executor.
|
||||||
|
#[inline(always)]
|
||||||
pub fn is_running(&self) -> bool {
|
pub fn is_running(&self) -> bool {
|
||||||
self.running.load(Ordering::Relaxed)
|
self.running.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if a waker has pended the executor and simultaneously clears the flag.
|
/// Checks if a waker has pended the executor and simultaneously clears the flag.
|
||||||
pub fn check_and_clear_pending(&self) -> bool {
|
#[inline(always)]
|
||||||
|
fn check_and_clear_pending(&self) -> bool {
|
||||||
|
// Ordering::Acquire to enforce that update of task is visible to poll
|
||||||
self.pending
|
self.pending
|
||||||
.compare_exchange(true, false, Ordering::Relaxed, Ordering::Relaxed)
|
.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used by wakers to indicate that the executor needs to run.
|
// Used by wakers to indicate that the executor needs to run.
|
||||||
|
#[inline(always)]
|
||||||
pub fn set_pending(&self) {
|
pub fn set_pending(&self) {
|
||||||
self.pending.store(true, Ordering::Release);
|
self.pending.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to reserve the executor for a future.
|
/// Spawn a future
|
||||||
/// Used in conjunction with `spawn_unchecked` to reserve the executor before spawning.
|
#[inline(always)]
|
||||||
///
|
pub fn spawn(&self, future: impl Fn() -> F) -> bool {
|
||||||
/// This could have been joined with `spawn_unchecked` for a complete safe API, however the
|
// Try to reserve the executor for a future.
|
||||||
/// codegen needs to see if the reserve fails so it can give back input parameters. If spawning
|
if self
|
||||||
/// was done within the same call the input parameters would be lost and could not be returned.
|
.running
|
||||||
pub fn try_reserve(&self) -> bool {
|
|
||||||
self.running
|
|
||||||
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
|
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
|
{
|
||||||
|
// This unsafe is protected by `running` being false and the atomic setting it to true.
|
||||||
|
unsafe {
|
||||||
|
self.task.get().write(MaybeUninit::new(future()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawn a future, only valid to do after `try_reserve` succeeds.
|
|
||||||
pub unsafe fn spawn_unchecked(&self, future: F) {
|
|
||||||
debug_assert!(self.running.load(Ordering::Relaxed));
|
|
||||||
|
|
||||||
self.task.get().write(MaybeUninit::new(future));
|
|
||||||
self.set_pending();
|
self.set_pending();
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Poll the future in the executor.
|
/// Poll the future in the executor.
|
||||||
|
#[inline(always)]
|
||||||
pub fn poll(&self, wake: fn()) {
|
pub fn poll(&self, wake: fn()) {
|
||||||
if self.is_running() {
|
if self.is_running() && self.check_and_clear_pending() {
|
||||||
let waker = unsafe { Waker::from_raw(RawWaker::new(wake as *const (), &WAKER_VTABLE)) };
|
let waker = unsafe { Waker::from_raw(RawWaker::new(wake as *const (), &WAKER_VTABLE)) };
|
||||||
let mut cx = Context::from_waker(&waker);
|
let mut cx = Context::from_waker(&waker);
|
||||||
let future = unsafe { Pin::new_unchecked(&mut *(self.task.get() as *mut F)) };
|
let future = unsafe { Pin::new_unchecked(&mut *(self.task.get() as *mut F)) };
|
||||||
|
|
Loading…
Reference in a new issue