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);
328impl SemaphoreHandle {
329    /// Creates a new semaphore instance.
330    ///
331    /// `kind` specifies the type of semaphore to create.
332    ///
333    /// - Use `SemaphoreKind::Counting` to create counting semaphores and non-recursive mutexes.
334    /// - Use `SemaphoreKind::RecursiveMutex` to create recursive mutexes.
335    #[inline]
336    pub fn new(kind: SemaphoreKind) -> Self {
337        let ptr = unsafe { esp_rtos_semaphore_create(kind) };
338        Self(ptr)
339    }
340
341    /// Converts this object into a pointer without dropping it.
342    #[inline]
343    pub fn leak(self) -> SemaphorePtr {
344        let ptr = self.0;
345        core::mem::forget(self);
346        ptr
347    }
348
349    /// Recovers the object from a leaked pointer.
350    ///
351    /// # Safety
352    ///
353    /// - The caller must only use pointers created using [`Self::leak`].
354    /// - The caller must ensure the pointer is not shared.
355    #[inline]
356    pub unsafe fn from_ptr(ptr: SemaphorePtr) -> Self {
357        Self(ptr)
358    }
359
360    /// Creates a reference to this object from a leaked pointer.
361    ///
362    /// This function is used in the esp-radio code to interact with the semaphore.
363    ///
364    /// # Safety
365    ///
366    /// - The caller must only use pointers created using [`Self::leak`].
367    #[inline]
368    pub unsafe fn ref_from_ptr(ptr: &SemaphorePtr) -> &Self {
369        unsafe { core::mem::transmute(ptr) }
370    }
371
372    /// Decrements the semaphore's counter.
373    ///
374    /// If a timeout is given, this function blocks until either a semaphore could be taken, or the
375    /// timeout has been reached. If no timeout is given, this function blocks until the operation
376    /// succeeds.
377    ///
378    /// This function returns `true` if the semaphore was taken, `false` if the timeout was reached.
379    #[inline]
380    pub fn take(&self, timeout_us: Option<u32>) -> bool {
381        unsafe { esp_rtos_semaphore_take(self.0, timeout_us) }
382    }
383
384    /// Decrements the semaphore's counter.
385    ///
386    /// If a deadline is given, this function blocks until either a semaphore could be taken, or the
387    /// deadline has been reached. If no deadline is given, this function blocks until the operation
388    /// succeeds.
389    ///
390    /// This function returns `true` if the semaphore was taken, `false` if the deadline was
391    /// reached.
392    #[inline]
393    pub fn take_with_deadline(&self, deadline_instant: Option<u64>) -> bool {
394        unsafe { esp_rtos_semaphore_take_with_deadline(self.0, deadline_instant) }
395    }
396
397    /// Increments the semaphore's counter.
398    ///
399    /// This function returns `true` if the semaphore was given, `false` if the counter is at its
400    /// maximum.
401    #[inline]
402    pub fn give(&self) -> bool {
403        unsafe { esp_rtos_semaphore_give(self.0) }
404    }
405
406    /// Attempts to increment the semaphore's counter from an ISR.
407    ///
408    /// If the counter is at its maximum, this function returns `false`.
409    ///
410    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
411    #[inline]
412    pub fn try_give_from_isr(&self, higher_prio_task_waken: Option<&mut bool>) -> bool {
413        unsafe { esp_rtos_semaphore_try_give_from_isr(self.0, higher_prio_task_waken) }
414    }
415
416    /// Returns the current counter value.
417    #[inline]
418    pub fn current_count(&self) -> u32 {
419        unsafe { esp_rtos_semaphore_current_count(self.0) }
420    }
421
422    /// Attempts to decrement the semaphore's counter.
423    ///
424    /// If the counter is zero, this function returns `false`.
425    #[inline]
426    pub fn try_take(&self) -> bool {
427        unsafe { esp_rtos_semaphore_try_take(self.0) }
428    }
429
430    /// Attempts to decrement the semaphore's counter from an ISR.
431    ///
432    /// If the counter is zero, this function returns `false`.
433    ///
434    /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
435    /// is set to `true`.
436    #[inline]
437    pub fn try_take_from_isr(&self, higher_prio_task_waken: Option<&mut bool>) -> bool {
438        unsafe { esp_rtos_semaphore_try_take_from_isr(self.0, higher_prio_task_waken) }
439    }
440}
441
442impl Drop for SemaphoreHandle {
443    #[inline]
444    fn drop(&mut self) {
445        unsafe { esp_rtos_semaphore_delete(self.0) };
446    }
447}
448
449unsafe impl Send for SemaphoreHandle {}
450
451#[cfg(feature = "ipc-implementations")]
452mod implementation {
453    use alloc::boxed::Box;
454    use core::ptr::NonNull;
455
456    use esp_sync::NonReentrantMutex;
457
458    use super::*;
459    use crate::{
460        ThreadPtr,
461        current_task,
462        now,
463        set_task_priority,
464        task_priority,
465        wait_queue::WaitQueueHandle,
466    };
467
468    enum SemaphoreInner {
469        Counting {
470            current: u32,
471            max: u32,
472            waiting: WaitQueueHandle,
473        },
474        Mutex {
475            recursive: bool,
476            owner: Option<ThreadPtr>,
477            original_priority: u32,
478            lock_counter: u32,
479            waiting: WaitQueueHandle,
480        },
481    }
482
483    impl SemaphoreInner {
484        fn try_take(&mut self) -> bool {
485            match self {
486                SemaphoreInner::Counting { current, .. } => {
487                    if *current > 0 {
488                        *current -= 1;
489                        true
490                    } else {
491                        false
492                    }
493                }
494                SemaphoreInner::Mutex {
495                    recursive,
496                    owner,
497                    lock_counter,
498                    original_priority,
499                    ..
500                } => {
501                    let current = current_task();
502                    if let Some(owner) = *owner {
503                        if owner == current && *recursive {
504                            *lock_counter += 1;
505                            true
506                        } else {
507                            // We can't lock the mutex. Make sure the mutex holder has a high enough
508                            // priority to avoid priority inversion.
509                            let current_priority = unsafe { task_priority(current) };
510                            let owner_priority = unsafe { task_priority(owner) };
511                            if owner_priority < current_priority {
512                                unsafe { set_task_priority(owner, current_priority) };
513                            }
514                            false
515                        }
516                    } else {
517                        *owner = Some(current);
518                        *lock_counter += 1;
519                        *original_priority = unsafe { task_priority(current) };
520                        true
521                    }
522                }
523            }
524        }
525
526        fn try_take_from_isr(&mut self) -> bool {
527            match self {
528                SemaphoreInner::Counting { current, .. } => {
529                    if *current > 0 {
530                        *current -= 1;
531                        true
532                    } else {
533                        false
534                    }
535                }
536                SemaphoreInner::Mutex {
537                    recursive,
538                    owner,
539                    lock_counter,
540                    ..
541                } => {
542                    // In an ISR context we don't have a current task, so we can't implement
543                    // priority inheritance an we have to conjure up an owner.
544                    let current = NonNull::dangling();
545                    if let Some(owner) = owner {
546                        if *owner == current && *recursive {
547                            *lock_counter += 1;
548                            true
549                        } else {
550                            false
551                        }
552                    } else {
553                        *owner = Some(current);
554                        *lock_counter += 1;
555                        true
556                    }
557                }
558            }
559        }
560
561        fn try_give(&mut self) -> bool {
562            match self {
563                SemaphoreInner::Counting { current, max, .. } => {
564                    if *current < *max {
565                        *current += 1;
566                        true
567                    } else {
568                        false
569                    }
570                }
571                SemaphoreInner::Mutex {
572                    owner,
573                    lock_counter,
574                    original_priority,
575                    ..
576                } => {
577                    let current = current_task();
578
579                    if *owner == Some(current) && *lock_counter > 0 {
580                        *lock_counter -= 1;
581                        if *lock_counter == 0
582                            && let Some(owner) = owner.take()
583                        {
584                            unsafe { set_task_priority(owner, *original_priority) };
585                        }
586                        true
587                    } else {
588                        false
589                    }
590                }
591            }
592        }
593
594        fn try_give_from_isr(&mut self) -> bool {
595            match self {
596                SemaphoreInner::Counting { current, max, .. } => {
597                    if *current < *max {
598                        *current += 1;
599                        true
600                    } else {
601                        false
602                    }
603                }
604                SemaphoreInner::Mutex {
605                    owner,
606                    lock_counter,
607                    ..
608                } => {
609                    let current = NonNull::dangling();
610                    if *owner == Some(current) && *lock_counter > 0 {
611                        *lock_counter -= 1;
612                        if *lock_counter == 0 {
613                            *owner = None;
614                        }
615                        true
616                    } else {
617                        false
618                    }
619                }
620            }
621        }
622
623        fn current_count(&mut self) -> u32 {
624            match self {
625                SemaphoreInner::Counting { current, .. } => *current,
626                SemaphoreInner::Mutex { .. } => {
627                    panic!("RecursiveMutex does not support current_count")
628                }
629            }
630        }
631
632        fn wait_with_deadline(&mut self, deadline: Option<u64>) {
633            trace!("Semaphore wait_with_deadline - {:?}", deadline);
634            match self {
635                SemaphoreInner::Counting { waiting, .. } => waiting.wait_until(deadline),
636                SemaphoreInner::Mutex { waiting, .. } => waiting.wait_until(deadline),
637            }
638        }
639
640        fn notify(&mut self) {
641            trace!("Semaphore notify");
642            match self {
643                SemaphoreInner::Counting { waiting, .. } => waiting.notify(),
644                SemaphoreInner::Mutex { waiting, .. } => waiting.notify(),
645            }
646        }
647
648        fn notify_from_isr(&mut self, higher_prio_task_waken: Option<&mut bool>) {
649            trace!("Semaphore notify from ISR");
650            match self {
651                SemaphoreInner::Counting { waiting, .. } => {
652                    waiting.notify_from_isr(higher_prio_task_waken)
653                }
654                SemaphoreInner::Mutex { waiting, .. } => {
655                    waiting.notify_from_isr(higher_prio_task_waken)
656                }
657            }
658        }
659    }
660
661    /// Semaphore and mutex primitives.
662    pub struct CompatSemaphore {
663        inner: NonReentrantMutex<SemaphoreInner>,
664    }
665
666    unsafe impl Sync for CompatSemaphore {}
667
668    impl CompatSemaphore {
669        /// Create a new counting semaphore.
670        fn new_counting(initial: u32, max: u32) -> Self {
671            CompatSemaphore {
672                inner: NonReentrantMutex::new(SemaphoreInner::Counting {
673                    current: initial,
674                    max,
675                    waiting: WaitQueueHandle::new(),
676                }),
677            }
678        }
679
680        /// Create a new mutex.
681        ///
682        /// If `recursive` is true, the mutex can be locked multiple times by the same task.
683        fn new_mutex(recursive: bool) -> Self {
684            CompatSemaphore {
685                inner: NonReentrantMutex::new(SemaphoreInner::Mutex {
686                    recursive,
687                    owner: None,
688                    lock_counter: 0,
689                    original_priority: 0,
690                    waiting: WaitQueueHandle::new(),
691                }),
692            }
693        }
694
695        unsafe fn from_ptr<'a>(ptr: SemaphorePtr) -> &'a Self {
696            unsafe { ptr.cast::<Self>().as_ref() }
697        }
698
699        /// Try to take the semaphore.
700        ///
701        /// This is a non-blocking operation. The return value indicates whether the semaphore was
702        /// successfully taken.
703        fn try_take(&self) -> bool {
704            self.inner.with(|sem| sem.try_take())
705        }
706
707        /// Try to take the semaphore from an ISR.
708        ///
709        /// This is a non-blocking operation. The return value indicates whether the semaphore was
710        /// successfully taken.
711        fn try_take_from_isr(&self) -> bool {
712            self.inner.with(|sem| sem.try_take_from_isr())
713        }
714
715        /// Take the semaphore.
716        ///
717        /// This is a blocking operation.
718        ///
719        /// If the semaphore is already taken, the task will be blocked until the semaphore is
720        /// released. Recursive mutexes can be locked multiple times by the mutex owner
721        /// task.
722        fn take_with_deadline(&self, deadline: Option<u64>) -> bool {
723            let deadline_instant = deadline.unwrap_or(u64::MAX);
724            loop {
725                let taken = self.inner.with(|sem| {
726                    if sem.try_take() {
727                        true
728                    } else {
729                        // The task will go to sleep when the above critical section is released.
730                        sem.wait_with_deadline(deadline);
731                        false
732                    }
733                });
734
735                if taken {
736                    debug!("Semaphore - take - success");
737                    return true;
738                }
739
740                if now() > deadline_instant {
741                    debug!("Semaphore - take - timed out");
742                    return false;
743                }
744            }
745        }
746
747        /// Return the current count of the semaphore.
748        fn current_count(&self) -> u32 {
749            self.inner.with(|sem| sem.current_count())
750        }
751
752        /// Unlock the semaphore.
753        fn give(&self) -> bool {
754            self.inner.with(|sem| {
755                if sem.try_give() {
756                    sem.notify();
757                    true
758                } else {
759                    false
760                }
761            })
762        }
763
764        /// Try to unlock the semaphore from an ISR.
765        ///
766        /// The return value indicates whether the semaphore was successfully unlocked.
767        fn try_give_from_isr(&self, higher_priority_task_waken: Option<&mut bool>) -> bool {
768            self.inner.with(|sem| {
769                if sem.try_give_from_isr() {
770                    sem.notify_from_isr(higher_priority_task_waken);
771                    true
772                } else {
773                    false
774                }
775            })
776        }
777    }
778
779    impl SemaphoreImplementation for CompatSemaphore {
780        fn create(kind: SemaphoreKind) -> SemaphorePtr {
781            let sem = Box::new(match kind {
782                SemaphoreKind::Counting { max, initial } => Self::new_counting(initial, max),
783                SemaphoreKind::Mutex => Self::new_mutex(false),
784                SemaphoreKind::RecursiveMutex => Self::new_mutex(true),
785            });
786            NonNull::from(Box::leak(sem)).cast()
787        }
788
789        unsafe fn delete(semaphore: SemaphorePtr) {
790            let sem = unsafe { Box::from_raw(semaphore.cast::<Self>().as_ptr()) };
791            core::mem::drop(sem);
792        }
793
794        unsafe fn take(semaphore: SemaphorePtr, timeout_us: Option<u32>) -> bool {
795            unsafe {
796                <Self as SemaphoreImplementation>::take_with_deadline(
797                    semaphore,
798                    timeout_us.map(|us| now() + us as u64),
799                )
800            }
801        }
802
803        unsafe fn take_with_deadline(
804            semaphore: SemaphorePtr,
805            deadline_instant: Option<u64>,
806        ) -> bool {
807            let semaphore = unsafe { Self::from_ptr(semaphore) };
808
809            semaphore.take_with_deadline(deadline_instant)
810        }
811
812        unsafe fn give(semaphore: SemaphorePtr) -> bool {
813            let semaphore = unsafe { Self::from_ptr(semaphore) };
814
815            semaphore.give()
816        }
817
818        unsafe fn current_count(semaphore: SemaphorePtr) -> u32 {
819            let semaphore = unsafe { Self::from_ptr(semaphore) };
820
821            semaphore.current_count()
822        }
823
824        unsafe fn try_take(semaphore: SemaphorePtr) -> bool {
825            let semaphore = unsafe { Self::from_ptr(semaphore) };
826
827            semaphore.try_take()
828        }
829
830        unsafe fn try_give_from_isr(
831            semaphore: SemaphorePtr,
832            higher_priority_task_waken: Option<&mut bool>,
833        ) -> bool {
834            let semaphore = unsafe { Self::from_ptr(semaphore) };
835
836            semaphore.try_give_from_isr(higher_priority_task_waken)
837        }
838
839        unsafe fn try_take_from_isr(semaphore: SemaphorePtr, _hptw: Option<&mut bool>) -> bool {
840            let semaphore = unsafe { Self::from_ptr(semaphore) };
841
842            semaphore.try_take_from_isr()
843        }
844    }
845}
846
847#[cfg(feature = "ipc-implementations")]
848pub use implementation::CompatSemaphore;