Notion of MutexFamily (#63)

* Signal generalized; efficient notifiable-from-ISR single-threaded executor

* Notion of MutexFamily

* New release

* Fix compilation error on riscv

* Fix fmt

* Address clippy warnings

* Address clippy warnings

* Address clippy warnings
This commit is contained in:
Ivan Markov 2022-04-16 19:04:09 +03:00 committed by GitHub
parent d446ec9a39
commit 0c55a52692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 203 additions and 39 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "esp-idf-hal"
version = "0.35.2"
version = "0.36.0"
authors = ["sapir <yasapir@gmail.com>", "Ivan Markov <ivan.markov@gmail.com>"]
edition = "2018"
resolver = "2"
@ -28,7 +28,7 @@ nb = "0.1.2"
mutex-trait = { version = "0.2", optional = true, default-features = false }
embedded-hal = "=1.0.0-alpha.7"
embedded-hal-0-2 = { package = "embedded-hal", version = "0.2.7", features = ["unproven"] }
embedded-svc = { version = "0.19", optional = true, default-features = false }
embedded-svc = { version = "0.20", optional = true, default-features = false }
esp-idf-sys = { version = "0.31", optional = true, default-features = false, features = ["native"] }
critical-section = { version = "0.2.5", optional = true, features = ["custom-impl"] }
embassy = { version = "0", optional = true }

View File

@ -1,5 +1,7 @@
use core::cell::{RefCell, RefMut};
use core::ops::{Deref, DerefMut};
use core::ptr;
use core::sync::atomic::{AtomicPtr, Ordering};
use esp_idf_sys::*;
@ -10,24 +12,187 @@ pub fn active() -> bool {
unsafe { xPortInIsrContext() != 0 }
}
#[inline(always)]
#[link_section = ".iram1.interrupt_do_yield"]
pub fn do_yield() {
if active() {
#[cfg(esp32c3)]
unsafe {
vPortYieldFromISR();
}
static ISR_YIELDER: AtomicPtr<unsafe fn()> = AtomicPtr::new(ptr::null_mut());
#[cfg(not(esp32c3))]
unsafe {
vPortEvaluateYieldFromISR(0);
}
#[inline(always)]
#[link_section = ".iram1.interrupt_get_isr_yielder"]
unsafe fn get_isr_yielder() -> Option<unsafe fn()> {
if active() {
ISR_YIELDER.load(Ordering::SeqCst).as_ref().copied()
} else {
unsafe {
vPortYield();
None
}
}
/// # Safety
///
/// This function should only be called from within an ISR handler, so as to set
/// a custom ISR yield function (e.g. when using the ESP-IDF timer service).
///
/// Thus, if some function further down the ISR call chain invokes `do_yield`,
/// the custom yield function set here will be called.
///
/// Users should not forget to call again `set_isr_yielder` at the end of the
/// ISR handler so as to reastore the yield function which was valid before the
/// ISR handler was invoked.
#[inline(always)]
#[link_section = ".iram1.interrupt_set_isr_yielder"]
pub unsafe fn set_isr_yielder(yielder: Option<unsafe fn()>) -> Option<unsafe fn()> {
if active() {
let yielder = if let Some(yielder) = yielder {
&yielder as *const _ as *mut _
} else {
ptr::null_mut()
};
ISR_YIELDER
.swap(yielder, Ordering::SeqCst)
.as_ref()
.copied()
} else {
None
}
}
pub mod task {
use core::ptr;
use core::time::Duration;
use esp_idf_sys::*;
use crate::delay::TickType;
#[inline(always)]
#[link_section = ".iram1.interrupt_task_do_yield"]
pub fn do_yield() {
if super::active() {
#[cfg(esp32c3)]
unsafe {
if let Some(yielder) = super::get_isr_yielder() {
yielder();
} else {
vPortYieldFromISR();
}
}
#[cfg(not(esp32c3))]
unsafe {
if let Some(yielder) = super::get_isr_yielder() {
yielder();
} else {
vPortEvaluateYieldFromISR(0);
}
}
} else {
unsafe {
vPortYield();
}
}
}
#[inline(always)]
#[link_section = ".iram1.interrupt_task_current"]
pub fn current() -> Option<TaskHandle_t> {
if super::active() {
None
} else {
Some(unsafe { xTaskGetCurrentTaskHandle() })
}
}
pub fn wait_any_notification() {
loop {
if let Some(notification) = wait_notification(None) {
if notification != 0 {
break;
}
}
}
}
pub fn wait_notification(duration: Option<Duration>) -> Option<u32> {
let mut notification = 0_u32;
#[cfg(esp_idf_version = "4.3")]
let notified = unsafe {
xTaskNotifyWait(
0,
u32::MAX,
&mut notification as *mut _,
TickType::from(duration).0,
)
} != 0;
#[cfg(not(esp_idf_version = "4.3"))]
let notified = unsafe {
xTaskGenericNotifyWait(
0,
0,
u32::MAX,
&mut notification as *mut _,
TickType::from(duration).0,
)
} != 0;
if notified {
Some(notification)
} else {
None
}
}
/// # Safety
///
/// When calling this function care should be taken to pass a valid
/// FreeRTOS task handle. Moreover, the FreeRTOS task should be valid
/// when this function is being called.
pub unsafe fn notify(task: TaskHandle_t, notification: u32) -> bool {
let notified = if super::active() {
let mut higher_prio_task_woken: BaseType_t = Default::default();
#[cfg(esp_idf_version = "4.3")]
let notified = xTaskGenericNotifyFromISR(
task,
notification,
eNotifyAction_eSetBits,
ptr::null_mut(),
&mut higher_prio_task_woken as *mut _,
);
#[cfg(not(esp_idf_version = "4.3"))]
let notified = xTaskGenericNotifyFromISR(
task,
0,
notification,
eNotifyAction_eSetBits,
ptr::null_mut(),
&mut higher_prio_task_woken as *mut _,
);
if higher_prio_task_woken != 0 {
do_yield();
}
notified
} else {
#[cfg(esp_idf_version = "4.3")]
let notified =
xTaskGenericNotify(task, notification, eNotifyAction_eSetBits, ptr::null_mut());
#[cfg(not(esp_idf_version = "4.3"))]
let notified = xTaskGenericNotify(
task,
0,
notification,
eNotifyAction_eSetBits,
ptr::null_mut(),
);
notified
};
notified != 0
}
}
/// A critical section allows the user to disable interrupts
@ -275,6 +440,14 @@ impl<'a, T> mutex_trait::Mutex for &'a Mutex<T> {
}
}
#[cfg(feature = "embedded-svc")]
pub struct MutexFamily;
#[cfg(feature = "embedded-svc")]
impl embedded_svc::mutex::MutexFamily for MutexFamily {
type Mutex<T> = Mutex<T>;
}
#[cfg(feature = "embedded-svc")]
impl<T> embedded_svc::mutex::Mutex for Mutex<T> {
type Data = T;

View File

@ -117,10 +117,7 @@ impl Condvar {
Self(UnsafeCell::new(cond))
}
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T>
where
T: Send,
{
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
let r = unsafe { pthread_cond_wait(self.0.get(), guard.0 .0.get()) };
debug_assert_eq!(r, 0);
@ -131,10 +128,7 @@ impl Condvar {
&self,
guard: MutexGuard<'a, T>,
duration: Duration,
) -> (MutexGuard<'a, T>, bool)
where
T: Send,
{
) -> (MutexGuard<'a, T>, bool) {
let abstime = timespec {
tv_sec: duration.as_secs() as _,
tv_nsec: duration.subsec_nanos() as _,
@ -175,12 +169,12 @@ impl Drop for Condvar {
}
#[cfg(feature = "embedded-svc")]
impl embedded_svc::mutex::Condvar for Condvar {
type Mutex<T>
where
T: Send,
= Mutex<T>;
impl embedded_svc::mutex::MutexFamily for Condvar {
type Mutex<T> = Mutex<T>;
}
#[cfg(feature = "embedded-svc")]
impl embedded_svc::mutex::Condvar for Condvar {
#[inline(always)]
fn new() -> Self {
Condvar::new()
@ -188,25 +182,22 @@ impl embedded_svc::mutex::Condvar for Condvar {
fn wait<'a, T>(
&self,
guard: <<Self as embedded_svc::mutex::Condvar>::Mutex<T> as embedded_svc::mutex::Mutex>::Guard<'a>,
) -> <<Self as embedded_svc::mutex::Condvar>::Mutex<T> as embedded_svc::mutex::Mutex>::Guard<'a>
where
T: Send,
guard: <<Self as embedded_svc::mutex::MutexFamily>::Mutex<T> as embedded_svc::mutex::Mutex>::Guard<'a>,
) -> <<Self as embedded_svc::mutex::MutexFamily>::Mutex<T> as embedded_svc::mutex::Mutex>::Guard<'a>
{
Condvar::wait(self, guard)
}
fn wait_timeout<'a, T>(
&self,
guard: <<Self as embedded_svc::mutex::Condvar>::Mutex<T> as embedded_svc::mutex::Mutex>::Guard<'a>,
guard: <<Self as embedded_svc::mutex::MutexFamily>::Mutex<T> as embedded_svc::mutex::Mutex>::Guard<'a>,
duration: Duration,
) -> (
<<Self as embedded_svc::mutex::Condvar>::Mutex<T> as embedded_svc::mutex::Mutex>::Guard<'a>,
<<Self as embedded_svc::mutex::MutexFamily>::Mutex<T> as embedded_svc::mutex::Mutex>::Guard<
'a,
>,
bool,
)
where
T: Send,
{
) {
Condvar::wait_timeout(self, guard, duration)
}