mirror of
https://github.com/esp-rs/esp-idf-hal.git
synced 2025-09-28 21:01:26 +00:00
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:
parent
d446ec9a39
commit
0c55a52692
@ -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 }
|
||||
|
201
src/interrupt.rs
201
src/interrupt.rs
@ -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;
|
||||
|
37
src/mutex.rs
37
src/mutex.rs
@ -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)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user