Skip to main content

esp_radio_rtos_driver/
semaphore.rs

1//! Semaphores
2//!
3//! Semaphores are synchronization primitives that allow threads to coordinate their execution.
4//! They are used to control access to a shared resource by limiting the number of threads that can
5//! access it simultaneously.
6//!
7//! esp-radio sometimes mixes up semaphores and mutexes (FreeRTOS allows this), so this crate
8//! exposes a single interface to work with both.
9//!
10//! ## Implementation
11//!
12//! Implement the `SemaphoreImplementation` trait for an object, and use the
13//! `register_semaphore_implementation` to register that implementation for esp-radio.
14//!
15//! See the [`SemaphoreImplementation`] documentation for more information.
16//!
17//! ## Usage
18//!
19//! Users should use [`SemaphoreHandle`] to interact with semaphores created by the driver
20//! implementation. Use [`SemaphoreKind`] to specify the type of semaphore or mutex to create.
21//!
22//! > Note that the only expected user of this crate is esp-radio. Application code should rely on
23//! > the platform's implementation of semaphores and mutexes.
24
25use core::ptr::NonNull;
26
27/// Pointer to an opaque semaphore created by the driver implementation.
28pub type SemaphorePtr = NonNull<()>;
29
30/// The type of semaphore or mutex to create.
31pub enum SemaphoreKind {
32    /// Counting semaphore.
33    Counting { max: u32, initial: u32 },
34
35    /// Non-recursive mutex.
36    Mutex,
37
38    /// Recursive mutex.
39    RecursiveMutex,
40}
41
42unsafe extern "Rust" {
43    fn esp_rtos_semaphore_create(kind: SemaphoreKind) -> SemaphorePtr;
44    fn esp_rtos_semaphore_delete(semaphore: SemaphorePtr);
45
46    fn esp_rtos_semaphore_take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool;
47    fn esp_rtos_semaphore_take_with_deadline(
48        semaphore: SemaphorePtr,
49        deadline_instant: Option<u64>,
50    ) -> bool;
51    fn esp_rtos_semaphore_give(semaphore: SemaphorePtr) -> bool;
52    fn esp_rtos_semaphore_try_give_from_isr(
53        semaphore: SemaphorePtr,
54        higher_prio_task_waken: Option<&mut bool>,
55    ) -> bool;
56    fn esp_rtos_semaphore_current_count(semaphore: SemaphorePtr) -> u32;
57
58    fn esp_rtos_semaphore_try_take(semaphore: SemaphorePtr) -> bool;
59    fn esp_rtos_semaphore_try_take_from_isr(
60        semaphore: SemaphorePtr,
61        higher_prio_task_waken: Option<&mut bool>,
62    ) -> bool;
63}
64
65/// A semaphore primitive.
66///
67/// The following snippet demonstrates the boilerplate necessary to implement a semaphore using the
68/// `SemaphoreImplementation` trait:
69///
70/// ```rust,no_run
71/// use esp_radio_rtos_driver::{
72///     register_semaphore_implementation,
73///     semaphore::{SemaphoreImplementation, SemaphoreKind, SemaphorePtr},
74/// };
75///
76/// struct MySemaphore {
77///     // Semaphore implementation details
78/// }
79///
80/// impl SemaphoreImplementation for MySemaphore {
81///     fn create(kind: SemaphoreKind) -> SemaphorePtr {
82///         unimplemented!()
83///     }
84///
85///     unsafe fn delete(semaphore: SemaphorePtr) {
86///         unimplemented!()
87///     }
88///
89///     unsafe fn take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool {
90///         unimplemented!()
91///     }
92///
93///     unsafe fn give(semaphore: SemaphorePtr) -> bool {
94///         unimplemented!()
95///     }
96///
97///     unsafe fn try_give_from_isr(
98///         semaphore: SemaphorePtr,
99///         higher_prio_task_waken: Option<&mut bool>,
100///     ) -> bool {
101///         unimplemented!()
102///     }
103///
104///     unsafe fn current_count(semaphore: SemaphorePtr) -> u32 {
105///         unimplemented!()
106///     }
107///
108///     unsafe fn try_take(semaphore: SemaphorePtr) -> bool {
109///         unimplemented!()
110///     }
111///
112///     unsafe fn try_take_from_isr(
113///         semaphore: SemaphorePtr,
114///         higher_prio_task_waken: Option<&mut bool>,
115///     ) -> bool {
116///         unimplemented!()
117///     }
118/// }
119///
120/// register_semaphore_implementation!(MySemaphore);
121/// ```
122pub trait SemaphoreImplementation {
123    /// Creates a new semaphore instance.
124    ///
125    /// `kind` specifies the type of semaphore to create.
126    ///
127    /// - `SemaphoreKind::Counting` should create counting, non-recursive semaphores/mutexes.
128    /// - `SemaphoreKind::RecursiveMutex` should create recursive mutexes.
129    fn create(kind: SemaphoreKind) -> SemaphorePtr;
130
131    /// Deletes a semaphore instance.
132    ///
133    /// # Safety
134    ///
135    /// `semaphore` must be a pointer returned from [`Self::create`].
136    unsafe fn delete(semaphore: SemaphorePtr);
137
138    /// Decrements the semaphore's counter.
139    ///
140    /// If a timeout is given, this function should block until either a semaphore could be taken,
141    /// or the timeout has been reached. If no timeout is specified, the function should block
142    /// indefinitely.
143    ///
144    /// Recursive mutexes can be repeatedly taken by the same task.
145    ///
146    /// The timeout is specified in microseconds.
147    ///
148    /// This function returns `true` if the semaphore was taken, `false` if the timeout was reached.
149    ///
150    /// # Safety
151    ///
152    /// `semaphore` must be a pointer returned from [`Self::create`].
153    unsafe fn take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool;
154
155    /// Decrements the semaphore's counter.
156    ///
157    /// If a deadline is given, this function should block until either a semaphore could be taken,
158    /// or the deadline has been reached. If no deadline is specified, the function should block
159    /// indefinitely.
160    ///
161    /// Recursive mutexes can be repeatedly taken by the same task.
162    ///
163    /// The deadline is specified in microseconds since epoch.
164    ///
165    /// This function returns `true` if the semaphore was taken, `false` if the deadline was
166    /// reached.
167    ///
168    /// # Safety
169    ///
170    /// `semaphore` must be a pointer returned from [`Self::create`].
171    unsafe fn take_with_deadline(semaphore: SemaphorePtr, deadline_instant: Option<u64>) -> bool;
172
173    /// Increments the semaphore's counter.
174    ///
175    /// This function returns `true` if the semaphore was given, `false` if the counter is at
176    /// its maximum.
177    ///
178    /// Recursive mutexes can not be given by a task other than the one that first locked it.
179    ///
180    /// # Safety
181    ///
182    /// `semaphore` must be a pointer returned from [`Self::create`].
183    unsafe fn give(semaphore: SemaphorePtr) -> bool;
184
185    /// Attempts to increment the semaphore's counter from an ISR.
186    ///
187    /// This function returns `true` if the semaphore was given, `false` if the counter is at
188    /// its maximum.
189    ///
190    /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
191    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
192    ///
193    /// # Safety
194    ///
195    /// `semaphore` must be a pointer returned from [`Self::create`].
196    unsafe fn try_give_from_isr(
197        semaphore: SemaphorePtr,
198        higher_prio_task_waken: Option<&mut bool>,
199    ) -> bool;
200
201    /// Returns the semaphore's current counter value.
202    ///
203    /// # Safety
204    ///
205    /// `semaphore` must be a pointer returned from [`Self::create`].
206    unsafe fn current_count(semaphore: SemaphorePtr) -> u32;
207
208    /// Attempts to decrement the semaphore's counter.
209    ///
210    /// If the counter is zero, this function must immediately return `false`.
211    ///
212    /// # Safety
213    ///
214    /// `semaphore` must be a pointer returned from [`Self::create`].
215    unsafe fn try_take(semaphore: SemaphorePtr) -> bool;
216
217    /// Attempts to decrement the semaphore's counter from an ISR.
218    ///
219    /// If the counter is zero, this function must immediately return `false`.
220    ///
221    /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
222    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
223    ///
224    /// # Safety
225    ///
226    /// `semaphore` must be a pointer returned from [`Self::create`].
227    unsafe fn try_take_from_isr(
228        semaphore: SemaphorePtr,
229        higher_prio_task_waken: Option<&mut bool>,
230    ) -> bool;
231}
232
233#[macro_export]
234macro_rules! register_semaphore_implementation {
235    ($t: ty) => {
236        #[unsafe(no_mangle)]
237        #[inline]
238        fn esp_rtos_semaphore_create(
239            kind: $crate::semaphore::SemaphoreKind,
240        ) -> $crate::semaphore::SemaphorePtr {
241            <$t as $crate::semaphore::SemaphoreImplementation>::create(kind)
242        }
243
244        #[unsafe(no_mangle)]
245        #[inline]
246        fn esp_rtos_semaphore_delete(semaphore: $crate::semaphore::SemaphorePtr) {
247            unsafe { <$t as $crate::semaphore::SemaphoreImplementation>::delete(semaphore) }
248        }
249
250        #[unsafe(no_mangle)]
251        #[inline]
252        fn esp_rtos_semaphore_take(
253            semaphore: $crate::semaphore::SemaphorePtr,
254            timeout_us: Option<u32>,
255        ) -> bool {
256            unsafe {
257                <$t as $crate::semaphore::SemaphoreImplementation>::take(semaphore, timeout_us)
258            }
259        }
260
261        #[unsafe(no_mangle)]
262        #[inline]
263        fn esp_rtos_semaphore_take_with_deadline(
264            semaphore: $crate::semaphore::SemaphorePtr,
265            deadline_instant: Option<u64>,
266        ) -> bool {
267            unsafe {
268                <$t as $crate::semaphore::SemaphoreImplementation>::take_with_deadline(
269                    semaphore,
270                    deadline_instant,
271                )
272            }
273        }
274
275        #[unsafe(no_mangle)]
276        #[inline]
277        fn esp_rtos_semaphore_give(semaphore: $crate::semaphore::SemaphorePtr) -> bool {
278            unsafe { <$t as $crate::semaphore::SemaphoreImplementation>::give(semaphore) }
279        }
280
281        #[unsafe(no_mangle)]
282        #[inline]
283        fn esp_rtos_semaphore_try_give_from_isr(
284            semaphore: $crate::semaphore::SemaphorePtr,
285            higher_prio_task_waken: Option<&mut bool>,
286        ) -> bool {
287            unsafe {
288                <$t as $crate::semaphore::SemaphoreImplementation>::try_give_from_isr(
289                    semaphore,
290                    higher_prio_task_waken,
291                )
292            }
293        }
294
295        #[unsafe(no_mangle)]
296        #[inline]
297        fn esp_rtos_semaphore_current_count(semaphore: $crate::semaphore::SemaphorePtr) -> u32 {
298            unsafe { <$t as $crate::semaphore::SemaphoreImplementation>::current_count(semaphore) }
299        }
300
301        #[unsafe(no_mangle)]
302        #[inline]
303        fn esp_rtos_semaphore_try_take(semaphore: $crate::semaphore::SemaphorePtr) -> bool {
304            unsafe { <$t as $crate::semaphore::SemaphoreImplementation>::try_take(semaphore) }
305        }
306
307        #[unsafe(no_mangle)]
308        #[inline]
309        fn esp_rtos_semaphore_try_take_from_isr(
310            semaphore: $crate::semaphore::SemaphorePtr,
311            higher_prio_task_waken: Option<&mut bool>,
312        ) -> bool {
313            unsafe {
314                <$t as $crate::semaphore::SemaphoreImplementation>::try_take_from_isr(
315                    semaphore,
316                    higher_prio_task_waken,
317                )
318            }
319        }
320    };
321}
322
323/// Semaphore handle.
324///
325/// This handle is used to interact with semaphores created by the driver implementation.
326#[repr(transparent)]
327pub struct SemaphoreHandle(SemaphorePtr);
328
329unsafe impl Send for SemaphoreHandle {}
330unsafe impl Sync for SemaphoreHandle {}
331
332impl SemaphoreHandle {
333    /// Creates a new semaphore instance.
334    ///
335    /// `kind` specifies the type of semaphore to create.
336    ///
337    /// - Use `SemaphoreKind::Counting` to create counting semaphores and non-recursive mutexes.
338    /// - Use `SemaphoreKind::RecursiveMutex` to create recursive mutexes.
339    #[inline]
340    pub fn new(kind: SemaphoreKind) -> Self {
341        let ptr = unsafe { esp_rtos_semaphore_create(kind) };
342        Self(ptr)
343    }
344
345    /// Converts this object into a pointer without dropping it.
346    #[inline]
347    pub fn leak(self) -> SemaphorePtr {
348        let ptr = self.0;
349        core::mem::forget(self);
350        ptr
351    }
352
353    /// Recovers the object from a leaked pointer.
354    ///
355    /// # Safety
356    ///
357    /// - The caller must only use pointers created using [`Self::leak`].
358    /// - The caller must ensure the pointer is not shared.
359    #[inline]
360    pub unsafe fn from_ptr(ptr: SemaphorePtr) -> Self {
361        Self(ptr)
362    }
363
364    /// Creates a reference to this object from a leaked pointer.
365    ///
366    /// This function is used in the esp-radio code to interact with the semaphore.
367    ///
368    /// # Safety
369    ///
370    /// - The caller must only use pointers created using [`Self::leak`].
371    #[inline]
372    pub unsafe fn ref_from_ptr(ptr: &SemaphorePtr) -> &Self {
373        unsafe { core::mem::transmute(ptr) }
374    }
375
376    /// Decrements the semaphore's counter.
377    ///
378    /// If a timeout is given, this function blocks until either a semaphore could be taken, or the
379    /// timeout has been reached. If no timeout is given, this function blocks until the operation
380    /// succeeds.
381    ///
382    /// This function returns `true` if the semaphore was taken, `false` if the timeout was reached.
383    #[inline]
384    pub fn take(&self, timeout_us: Option<u32>) -> bool {
385        unsafe { esp_rtos_semaphore_take(self.0, timeout_us) }
386    }
387
388    /// Decrements the semaphore's counter.
389    ///
390    /// If a deadline is given, this function blocks until either a semaphore could be taken, or the
391    /// deadline has been reached. If no deadline is given, this function blocks until the operation
392    /// succeeds.
393    ///
394    /// This function returns `true` if the semaphore was taken, `false` if the deadline was
395    /// reached.
396    #[inline]
397    pub fn take_with_deadline(&self, deadline_instant: Option<u64>) -> bool {
398        unsafe { esp_rtos_semaphore_take_with_deadline(self.0, deadline_instant) }
399    }
400
401    /// Increments the semaphore's counter.
402    ///
403    /// This function returns `true` if the semaphore was given, `false` if the counter is at its
404    /// maximum.
405    #[inline]
406    pub fn give(&self) -> bool {
407        unsafe { esp_rtos_semaphore_give(self.0) }
408    }
409
410    /// Attempts to increment the semaphore's counter from an ISR.
411    ///
412    /// If the counter is at its maximum, this function returns `false`.
413    ///
414    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
415    #[inline]
416    pub fn try_give_from_isr(&self, higher_prio_task_waken: Option<&mut bool>) -> bool {
417        unsafe { esp_rtos_semaphore_try_give_from_isr(self.0, higher_prio_task_waken) }
418    }
419
420    /// Returns the current counter value.
421    #[inline]
422    pub fn current_count(&self) -> u32 {
423        unsafe { esp_rtos_semaphore_current_count(self.0) }
424    }
425
426    /// Attempts to decrement the semaphore's counter.
427    ///
428    /// If the counter is zero, this function returns `false`.
429    #[inline]
430    pub fn try_take(&self) -> bool {
431        unsafe { esp_rtos_semaphore_try_take(self.0) }
432    }
433
434    /// Attempts to decrement the semaphore's counter from an ISR.
435    ///
436    /// If the counter is zero, this function returns `false`.
437    ///
438    /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
439    /// is set to `true`.
440    #[inline]
441    pub fn try_take_from_isr(&self, higher_prio_task_waken: Option<&mut bool>) -> bool {
442        unsafe { esp_rtos_semaphore_try_take_from_isr(self.0, higher_prio_task_waken) }
443    }
444}
445
446impl Drop for SemaphoreHandle {
447    #[inline]
448    fn drop(&mut self) {
449        unsafe { esp_rtos_semaphore_delete(self.0) };
450    }
451}
452
453#[cfg(feature = "ipc-implementations")]
454mod implementation {
455    use alloc::boxed::Box;
456    use core::ptr::NonNull;
457
458    use esp_sync::NonReentrantMutex;
459
460    use super::*;
461    use crate::{
462        ThreadPtr,
463        current_task,
464        now,
465        set_task_priority,
466        task_priority,
467        wait_queue::WaitQueueHandle,
468    };
469
470    enum SemaphoreInner {
471        Counting {
472            current: u32,
473            max: u32,
474            waiting: WaitQueueHandle,
475        },
476        Mutex {
477            recursive: bool,
478            owner: Option<ThreadPtr>,
479            original_priority: u32,
480            lock_counter: u32,
481            waiting: WaitQueueHandle,
482        },
483    }
484
485    impl SemaphoreInner {
486        fn try_take(&mut self) -> bool {
487            match self {
488                SemaphoreInner::Counting { current, .. } => {
489                    if *current > 0 {
490                        *current -= 1;
491                        true
492                    } else {
493                        false
494                    }
495                }
496                SemaphoreInner::Mutex {
497                    recursive,
498                    owner,
499                    lock_counter,
500                    original_priority,
501                    ..
502                } => {
503                    let current = current_task();
504                    if let Some(owner) = *owner {
505                        if owner == current && *recursive {
506                            *lock_counter += 1;
507                            true
508                        } else {
509                            // We can't lock the mutex. Make sure the mutex holder has a high enough
510                            // priority to avoid priority inversion.
511                            let current_priority = unsafe { task_priority(current) };
512                            let owner_priority = unsafe { task_priority(owner) };
513                            if owner_priority < current_priority {
514                                unsafe { set_task_priority(owner, current_priority) };
515                            }
516                            false
517                        }
518                    } else {
519                        *owner = Some(current);
520                        *lock_counter += 1;
521                        *original_priority = unsafe { task_priority(current) };
522                        true
523                    }
524                }
525            }
526        }
527
528        fn try_take_from_isr(&mut self) -> bool {
529            match self {
530                SemaphoreInner::Counting { current, .. } => {
531                    if *current > 0 {
532                        *current -= 1;
533                        true
534                    } else {
535                        false
536                    }
537                }
538                SemaphoreInner::Mutex {
539                    recursive,
540                    owner,
541                    lock_counter,
542                    ..
543                } => {
544                    // In an ISR context we don't have a current task, so we can't implement
545                    // priority inheritance an we have to conjure up an owner.
546                    let current = NonNull::dangling();
547                    if let Some(owner) = owner {
548                        if *owner == current && *recursive {
549                            *lock_counter += 1;
550                            true
551                        } else {
552                            false
553                        }
554                    } else {
555                        *owner = Some(current);
556                        *lock_counter += 1;
557                        true
558                    }
559                }
560            }
561        }
562
563        fn try_give(&mut self) -> bool {
564            match self {
565                SemaphoreInner::Counting { current, max, .. } => {
566                    if *current < *max {
567                        *current += 1;
568                        true
569                    } else {
570                        false
571                    }
572                }
573                SemaphoreInner::Mutex {
574                    owner,
575                    lock_counter,
576                    original_priority,
577                    ..
578                } => {
579                    let current = current_task();
580
581                    if *owner == Some(current) && *lock_counter > 0 {
582                        *lock_counter -= 1;
583                        if *lock_counter == 0
584                            && let Some(owner) = owner.take()
585                        {
586                            unsafe { set_task_priority(owner, *original_priority) };
587                        }
588                        true
589                    } else {
590                        false
591                    }
592                }
593            }
594        }
595
596        fn try_give_from_isr(&mut self) -> bool {
597            match self {
598                SemaphoreInner::Counting { current, max, .. } => {
599                    if *current < *max {
600                        *current += 1;
601                        true
602                    } else {
603                        false
604                    }
605                }
606                SemaphoreInner::Mutex {
607                    owner,
608                    lock_counter,
609                    ..
610                } => {
611                    let current = NonNull::dangling();
612                    if *owner == Some(current) && *lock_counter > 0 {
613                        *lock_counter -= 1;
614                        if *lock_counter == 0 {
615                            *owner = None;
616                        }
617                        true
618                    } else {
619                        false
620                    }
621                }
622            }
623        }
624
625        fn current_count(&mut self) -> u32 {
626            match self {
627                SemaphoreInner::Counting { current, .. } => *current,
628                SemaphoreInner::Mutex { .. } => {
629                    panic!("RecursiveMutex does not support current_count")
630                }
631            }
632        }
633
634        fn wait_with_deadline(&mut self, deadline: Option<u64>) {
635            trace!("Semaphore wait_with_deadline - {:?}", deadline);
636            match self {
637                SemaphoreInner::Counting { waiting, .. } => waiting.wait_until(deadline),
638                SemaphoreInner::Mutex { waiting, .. } => waiting.wait_until(deadline),
639            }
640        }
641
642        fn notify(&mut self) {
643            trace!("Semaphore notify");
644            match self {
645                SemaphoreInner::Counting { waiting, .. } => waiting.notify(),
646                SemaphoreInner::Mutex { waiting, .. } => waiting.notify(),
647            }
648        }
649
650        fn notify_from_isr(&mut self, higher_prio_task_waken: Option<&mut bool>) {
651            trace!("Semaphore notify from ISR");
652            match self {
653                SemaphoreInner::Counting { waiting, .. } => {
654                    waiting.notify_from_isr(higher_prio_task_waken)
655                }
656                SemaphoreInner::Mutex { waiting, .. } => {
657                    waiting.notify_from_isr(higher_prio_task_waken)
658                }
659            }
660        }
661    }
662
663    /// Semaphore and mutex primitives.
664    pub struct CompatSemaphore {
665        inner: NonReentrantMutex<SemaphoreInner>,
666    }
667
668    unsafe impl Sync for CompatSemaphore {}
669
670    impl CompatSemaphore {
671        /// Create a new counting semaphore.
672        fn new_counting(initial: u32, max: u32) -> Self {
673            CompatSemaphore {
674                inner: NonReentrantMutex::new(SemaphoreInner::Counting {
675                    current: initial,
676                    max,
677                    waiting: WaitQueueHandle::new(),
678                }),
679            }
680        }
681
682        /// Create a new mutex.
683        ///
684        /// If `recursive` is true, the mutex can be locked multiple times by the same task.
685        fn new_mutex(recursive: bool) -> Self {
686            CompatSemaphore {
687                inner: NonReentrantMutex::new(SemaphoreInner::Mutex {
688                    recursive,
689                    owner: None,
690                    lock_counter: 0,
691                    original_priority: 0,
692                    waiting: WaitQueueHandle::new(),
693                }),
694            }
695        }
696
697        unsafe fn from_ptr<'a>(ptr: SemaphorePtr) -> &'a Self {
698            unsafe { ptr.cast::<Self>().as_ref() }
699        }
700
701        /// Try to take the semaphore.
702        ///
703        /// This is a non-blocking operation. The return value indicates whether the semaphore was
704        /// successfully taken.
705        fn try_take(&self) -> bool {
706            self.inner.with(|sem| sem.try_take())
707        }
708
709        /// Try to take the semaphore from an ISR.
710        ///
711        /// This is a non-blocking operation. The return value indicates whether the semaphore was
712        /// successfully taken.
713        fn try_take_from_isr(&self) -> bool {
714            self.inner.with(|sem| sem.try_take_from_isr())
715        }
716
717        /// Take the semaphore.
718        ///
719        /// This is a blocking operation.
720        ///
721        /// If the semaphore is already taken, the task will be blocked until the semaphore is
722        /// released. Recursive mutexes can be locked multiple times by the mutex owner
723        /// task.
724        fn take_with_deadline(&self, deadline: Option<u64>) -> bool {
725            let deadline_instant = deadline.unwrap_or(u64::MAX);
726            loop {
727                let taken = self.inner.with(|sem| {
728                    if sem.try_take() {
729                        true
730                    } else {
731                        // The task will go to sleep when the above critical section is released.
732                        sem.wait_with_deadline(deadline);
733                        false
734                    }
735                });
736
737                if taken {
738                    debug!("Semaphore - take - success");
739                    return true;
740                }
741
742                if now() > deadline_instant {
743                    debug!("Semaphore - take - timed out");
744                    return false;
745                }
746            }
747        }
748
749        /// Return the current count of the semaphore.
750        fn current_count(&self) -> u32 {
751            self.inner.with(|sem| sem.current_count())
752        }
753
754        /// Unlock the semaphore.
755        fn give(&self) -> bool {
756            self.inner.with(|sem| {
757                if sem.try_give() {
758                    sem.notify();
759                    true
760                } else {
761                    false
762                }
763            })
764        }
765
766        /// Try to unlock the semaphore from an ISR.
767        ///
768        /// The return value indicates whether the semaphore was successfully unlocked.
769        fn try_give_from_isr(&self, higher_priority_task_waken: Option<&mut bool>) -> bool {
770            self.inner.with(|sem| {
771                if sem.try_give_from_isr() {
772                    sem.notify_from_isr(higher_priority_task_waken);
773                    true
774                } else {
775                    false
776                }
777            })
778        }
779    }
780
781    impl SemaphoreImplementation for CompatSemaphore {
782        fn create(kind: SemaphoreKind) -> SemaphorePtr {
783            let sem = Box::new(match kind {
784                SemaphoreKind::Counting { max, initial } => Self::new_counting(initial, max),
785                SemaphoreKind::Mutex => Self::new_mutex(false),
786                SemaphoreKind::RecursiveMutex => Self::new_mutex(true),
787            });
788            NonNull::from(Box::leak(sem)).cast()
789        }
790
791        unsafe fn delete(semaphore: SemaphorePtr) {
792            let sem = unsafe { Box::from_raw(semaphore.cast::<Self>().as_ptr()) };
793            core::mem::drop(sem);
794        }
795
796        unsafe fn take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool {
797            unsafe {
798                <Self as SemaphoreImplementation>::take_with_deadline(
799                    semaphore,
800                    timeout_us.map(|us| now() + us as u64),
801                )
802            }
803        }
804
805        unsafe fn take_with_deadline(
806            semaphore: SemaphorePtr,
807            deadline_instant: Option<u64>,
808        ) -> bool {
809            let semaphore = unsafe { Self::from_ptr(semaphore) };
810
811            semaphore.take_with_deadline(deadline_instant)
812        }
813
814        unsafe fn give(semaphore: SemaphorePtr) -> bool {
815            let semaphore = unsafe { Self::from_ptr(semaphore) };
816
817            semaphore.give()
818        }
819
820        unsafe fn current_count(semaphore: SemaphorePtr) -> u32 {
821            let semaphore = unsafe { Self::from_ptr(semaphore) };
822
823            semaphore.current_count()
824        }
825
826        unsafe fn try_take(semaphore: SemaphorePtr) -> bool {
827            let semaphore = unsafe { Self::from_ptr(semaphore) };
828
829            semaphore.try_take()
830        }
831
832        unsafe fn try_give_from_isr(
833            semaphore: SemaphorePtr,
834            higher_priority_task_waken: Option<&mut bool>,
835        ) -> bool {
836            let semaphore = unsafe { Self::from_ptr(semaphore) };
837
838            semaphore.try_give_from_isr(higher_priority_task_waken)
839        }
840
841        unsafe fn try_take_from_isr(semaphore: SemaphorePtr, _hptw: Option<&mut bool>) -> bool {
842            let semaphore = unsafe { Self::from_ptr(semaphore) };
843
844            semaphore.try_take_from_isr()
845        }
846    }
847}
848
849#[cfg(feature = "ipc-implementations")]
850pub use implementation::CompatSemaphore;