Skip to main content

esp_radio_rtos_driver/
queue.rs

1//! # Queues
2//!
3//! Queues are a synchronization primitive used to communicate between tasks.
4//! They allow tasks to send and receive data in a first-in-first-out (FIFO) manner.
5//!
6//! ## Implementation
7//!
8//! Implement the `QueueImplementation` trait for an object, and use the
9//! `register_queue_implementation` to register that implementation for esp-radio.
10//!
11//! See the [`QueueImplementation`] documentation for more information.
12//!
13//! You may also choose to use the [`CompatQueue`] implementation provided by this crate.
14//!
15//! ## Usage
16//!
17//! Users should use [`QueueHandle`] to interact with queues created by the driver implementation.
18//!
19//! > Note that the only expected user of this crate is esp-radio.
20
21use core::ptr::NonNull;
22
23/// Pointer to an opaque queue created by the driver implementation.
24pub type QueuePtr = NonNull<()>;
25
26unsafe extern "Rust" {
27    fn esp_rtos_queue_create(capacity: usize, item_size: usize) -> QueuePtr;
28    fn esp_rtos_queue_delete(queue: QueuePtr);
29
30    fn esp_rtos_queue_send_to_front(
31        queue: QueuePtr,
32        item: *const u8,
33        timeout_us: Option<u32>,
34    ) -> bool;
35    fn esp_rtos_queue_send_to_front_with_deadline(
36        queue: QueuePtr,
37        item: *const u8,
38        deadline_instant: Option<u64>,
39    ) -> bool;
40
41    fn esp_rtos_queue_send_to_back(
42        queue: QueuePtr,
43        item: *const u8,
44        timeout_us: Option<u32>,
45    ) -> bool;
46    fn esp_rtos_queue_send_to_back_with_deadline(
47        queue: QueuePtr,
48        item: *const u8,
49        deadline_instant: Option<u64>,
50    ) -> bool;
51
52    fn esp_rtos_queue_try_send_to_back_from_isr(
53        queue: QueuePtr,
54        item: *const u8,
55        higher_prio_task_waken: Option<&mut bool>,
56    ) -> bool;
57    fn esp_rtos_queue_receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool;
58    fn esp_rtos_queue_receive_with_deadline(
59        queue: QueuePtr,
60        item: *mut u8,
61        deadline_instant: Option<u64>,
62    ) -> bool;
63    fn esp_rtos_queue_try_receive_from_isr(
64        queue: QueuePtr,
65        item: *mut u8,
66        higher_prio_task_waken: Option<&mut bool>,
67    ) -> bool;
68    fn esp_rtos_queue_remove(queue: QueuePtr, item: *const u8);
69    fn esp_rtos_queue_messages_waiting(queue: QueuePtr) -> usize;
70}
71
72/// A queue primitive.
73///
74/// The following snippet demonstrates the boilerplate necessary to implement a queue using the
75/// `QueueImplementation` trait:
76///
77/// ```rust,no_run
78/// use esp_radio_rtos_driver::{
79///     queue::{QueueImplementation, QueuePtr},
80///     register_queue_implementation,
81/// };
82///
83/// struct MyQueue {
84///     // Queue implementation details
85/// }
86///
87/// impl QueueImplementation for MyQueue {
88///     fn create(capacity: usize, item_size: usize) -> QueuePtr {
89///         unimplemented!()
90///     }
91///
92///     unsafe fn delete(queue: QueuePtr) {
93///         unimplemented!()
94///     }
95///
96///     unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
97///         unimplemented!()
98///     }
99///
100///     unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
101///         unimplemented!()
102///     }
103///
104///     unsafe fn try_send_to_back_from_isr(
105///         queue: QueuePtr,
106///         item: *const u8,
107///         higher_prio_task_waken: Option<&mut bool>,
108///     ) -> bool {
109///         unimplemented!()
110///     }
111///
112///     unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool {
113///         unimplemented!()
114///     }
115///
116///     unsafe fn try_receive_from_isr(
117///         queue: QueuePtr,
118///         item: *mut u8,
119///         higher_prio_task_waken: Option<&mut bool>,
120///     ) -> bool {
121///         unimplemented!()
122///     }
123///
124///     unsafe fn remove(queue: QueuePtr, item: *const u8) {
125///         unimplemented!()
126///     }
127///
128///     fn messages_waiting(queue: QueuePtr) -> usize {
129///         unimplemented!()
130///     }
131/// }
132///
133/// register_queue_implementation!(MyQueue);
134/// ```
135pub trait QueueImplementation {
136    /// Creates a new, empty queue instance.
137    ///
138    /// The queue must have a capacity for `capacity` number of `item_size` byte items.
139    fn create(capacity: usize, item_size: usize) -> QueuePtr;
140
141    /// Deletes a queue instance.
142    ///
143    /// # Safety
144    ///
145    /// `queue` must be a pointer returned from [`Self::create`].
146    unsafe fn delete(queue: QueuePtr);
147
148    /// Enqueues a high-priority item.
149    ///
150    /// If the queue is full, this function will block for the given timeout. If timeout is None,
151    /// the function will block indefinitely.
152    ///
153    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
154    ///
155    /// # Safety
156    ///
157    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
158    /// a size equal to the queue's item size.
159    unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool;
160
161    /// Enqueues a high-priority item.
162    ///
163    /// If the queue is full, this function will block until the deadline is reached. If the
164    /// deadline is None, the function will block indefinitely.
165    ///
166    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
167    ///
168    /// # Safety
169    ///
170    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
171    /// a size equal to the queue's item size.
172    unsafe fn send_to_front_with_deadline(
173        queue: QueuePtr,
174        item: *const u8,
175        deadline_instant: Option<u64>,
176    ) -> bool;
177
178    /// Enqueues an item.
179    ///
180    /// If the queue is full, this function will block for the given timeout. If timeout is None,
181    /// the function will block indefinitely.
182    ///
183    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
184    ///
185    /// # Safety
186    ///
187    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
188    /// a size equal to the queue's item size.
189    unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool;
190
191    /// Enqueues an item.
192    ///
193    /// If the queue is full, this function will block until the given deadline. If deadline is
194    /// None, the function will block indefinitely.
195    ///
196    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
197    ///
198    /// # Safety
199    ///
200    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
201    /// a size equal to the queue's item size.
202    unsafe fn send_to_back_with_deadline(
203        queue: QueuePtr,
204        item: *const u8,
205        deadline_instant: Option<u64>,
206    ) -> bool;
207
208    /// Attempts to enqueues an item.
209    ///
210    /// If the queue is full, this function will immediately return `false`.
211    ///
212    /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
213    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
214    ///
215    /// # Safety
216    ///
217    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
218    /// a size equal to the queue's item size.
219    unsafe fn try_send_to_back_from_isr(
220        queue: QueuePtr,
221        item: *const u8,
222        higher_prio_task_waken: Option<&mut bool>,
223    ) -> bool;
224
225    /// Dequeues an item from the queue.
226    ///
227    /// If the queue is empty, this function will block for the given timeout. If timeout is None,
228    /// the function will block indefinitely.
229    ///
230    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
231    ///
232    /// # Safety
233    ///
234    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
235    /// a size equal to the queue's item size.
236    unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool;
237
238    /// Dequeues an item from the queue.
239    ///
240    /// If the queue is empty, this function will block until the given deadline is reached. If the
241    /// deadline is None, the function will block indefinitely.
242    ///
243    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
244    ///
245    /// # Safety
246    ///
247    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
248    /// a size equal to the queue's item size.
249    unsafe fn receive_with_deadline(
250        queue: QueuePtr,
251        item: *mut u8,
252        deadline_instant: Option<u64>,
253    ) -> bool;
254
255    /// Attempts to dequeue an item from the queue.
256    ///
257    /// If the queue is empty, this function will return `false` immediately.
258    ///
259    /// The `higher_prio_task_waken` parameter is an optional mutable reference to a boolean flag.
260    /// If the flag is `Some`, the implementation may set it to `true` to request a context switch.
261    ///
262    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
263    ///
264    /// # Safety
265    ///
266    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
267    /// a size equal to the queue's item size.
268    unsafe fn try_receive_from_isr(
269        queue: QueuePtr,
270        item: *mut u8,
271        higher_prio_task_waken: Option<&mut bool>,
272    ) -> bool;
273
274    /// Removes an item from the queue.
275    ///
276    /// # Safety
277    ///
278    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
279    /// a size equal to the queue's item size.
280    unsafe fn remove(queue: QueuePtr, item: *const u8);
281
282    /// Returns the number of messages in the queue.
283    fn messages_waiting(queue: QueuePtr) -> usize;
284}
285
286#[macro_export]
287macro_rules! register_queue_implementation {
288    ($t: ty) => {
289        #[unsafe(no_mangle)]
290        #[inline]
291        fn esp_rtos_queue_create(capacity: usize, item_size: usize) -> $crate::queue::QueuePtr {
292            <$t as $crate::queue::QueueImplementation>::create(capacity, item_size)
293        }
294
295        #[unsafe(no_mangle)]
296        #[inline]
297        fn esp_rtos_queue_delete(queue: $crate::queue::QueuePtr) {
298            unsafe { <$t as $crate::queue::QueueImplementation>::delete(queue) }
299        }
300
301        #[unsafe(no_mangle)]
302        #[inline]
303        fn esp_rtos_queue_send_to_front(
304            queue: $crate::queue::QueuePtr,
305            item: *const u8,
306            timeout_us: Option<u32>,
307        ) -> bool {
308            unsafe {
309                <$t as $crate::queue::QueueImplementation>::send_to_front(queue, item, timeout_us)
310            }
311        }
312
313        #[unsafe(no_mangle)]
314        #[inline]
315        fn esp_rtos_queue_send_to_front_with_deadline(
316            queue: $crate::queue::QueuePtr,
317            item: *const u8,
318            deadline_instant: Option<u64>,
319        ) -> bool {
320            unsafe {
321                <$t as $crate::queue::QueueImplementation>::send_to_front_with_deadline(
322                    queue,
323                    item,
324                    deadline_instant,
325                )
326            }
327        }
328
329        #[unsafe(no_mangle)]
330        #[inline]
331        fn esp_rtos_queue_send_to_back(
332            queue: $crate::queue::QueuePtr,
333            item: *const u8,
334            timeout_us: Option<u32>,
335        ) -> bool {
336            unsafe {
337                <$t as $crate::queue::QueueImplementation>::send_to_back(queue, item, timeout_us)
338            }
339        }
340
341        #[unsafe(no_mangle)]
342        #[inline]
343        fn esp_rtos_queue_send_to_back_with_deadline(
344            queue: $crate::queue::QueuePtr,
345            item: *const u8,
346            deadline_instant: Option<u64>,
347        ) -> bool {
348            unsafe {
349                <$t as $crate::queue::QueueImplementation>::send_to_back_with_deadline(
350                    queue,
351                    item,
352                    deadline_instant,
353                )
354            }
355        }
356
357        #[unsafe(no_mangle)]
358        #[inline]
359        fn esp_rtos_queue_try_send_to_back_from_isr(
360            queue: $crate::queue::QueuePtr,
361            item: *const u8,
362            higher_prio_task_waken: Option<&mut bool>,
363        ) -> bool {
364            unsafe {
365                <$t as $crate::queue::QueueImplementation>::try_send_to_back_from_isr(
366                    queue,
367                    item,
368                    higher_prio_task_waken,
369                )
370            }
371        }
372
373        #[unsafe(no_mangle)]
374        #[inline]
375        fn esp_rtos_queue_receive(
376            queue: $crate::queue::QueuePtr,
377            item: *mut u8,
378            timeout_us: Option<u32>,
379        ) -> bool {
380            unsafe { <$t as $crate::queue::QueueImplementation>::receive(queue, item, timeout_us) }
381        }
382
383        #[unsafe(no_mangle)]
384        #[inline]
385        fn esp_rtos_queue_receive_with_deadline(
386            queue: $crate::queue::QueuePtr,
387            item: *mut u8,
388            deadline_instant: Option<u64>,
389        ) -> bool {
390            unsafe {
391                <$t as $crate::queue::QueueImplementation>::receive_with_deadline(
392                    queue,
393                    item,
394                    deadline_instant,
395                )
396            }
397        }
398
399        #[unsafe(no_mangle)]
400        #[inline]
401        fn esp_rtos_queue_try_receive_from_isr(
402            queue: $crate::queue::QueuePtr,
403            item: *mut u8,
404            higher_prio_task_waken: Option<&mut bool>,
405        ) -> bool {
406            unsafe {
407                <$t as $crate::queue::QueueImplementation>::try_receive_from_isr(
408                    queue,
409                    item,
410                    higher_prio_task_waken,
411                )
412            }
413        }
414
415        #[unsafe(no_mangle)]
416        #[inline]
417        fn esp_rtos_queue_remove(queue: $crate::queue::QueuePtr, item: *mut u8) {
418            unsafe { <$t as $crate::queue::QueueImplementation>::remove(queue, item) }
419        }
420
421        #[unsafe(no_mangle)]
422        #[inline]
423        fn esp_rtos_queue_messages_waiting(queue: $crate::queue::QueuePtr) -> usize {
424            unsafe { <$t as $crate::queue::QueueImplementation>::messages_waiting(queue) }
425        }
426    };
427}
428
429/// Queue handle.
430///
431/// This handle is used to interact with queues created by the driver implementation.
432#[repr(transparent)]
433pub struct QueueHandle(QueuePtr);
434
435unsafe impl Send for QueueHandle {}
436unsafe impl Sync for QueueHandle {}
437
438impl QueueHandle {
439    /// Creates a new queue instance.
440    #[inline]
441    pub fn new(capacity: usize, item_size: usize) -> Self {
442        let ptr = unsafe { esp_rtos_queue_create(capacity, item_size) };
443        Self(ptr)
444    }
445
446    /// Converts this object into a pointer without dropping it.
447    #[inline]
448    pub fn leak(self) -> QueuePtr {
449        let ptr = self.0;
450        core::mem::forget(self);
451        ptr
452    }
453
454    /// Recovers the object from a leaked pointer.
455    ///
456    /// # Safety
457    ///
458    /// - The caller must only use pointers created using [`Self::leak`].
459    /// - The caller must ensure the pointer is not shared.
460    #[inline]
461    pub unsafe fn from_ptr(ptr: QueuePtr) -> Self {
462        Self(ptr)
463    }
464
465    /// Creates a reference to this object from a leaked pointer.
466    ///
467    /// This function is used in the esp-radio code to interact with the queue.
468    ///
469    /// # Safety
470    ///
471    /// - The caller must only use pointers created using [`Self::leak`].
472    #[inline]
473    pub unsafe fn ref_from_ptr(ptr: &QueuePtr) -> &Self {
474        unsafe { core::mem::transmute(ptr) }
475    }
476
477    /// Enqueues a high-priority item.
478    ///
479    /// If the queue is full, this function will block for the given timeout. If timeout is None,
480    /// the function will block indefinitely.
481    ///
482    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
483    ///
484    /// # Safety
485    ///
486    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
487    /// a size equal to the queue's item size.
488    #[inline]
489    pub unsafe fn send_to_front(&self, item: *const u8, timeout_us: Option<u32>) -> bool {
490        unsafe { esp_rtos_queue_send_to_front(self.0, item, timeout_us) }
491    }
492
493    /// Enqueues a high-priority item.
494    ///
495    /// If the queue is full, this function will block until the deadline is reached. If the
496    /// deadline is None, the function will block indefinitely.
497    ///
498    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
499    ///
500    /// # Safety
501    ///
502    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
503    /// a size equal to the queue's item size.
504    #[inline]
505    pub unsafe fn send_to_front_with_deadline(
506        &self,
507        item: *const u8,
508        deadline_instant: Option<u64>,
509    ) -> bool {
510        unsafe { esp_rtos_queue_send_to_front_with_deadline(self.0, item, deadline_instant) }
511    }
512
513    /// Enqueues an item.
514    ///
515    /// If the queue is full, this function will block for the given timeout. If timeout is None,
516    /// the function will block indefinitely.
517    ///
518    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
519    ///
520    /// # Safety
521    ///
522    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
523    /// a size equal to the queue's item size.
524    #[inline]
525    pub unsafe fn send_to_back(&self, item: *const u8, timeout_us: Option<u32>) -> bool {
526        unsafe { esp_rtos_queue_send_to_back(self.0, item, timeout_us) }
527    }
528
529    /// Enqueues an item.
530    ///
531    /// If the queue is full, this function will block until the given deadline. If deadline is
532    /// None, the function will block indefinitely.
533    ///
534    /// This function returns `true` if the item was successfully enqueued, `false` otherwise.
535    ///
536    /// # Safety
537    ///
538    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
539    /// a size equal to the queue's item size.
540    #[inline]
541    pub unsafe fn send_to_back_with_deadline(
542        &self,
543        item: *const u8,
544        deadline_instant: Option<u64>,
545    ) -> bool {
546        unsafe { esp_rtos_queue_send_to_back_with_deadline(self.0, item, deadline_instant) }
547    }
548
549    /// Attempts to enqueues an item.
550    ///
551    /// If the queue is full, this function will immediately return `false`.
552    ///
553    /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
554    /// is set to `true`.
555    ///
556    /// # Safety
557    ///
558    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
559    /// a size equal to the queue's item size.
560    #[inline]
561    pub unsafe fn try_send_to_back_from_isr(
562        &self,
563        item: *const u8,
564        higher_priority_task_waken: Option<&mut bool>,
565    ) -> bool {
566        unsafe {
567            esp_rtos_queue_try_send_to_back_from_isr(self.0, item, higher_priority_task_waken)
568        }
569    }
570
571    /// Dequeues an item from the queue.
572    ///
573    /// If the queue is empty, this function will block for the given timeout. If timeout is None,
574    /// the function will block indefinitely.
575    ///
576    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
577    ///
578    /// # Safety
579    ///
580    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
581    /// a size equal to the queue's item size.
582    #[inline]
583    pub unsafe fn receive(&self, item: *mut u8, timeout_us: Option<u32>) -> bool {
584        unsafe { esp_rtos_queue_receive(self.0, item, timeout_us) }
585    }
586
587    /// Dequeues an item from the queue.
588    ///
589    /// If the queue is empty, this function will block until the given deadline is reached. If
590    /// deadline is None, the function will block indefinitely.
591    ///
592    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
593    ///
594    /// # Safety
595    ///
596    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
597    /// a size equal to the queue's item size.
598    #[inline]
599    pub unsafe fn receive_with_deadline(
600        &self,
601        item: *mut u8,
602        deadline_instant: Option<u64>,
603    ) -> bool {
604        unsafe { esp_rtos_queue_receive_with_deadline(self.0, item, deadline_instant) }
605    }
606
607    /// Attempts to dequeue an item from the queue.
608    ///
609    /// If the queue is empty, this function will return `false` immediately.
610    ///
611    /// This function returns `true` if the item was successfully dequeued, `false` otherwise.
612    ///
613    /// If a higher priority task is woken up by this operation, the `higher_prio_task_waken` flag
614    /// is set to `true`.
615    ///
616    /// # Safety
617    ///
618    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
619    /// a size equal to the queue's item size.
620    #[inline]
621    pub unsafe fn try_receive_from_isr(
622        &self,
623        item: *mut u8,
624        higher_priority_task_waken: Option<&mut bool>,
625    ) -> bool {
626        unsafe { esp_rtos_queue_try_receive_from_isr(self.0, item, higher_priority_task_waken) }
627    }
628
629    /// Removes an item from the queue.
630    ///
631    /// # Safety
632    ///
633    /// The caller must ensure that `item` can be dereferenced and points to an allocation of
634    /// a size equal to the queue's item size.
635    #[inline]
636    pub unsafe fn remove(&self, item: *const u8) {
637        unsafe { esp_rtos_queue_remove(self.0, item) }
638    }
639
640    /// Returns the number of messages in the queue.
641    #[inline]
642    pub fn messages_waiting(&self) -> usize {
643        unsafe { esp_rtos_queue_messages_waiting(self.0) }
644    }
645}
646
647impl Drop for QueueHandle {
648    #[inline]
649    fn drop(&mut self) {
650        unsafe { esp_rtos_queue_delete(self.0) };
651    }
652}
653
654#[cfg(feature = "ipc-implementations")]
655mod implementation {
656    use alloc::{boxed::Box, vec};
657
658    use esp_sync::NonReentrantMutex;
659
660    use super::*;
661    use crate::{
662        now,
663        semaphore::{SemaphoreHandle, SemaphoreKind},
664    };
665
666    struct QueueInner {
667        storage: Box<[u8]>,
668        item_size: usize,
669        capacity: usize,
670        count: usize,
671        current_read: usize,
672        current_write: usize,
673    }
674
675    impl QueueInner {
676        fn get(&self, index: usize) -> &[u8] {
677            let item_start = self.item_size * index;
678            &self.storage[item_start..][..self.item_size]
679        }
680
681        fn get_mut(&mut self, index: usize) -> &mut [u8] {
682            let item_start = self.item_size * index;
683            &mut self.storage[item_start..][..self.item_size]
684        }
685
686        fn len(&self) -> usize {
687            self.count
688        }
689
690        fn send_to_back(&mut self, item: *const u8) {
691            let item = unsafe { core::slice::from_raw_parts(item, self.item_size) };
692
693            let dst = self.get_mut(self.current_write);
694            dst.copy_from_slice(item);
695
696            self.current_write = (self.current_write + 1) % self.capacity;
697            self.count += 1;
698        }
699
700        fn send_to_front(&mut self, item: *const u8) {
701            let item = unsafe { core::slice::from_raw_parts(item, self.item_size) };
702
703            self.current_read = (self.current_read + self.capacity - 1) % self.capacity;
704
705            let dst = self.get_mut(self.current_read);
706            dst.copy_from_slice(item);
707
708            self.count += 1;
709        }
710
711        fn read_from_front(&mut self, dst: *mut u8) {
712            let dst = unsafe { core::slice::from_raw_parts_mut(dst, self.item_size) };
713
714            let src = self.get(self.current_read);
715            dst.copy_from_slice(src);
716
717            self.current_read = (self.current_read + 1) % self.capacity;
718            self.count -= 1;
719        }
720
721        fn remove(&mut self, item: *const u8) -> bool {
722            let count = self.len();
723
724            if count == 0 {
725                return false;
726            }
727
728            let mut tmp_item = vec![0; self.item_size];
729
730            let mut found = false;
731            let item_slice = unsafe { core::slice::from_raw_parts(item, self.item_size) };
732            for _ in 0..count {
733                self.read_from_front(tmp_item.as_mut_ptr().cast());
734
735                if found || &tmp_item[..] != item_slice {
736                    self.send_to_back(tmp_item.as_mut_ptr().cast());
737                } else {
738                    found = true;
739                }
740
741                // Note that even if we find our item, we'll need to keep cycling through everything
742                // to keep insertion order.
743            }
744
745            found
746        }
747    }
748
749    /// A suitable queue implementation that only requires semaphores from the OS.
750    ///
751    /// Register in your OS implementation by adding the following code:
752    ///
753    /// ```rust
754    /// use esp_radio_rtos_driver::{queue::CompatQueue, register_queue_implementation};
755    ///
756    /// register_queue_implementation!(CompatQueue);
757    /// ```
758    pub struct CompatQueue {
759        /// Allows interior mutability for the queue's inner state, when the mutex is held.
760        inner: NonReentrantMutex<QueueInner>,
761
762        semaphore_empty: SemaphoreHandle,
763        semaphore_full: SemaphoreHandle,
764    }
765
766    impl CompatQueue {
767        fn new(capacity: usize, item_size: usize) -> Self {
768            let storage = vec![0; capacity * item_size].into_boxed_slice();
769            let semaphore_empty = SemaphoreHandle::new(SemaphoreKind::Counting {
770                max: capacity as u32,
771                initial: capacity as u32,
772            });
773            let semaphore_full = SemaphoreHandle::new(SemaphoreKind::Counting {
774                max: capacity as u32,
775                initial: 0,
776            });
777            Self {
778                inner: NonReentrantMutex::new(QueueInner {
779                    storage,
780                    item_size,
781                    capacity,
782                    count: 0,
783                    current_read: 0,
784                    current_write: 0,
785                }),
786                semaphore_empty,
787                semaphore_full,
788            }
789        }
790
791        unsafe fn from_ptr<'a>(ptr: QueuePtr) -> &'a Self {
792            unsafe { ptr.cast::<Self>().as_ref() }
793        }
794
795        fn with<R>(&self, f: impl FnOnce(&mut QueueInner) -> R) -> R {
796            self.inner.with(f)
797        }
798    }
799
800    impl QueueImplementation for CompatQueue {
801        fn create(capacity: usize, item_size: usize) -> QueuePtr {
802            let q = Box::new(CompatQueue::new(capacity, item_size));
803            NonNull::from(Box::leak(q)).cast()
804        }
805
806        unsafe fn delete(queue: QueuePtr) {
807            let q = unsafe { Box::from_raw(queue.cast::<CompatQueue>().as_ptr()) };
808            core::mem::drop(q);
809        }
810
811        unsafe fn send_to_front(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
812            let deadline_instant = timeout_us.map(|timeout| now() + timeout as u64);
813            unsafe { Self::send_to_front_with_deadline(queue, item, deadline_instant) }
814        }
815
816        unsafe fn send_to_front_with_deadline(
817            queue: QueuePtr,
818            item: *const u8,
819            deadline_instant: Option<u64>,
820        ) -> bool {
821            let queue = unsafe { CompatQueue::from_ptr(queue) };
822
823            if queue.semaphore_empty.take_with_deadline(deadline_instant) {
824                queue.with(|inner| inner.send_to_front(item));
825                queue.semaphore_full.give();
826                true
827            } else {
828                false
829            }
830        }
831
832        unsafe fn send_to_back(queue: QueuePtr, item: *const u8, timeout_us: Option<u32>) -> bool {
833            let deadline_instant = timeout_us.map(|timeout| now() + timeout as u64);
834            unsafe { Self::send_to_back_with_deadline(queue, item, deadline_instant) }
835        }
836
837        unsafe fn send_to_back_with_deadline(
838            queue: QueuePtr,
839            item: *const u8,
840            deadline_instant: Option<u64>,
841        ) -> bool {
842            let queue = unsafe { CompatQueue::from_ptr(queue) };
843
844            if queue.semaphore_empty.take_with_deadline(deadline_instant) {
845                queue.with(|inner| inner.send_to_back(item));
846                queue.semaphore_full.give();
847                true
848            } else {
849                false
850            }
851        }
852
853        unsafe fn try_send_to_back_from_isr(
854            queue: QueuePtr,
855            item: *const u8,
856            mut higher_prio_task_waken: Option<&mut bool>,
857        ) -> bool {
858            let queue = unsafe { CompatQueue::from_ptr(queue) };
859
860            if queue
861                .semaphore_empty
862                .try_take_from_isr(higher_prio_task_waken.as_deref_mut())
863            {
864                queue.with(|inner| inner.send_to_back(item));
865                queue
866                    .semaphore_full
867                    .try_give_from_isr(higher_prio_task_waken);
868                true
869            } else {
870                false
871            }
872        }
873
874        unsafe fn receive(queue: QueuePtr, item: *mut u8, timeout_us: Option<u32>) -> bool {
875            let deadline_instant = timeout_us.map(|timeout| now() + timeout as u64);
876            unsafe { Self::receive_with_deadline(queue, item, deadline_instant) }
877        }
878
879        unsafe fn receive_with_deadline(
880            queue: QueuePtr,
881            item: *mut u8,
882            deadline_instant: Option<u64>,
883        ) -> bool {
884            let queue = unsafe { CompatQueue::from_ptr(queue) };
885
886            if queue.semaphore_full.take_with_deadline(deadline_instant) {
887                queue.with(|inner| inner.read_from_front(item));
888                queue.semaphore_empty.give();
889                true
890            } else {
891                false
892            }
893        }
894
895        unsafe fn try_receive_from_isr(
896            queue: QueuePtr,
897            item: *mut u8,
898            mut higher_prio_task_waken: Option<&mut bool>,
899        ) -> bool {
900            let queue = unsafe { CompatQueue::from_ptr(queue) };
901
902            if queue
903                .semaphore_full
904                .try_take_from_isr(higher_prio_task_waken.as_deref_mut())
905            {
906                queue.with(|inner| inner.read_from_front(item));
907                queue
908                    .semaphore_empty
909                    .try_give_from_isr(higher_prio_task_waken);
910                true
911            } else {
912                false
913            }
914        }
915
916        unsafe fn remove(queue: QueuePtr, item: *const u8) {
917            let queue = unsafe { CompatQueue::from_ptr(queue) };
918
919            if queue.semaphore_full.take(Some(0)) {
920                let item_removed = queue.with(|inner| inner.remove(item));
921
922                if item_removed {
923                    queue.semaphore_empty.give();
924                } else {
925                    queue.semaphore_full.give();
926                }
927            }
928        }
929
930        fn messages_waiting(queue: QueuePtr) -> usize {
931            let queue = unsafe { CompatQueue::from_ptr(queue) };
932
933            queue.semaphore_full.current_count() as usize
934        }
935    }
936}
937
938#[cfg(feature = "ipc-implementations")]
939pub use implementation::CompatQueue;