diff options
Diffstat (limited to 'embassy-executor/src/raw')
| -rw-r--r-- | embassy-executor/src/raw/mod.rs | 97 |
1 files changed, 76 insertions, 21 deletions
diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 72c367c33..f6c66da5a 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs | |||
| @@ -19,7 +19,6 @@ use core::marker::PhantomData; | |||
| 19 | use core::mem; | 19 | use core::mem; |
| 20 | use core::pin::Pin; | 20 | use core::pin::Pin; |
| 21 | use core::ptr::NonNull; | 21 | use core::ptr::NonNull; |
| 22 | use core::sync::atomic::AtomicPtr; | ||
| 23 | use core::task::{Context, Poll}; | 22 | use core::task::{Context, Poll}; |
| 24 | 23 | ||
| 25 | use atomic_polyfill::{AtomicU32, Ordering}; | 24 | use atomic_polyfill::{AtomicU32, Ordering}; |
| @@ -290,10 +289,60 @@ impl<F: Future + 'static, const N: usize> TaskPool<F, N> { | |||
| 290 | } | 289 | } |
| 291 | } | 290 | } |
| 292 | 291 | ||
| 292 | #[derive(Clone, Copy)] | ||
| 293 | pub(crate) enum PenderInner { | ||
| 294 | #[cfg(feature = "executor-thread")] | ||
| 295 | Thread(crate::arch::ThreadPender), | ||
| 296 | #[cfg(feature = "executor-interrupt")] | ||
| 297 | Interrupt(crate::arch::InterruptPender), | ||
| 298 | #[cfg(feature = "pender-callback")] | ||
| 299 | Callback { func: fn(*mut ()), context: *mut () }, | ||
| 300 | } | ||
| 301 | |||
| 302 | unsafe impl Send for PenderInner {} | ||
| 303 | unsafe impl Sync for PenderInner {} | ||
| 304 | |||
| 305 | /// Platform/architecture-specific action executed when an executor has pending work. | ||
| 306 | /// | ||
| 307 | /// When a task within an executor is woken, the `Pender` is called. This does a | ||
| 308 | /// platform/architecture-specific action to signal there is pending work in the executor. | ||
| 309 | /// When this happens, you must arrange for [`Executor::poll`] to be called. | ||
| 310 | /// | ||
| 311 | /// You can think of it as a waker, but for the whole executor. | ||
| 312 | pub struct Pender(pub(crate) PenderInner); | ||
| 313 | |||
| 314 | impl Pender { | ||
| 315 | /// Create a `Pender` that will call an arbitrary function pointer. | ||
| 316 | /// | ||
| 317 | /// # Arguments | ||
| 318 | /// | ||
| 319 | /// - `func`: The function pointer to call. | ||
| 320 | /// - `context`: Opaque context pointer, that will be passed to the function pointer. | ||
| 321 | #[cfg(feature = "pender-callback")] | ||
| 322 | pub fn new_from_callback(func: fn(*mut ()), context: *mut ()) -> Self { | ||
| 323 | Self(PenderInner::Callback { | ||
| 324 | func, | ||
| 325 | context: context.into(), | ||
| 326 | }) | ||
| 327 | } | ||
| 328 | } | ||
| 329 | |||
| 330 | impl Pender { | ||
| 331 | pub(crate) fn pend(&self) { | ||
| 332 | match self.0 { | ||
| 333 | #[cfg(feature = "executor-thread")] | ||
| 334 | PenderInner::Thread(x) => x.pend(), | ||
| 335 | #[cfg(feature = "executor-interrupt")] | ||
| 336 | PenderInner::Interrupt(x) => x.pend(), | ||
| 337 | #[cfg(feature = "pender-callback")] | ||
| 338 | PenderInner::Callback { func, context } => func(context), | ||
| 339 | } | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 293 | pub(crate) struct SyncExecutor { | 343 | pub(crate) struct SyncExecutor { |
| 294 | run_queue: RunQueue, | 344 | run_queue: RunQueue, |
| 295 | signal_fn: fn(*mut ()), | 345 | pender: Pender, |
| 296 | signal_ctx: AtomicPtr<()>, | ||
| 297 | 346 | ||
| 298 | #[cfg(feature = "integrated-timers")] | 347 | #[cfg(feature = "integrated-timers")] |
| 299 | pub(crate) timer_queue: timer_queue::TimerQueue, | 348 | pub(crate) timer_queue: timer_queue::TimerQueue, |
| @@ -302,16 +351,13 @@ pub(crate) struct SyncExecutor { | |||
| 302 | } | 351 | } |
| 303 | 352 | ||
| 304 | impl SyncExecutor { | 353 | impl SyncExecutor { |
| 305 | pub(crate) fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { | 354 | pub(crate) fn new(pender: Pender) -> Self { |
| 306 | #[cfg(feature = "integrated-timers")] | 355 | #[cfg(feature = "integrated-timers")] |
| 307 | let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; | 356 | let alarm = unsafe { unwrap!(driver::allocate_alarm()) }; |
| 308 | #[cfg(feature = "integrated-timers")] | ||
| 309 | driver::set_alarm_callback(alarm, signal_fn, signal_ctx); | ||
| 310 | 357 | ||
| 311 | Self { | 358 | Self { |
| 312 | run_queue: RunQueue::new(), | 359 | run_queue: RunQueue::new(), |
| 313 | signal_fn, | 360 | pender, |
| 314 | signal_ctx: AtomicPtr::new(signal_ctx), | ||
| 315 | 361 | ||
| 316 | #[cfg(feature = "integrated-timers")] | 362 | #[cfg(feature = "integrated-timers")] |
| 317 | timer_queue: timer_queue::TimerQueue::new(), | 363 | timer_queue: timer_queue::TimerQueue::new(), |
| @@ -332,10 +378,16 @@ impl SyncExecutor { | |||
| 332 | trace::task_ready_begin(task.as_ptr() as u32); | 378 | trace::task_ready_begin(task.as_ptr() as u32); |
| 333 | 379 | ||
| 334 | if self.run_queue.enqueue(cs, task) { | 380 | if self.run_queue.enqueue(cs, task) { |
| 335 | (self.signal_fn)(self.signal_ctx.load(Ordering::Relaxed)) | 381 | self.pender.pend(); |
| 336 | } | 382 | } |
| 337 | } | 383 | } |
| 338 | 384 | ||
| 385 | #[cfg(feature = "integrated-timers")] | ||
| 386 | fn alarm_callback(ctx: *mut ()) { | ||
| 387 | let this: &Self = unsafe { &*(ctx as *const Self) }; | ||
| 388 | this.pender.pend(); | ||
| 389 | } | ||
| 390 | |||
| 339 | pub(super) unsafe fn spawn(&'static self, task: TaskRef) { | 391 | pub(super) unsafe fn spawn(&'static self, task: TaskRef) { |
| 340 | task.header().executor.set(Some(self)); | 392 | task.header().executor.set(Some(self)); |
| 341 | 393 | ||
| @@ -351,6 +403,9 @@ impl SyncExecutor { | |||
| 351 | /// | 403 | /// |
| 352 | /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. | 404 | /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. |
| 353 | pub(crate) unsafe fn poll(&'static self) { | 405 | pub(crate) unsafe fn poll(&'static self) { |
| 406 | #[cfg(feature = "integrated-timers")] | ||
| 407 | driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); | ||
| 408 | |||
| 354 | #[allow(clippy::never_loop)] | 409 | #[allow(clippy::never_loop)] |
| 355 | loop { | 410 | loop { |
| 356 | #[cfg(feature = "integrated-timers")] | 411 | #[cfg(feature = "integrated-timers")] |
| @@ -417,14 +472,14 @@ impl SyncExecutor { | |||
| 417 | /// | 472 | /// |
| 418 | /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks | 473 | /// - To get the executor to do work, call `poll()`. This will poll all queued tasks (all tasks |
| 419 | /// that "want to run"). | 474 | /// that "want to run"). |
| 420 | /// - You must supply a `signal_fn`. The executor will call it to notify you it has work | 475 | /// - You must supply a [`Pender`]. The executor will call it to notify you it has work |
| 421 | /// to do. You must arrange for `poll()` to be called as soon as possible. | 476 | /// to do. You must arrange for `poll()` to be called as soon as possible. |
| 422 | /// | 477 | /// |
| 423 | /// `signal_fn` can be called from *any* context: any thread, any interrupt priority | 478 | /// The [`Pender`] can be called from *any* context: any thread, any interrupt priority |
| 424 | /// level, etc. It may be called synchronously from any `Executor` method call as well. | 479 | /// level, etc. It may be called synchronously from any `Executor` method call as well. |
| 425 | /// You must deal with this correctly. | 480 | /// You must deal with this correctly. |
| 426 | /// | 481 | /// |
| 427 | /// In particular, you must NOT call `poll` directly from `signal_fn`, as this violates | 482 | /// In particular, you must NOT call `poll` directly from the pender callback, as this violates |
| 428 | /// the requirement for `poll` to not be called reentrantly. | 483 | /// the requirement for `poll` to not be called reentrantly. |
| 429 | #[repr(transparent)] | 484 | #[repr(transparent)] |
| 430 | pub struct Executor { | 485 | pub struct Executor { |
| @@ -437,15 +492,15 @@ impl Executor { | |||
| 437 | pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self { | 492 | pub(crate) unsafe fn wrap(inner: &SyncExecutor) -> &Self { |
| 438 | mem::transmute(inner) | 493 | mem::transmute(inner) |
| 439 | } | 494 | } |
| 495 | |||
| 440 | /// Create a new executor. | 496 | /// Create a new executor. |
| 441 | /// | 497 | /// |
| 442 | /// When the executor has work to do, it will call `signal_fn` with | 498 | /// When the executor has work to do, it will call the [`Pender`]. |
| 443 | /// `signal_ctx` as argument. | ||
| 444 | /// | 499 | /// |
| 445 | /// See [`Executor`] docs for details on `signal_fn`. | 500 | /// See [`Executor`] docs for details on `Pender`. |
| 446 | pub fn new(signal_fn: fn(*mut ()), signal_ctx: *mut ()) -> Self { | 501 | pub fn new(pender: Pender) -> Self { |
| 447 | Self { | 502 | Self { |
| 448 | inner: SyncExecutor::new(signal_fn, signal_ctx), | 503 | inner: SyncExecutor::new(pender), |
| 449 | _not_sync: PhantomData, | 504 | _not_sync: PhantomData, |
| 450 | } | 505 | } |
| 451 | } | 506 | } |
| @@ -468,16 +523,16 @@ impl Executor { | |||
| 468 | /// This loops over all tasks that are queued to be polled (i.e. they're | 523 | /// This loops over all tasks that are queued to be polled (i.e. they're |
| 469 | /// freshly spawned or they've been woken). Other tasks are not polled. | 524 | /// freshly spawned or they've been woken). Other tasks are not polled. |
| 470 | /// | 525 | /// |
| 471 | /// You must call `poll` after receiving a call to `signal_fn`. It is OK | 526 | /// You must call `poll` after receiving a call to the [`Pender`]. It is OK |
| 472 | /// to call `poll` even when not requested by `signal_fn`, but it wastes | 527 | /// to call `poll` even when not requested by the `Pender`, but it wastes |
| 473 | /// energy. | 528 | /// energy. |
| 474 | /// | 529 | /// |
| 475 | /// # Safety | 530 | /// # Safety |
| 476 | /// | 531 | /// |
| 477 | /// You must NOT call `poll` reentrantly on the same executor. | 532 | /// You must NOT call `poll` reentrantly on the same executor. |
| 478 | /// | 533 | /// |
| 479 | /// In particular, note that `poll` may call `signal_fn` synchronously. Therefore, you | 534 | /// In particular, note that `poll` may call the `Pender` synchronously. Therefore, you |
| 480 | /// must NOT directly call `poll()` from your `signal_fn`. Instead, `signal_fn` has to | 535 | /// must NOT directly call `poll()` from the `Pender` callback. Instead, the callback has to |
| 481 | /// somehow schedule for `poll()` to be called later, at a time you know for sure there's | 536 | /// somehow schedule for `poll()` to be called later, at a time you know for sure there's |
| 482 | /// no `poll()` already running. | 537 | /// no `poll()` already running. |
| 483 | pub unsafe fn poll(&'static self) { | 538 | pub unsafe fn poll(&'static self) { |
