idfxx 1.0.0
Modern C++23 components for ESP-IDF
Loading...
Searching...
No Matches
queue.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Chris Leishman
3
4#pragma once
5
22#include <idfxx/chrono>
23#include <idfxx/error>
24#include <idfxx/memory>
25
26#include <chrono>
27#include <cstddef>
28#include <esp_attr.h>
29#include <freertos/FreeRTOS.h>
30#include <freertos/idf_additions.h>
31#include <freertos/queue.h>
32#include <memory>
33#include <optional>
34#include <type_traits>
35#include <utility>
36
37namespace idfxx {
38
65template<typename T>
66 requires std::is_trivially_copyable_v<T>
67class queue {
68public:
69#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
79 [[nodiscard]] explicit queue(size_t length, memory_type mem_type = memory_type::internal)
80 : _handle(nullptr) {
81 if (length == 0) {
82 throw std::system_error(errc::invalid_arg);
83 }
84 _handle = xQueueCreateWithCaps(length, sizeof(T), std::to_underlying(mem_type));
85 if (_handle == nullptr) {
86 throw std::system_error(errc::no_mem);
87 }
88 }
89#endif
90
100 [[nodiscard]] static result<std::unique_ptr<queue>>
101 make(size_t length, memory_type mem_type = memory_type::internal) {
102 if (length == 0) {
103 return error(errc::invalid_arg);
104 }
105 auto q = std::unique_ptr<queue>(new queue());
106 q->_handle = xQueueCreateWithCaps(length, sizeof(T), std::to_underlying(mem_type));
107 if (q->_handle == nullptr) {
108 return error(errc::no_mem);
109 }
110 return q;
111 }
112
119 if (_handle != nullptr) {
120 vQueueDeleteWithCaps(_handle);
121 }
122 }
123
124 // Non-copyable and non-movable
125 queue(const queue&) = delete;
126 queue& operator=(const queue&) = delete;
127 queue(queue&&) = delete;
128 queue& operator=(queue&&) = delete;
129
130 // =========================================================================
131 // Send operations
132 // =========================================================================
133
134#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
144 void send(const T& item) { unwrap(try_send(item)); }
145
159 template<typename Rep, typename Period>
160 void send(const T& item, const std::chrono::duration<Rep, Period>& timeout) {
161 unwrap(try_send(item, timeout));
162 }
163
177 template<typename Clock, typename Duration>
178 void send_until(const T& item, const std::chrono::time_point<Clock, Duration>& deadline) {
179 unwrap(try_send_until(item, deadline));
180 }
181
192 void send_to_front(const T& item) { unwrap(try_send_to_front(item)); }
193
208 template<typename Rep, typename Period>
209 void send_to_front(const T& item, const std::chrono::duration<Rep, Period>& timeout) {
211 }
212
227 template<typename Clock, typename Duration>
228 void send_to_front_until(const T& item, const std::chrono::time_point<Clock, Duration>& deadline) {
229 unwrap(try_send_to_front_until(item, deadline));
230 }
231#endif
232
242 [[nodiscard]] result<void> try_send(const T& item) { return _try_send(item, portMAX_DELAY); }
243
256 template<typename Rep, typename Period>
257 [[nodiscard]] result<void> try_send(const T& item, const std::chrono::duration<Rep, Period>& timeout) {
258 return _try_send(item, chrono::ticks(timeout));
259 }
260
273 template<typename Clock, typename Duration>
274 [[nodiscard]] result<void> try_send_until(const T& item, const std::chrono::time_point<Clock, Duration>& deadline) {
275 auto remaining = deadline - Clock::now();
276 if (remaining <= decltype(remaining)::zero()) {
277 return _try_send(item, 0);
278 }
279 return _try_send(item, chrono::ticks(remaining));
280 }
281
292 [[nodiscard]] result<void> try_send_to_front(const T& item) { return _try_send_to_front(item, portMAX_DELAY); }
293
307 template<typename Rep, typename Period>
308 [[nodiscard]] result<void> try_send_to_front(const T& item, const std::chrono::duration<Rep, Period>& timeout) {
309 return _try_send_to_front(item, chrono::ticks(timeout));
310 }
311
325 template<typename Clock, typename Duration>
326 [[nodiscard]] result<void>
327 try_send_to_front_until(const T& item, const std::chrono::time_point<Clock, Duration>& deadline) {
328 auto remaining = deadline - Clock::now();
329 if (remaining <= decltype(remaining)::zero()) {
330 return _try_send_to_front(item, 0);
331 }
332 return _try_send_to_front(item, chrono::ticks(remaining));
333 }
334
335 // =========================================================================
336 // Overwrite operations
337 // =========================================================================
338
339#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
352 void overwrite(const T& item) noexcept { try_overwrite(item); }
353#endif
354
366 void try_overwrite(const T& item) noexcept { xQueueOverwrite(_handle, &item); }
367
368 // =========================================================================
369 // Receive operations
370 // =========================================================================
371
372#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
382 [[nodiscard]] T receive() { return unwrap(try_receive()); }
383
397 template<typename Rep, typename Period>
398 [[nodiscard]] T receive(const std::chrono::duration<Rep, Period>& timeout) {
399 return unwrap(try_receive(timeout));
400 }
401
415 template<typename Clock, typename Duration>
416 [[nodiscard]] T receive_until(const std::chrono::time_point<Clock, Duration>& deadline) {
417 return unwrap(try_receive_until(deadline));
418 }
419#endif
420
429 [[nodiscard]] result<T> try_receive() { return _try_receive(portMAX_DELAY); }
430
442 template<typename Rep, typename Period>
443 [[nodiscard]] result<T> try_receive(const std::chrono::duration<Rep, Period>& timeout) {
444 return _try_receive(chrono::ticks(timeout));
445 }
446
458 template<typename Clock, typename Duration>
459 [[nodiscard]] result<T> try_receive_until(const std::chrono::time_point<Clock, Duration>& deadline) {
460 auto remaining = deadline - Clock::now();
461 if (remaining <= decltype(remaining)::zero()) {
462 return _try_receive(0);
463 }
464 return _try_receive(chrono::ticks(remaining));
465 }
466
467 // =========================================================================
468 // Peek operations
469 // =========================================================================
470
471#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
481 [[nodiscard]] T peek() { return unwrap(try_peek()); }
482
497 template<typename Rep, typename Period>
498 [[nodiscard]] T peek(const std::chrono::duration<Rep, Period>& timeout) {
499 return unwrap(try_peek(timeout));
500 }
501
516 template<typename Clock, typename Duration>
517 [[nodiscard]] T peek_until(const std::chrono::time_point<Clock, Duration>& deadline) {
518 return unwrap(try_peek_until(deadline));
519 }
520#endif
521
530 [[nodiscard]] result<T> try_peek() { return _try_peek(portMAX_DELAY); }
531
544 template<typename Rep, typename Period>
545 [[nodiscard]] result<T> try_peek(const std::chrono::duration<Rep, Period>& timeout) {
546 return _try_peek(chrono::ticks(timeout));
547 }
548
561 template<typename Clock, typename Duration>
562 [[nodiscard]] result<T> try_peek_until(const std::chrono::time_point<Clock, Duration>& deadline) {
563 auto remaining = deadline - Clock::now();
564 if (remaining <= decltype(remaining)::zero()) {
565 return _try_peek(0);
566 }
567 return _try_peek(chrono::ticks(remaining));
568 }
569
570 // =========================================================================
571 // ISR operations
572 // =========================================================================
573
579 bool success;
580 bool yield;
581 };
582
588 std::optional<T> item;
589 bool yield;
590 };
591
609 [[nodiscard]] isr_send_result IRAM_ATTR send_from_isr(const T& item) noexcept {
610 BaseType_t woken = pdFALSE;
611 BaseType_t ret = xQueueSendFromISR(_handle, &item, &woken);
612 return {ret == pdTRUE, woken == pdTRUE};
613 }
614
632 [[nodiscard]] isr_send_result IRAM_ATTR send_to_front_from_isr(const T& item) noexcept {
633 BaseType_t woken = pdFALSE;
634 BaseType_t ret = xQueueSendToFrontFromISR(_handle, &item, &woken);
635 return {ret == pdTRUE, woken == pdTRUE};
636 }
637
657 [[nodiscard]] bool IRAM_ATTR overwrite_from_isr(const T& item) noexcept {
658 BaseType_t woken = pdFALSE;
659 xQueueOverwriteFromISR(_handle, &item, &woken);
660 return woken == pdTRUE;
661 }
662
682 [[nodiscard]] isr_receive_result IRAM_ATTR receive_from_isr() noexcept {
683 T item;
684 BaseType_t woken = pdFALSE;
685 if (xQueueReceiveFromISR(_handle, &item, &woken) == pdTRUE) {
686 return {item, woken == pdTRUE};
687 }
688 return {std::nullopt, woken == pdTRUE};
689 }
690
704 [[nodiscard]] std::optional<T> IRAM_ATTR peek_from_isr() const noexcept {
705 T item;
706 if (xQueuePeekFromISR(_handle, &item) == pdTRUE) {
707 return item;
708 }
709 return std::nullopt;
710 }
711
712 // =========================================================================
713 // Query and reset
714 // =========================================================================
715
721 [[nodiscard]] size_t size() const noexcept { return uxQueueMessagesWaiting(_handle); }
722
728 [[nodiscard]] size_t available() const noexcept { return uxQueueSpacesAvailable(_handle); }
729
735 [[nodiscard]] bool empty() const noexcept { return uxQueueMessagesWaiting(_handle) == 0; }
736
742 [[nodiscard]] bool full() const noexcept { return uxQueueSpacesAvailable(_handle) == 0; }
743
752 [[nodiscard]] QueueHandle_t idf_handle() const noexcept { return _handle; }
753
759 void reset() noexcept { xQueueReset(_handle); }
760
761private:
762 queue() noexcept
763 : _handle(nullptr) {}
764
765 [[nodiscard]] result<void> _try_send(const T& item, TickType_t ticks) {
766 if (xQueueSend(_handle, &item, ticks) != pdTRUE) {
767 return error(errc::timeout);
768 }
769 return {};
770 }
771
772 [[nodiscard]] result<void> _try_send_to_front(const T& item, TickType_t ticks) {
773 if (xQueueSendToFront(_handle, &item, ticks) != pdTRUE) {
774 return error(errc::timeout);
775 }
776 return {};
777 }
778
779 [[nodiscard]] result<T> _try_receive(TickType_t ticks) {
780 T item;
781 if (xQueueReceive(_handle, &item, ticks) != pdTRUE) {
782 return error(errc::timeout);
783 }
784 return item;
785 }
786
787 [[nodiscard]] result<T> _try_peek(TickType_t ticks) {
788 T item;
789 if (xQueuePeek(_handle, &item, ticks) != pdTRUE) {
790 return error(errc::timeout);
791 }
792 return item;
793 }
794
795 QueueHandle_t _handle;
796};
797
// end of idfxx_queue
799
800} // namespace idfxx
Type-safe inter-task message queue.
Definition queue.hpp:67
void send_to_front(const T &item, const std::chrono::duration< Rep, Period > &timeout)
Sends an item to the front of the queue with a timeout.
Definition queue.hpp:209
T peek_until(const std::chrono::time_point< Clock, Duration > &deadline)
Peeks at the front item in the queue without removing it, with a deadline.
Definition queue.hpp:517
size_t size() const noexcept
Returns the number of items currently in the queue.
Definition queue.hpp:721
void try_overwrite(const T &item) noexcept
Overwrites the last item in the queue, or sends if the queue is not full.
Definition queue.hpp:366
static result< std::unique_ptr< queue > > make(size_t length, memory_type mem_type=memory_type::internal)
Creates a queue with the specified capacity.
Definition queue.hpp:101
void send_to_front_until(const T &item, const std::chrono::time_point< Clock, Duration > &deadline)
Sends an item to the front of the queue with a deadline.
Definition queue.hpp:228
queue & operator=(const queue &)=delete
bool empty() const noexcept
Checks if the queue is empty.
Definition queue.hpp:735
T peek(const std::chrono::duration< Rep, Period > &timeout)
Peeks at the front item in the queue without removing it, with a timeout.
Definition queue.hpp:498
bool full() const noexcept
Checks if the queue is full.
Definition queue.hpp:742
result< void > try_send_to_front_until(const T &item, const std::chrono::time_point< Clock, Duration > &deadline)
Sends an item to the front of the queue with a deadline.
Definition queue.hpp:327
isr_send_result IRAM_ATTR send_to_front_from_isr(const T &item) noexcept
Sends an item to the front of the queue from ISR context.
Definition queue.hpp:632
result< T > try_peek_until(const std::chrono::time_point< Clock, Duration > &deadline)
Peeks at the front item in the queue without removing it, with a deadline.
Definition queue.hpp:562
T receive()
Receives an item from the queue, blocking indefinitely.
Definition queue.hpp:382
queue(size_t length, memory_type mem_type=memory_type::internal)
Creates a queue with the specified capacity.
Definition queue.hpp:79
void send_to_front(const T &item)
Sends an item to the front of the queue, blocking indefinitely.
Definition queue.hpp:192
void reset() noexcept
Removes all items from the queue.
Definition queue.hpp:759
void send(const T &item)
Sends an item to the back of the queue, blocking indefinitely.
Definition queue.hpp:144
~queue()
Destroys the queue and releases all resources.
Definition queue.hpp:118
queue(const queue &)=delete
result< void > try_send(const T &item)
Sends an item to the back of the queue, blocking indefinitely.
Definition queue.hpp:242
size_t available() const noexcept
Returns the number of free spaces in the queue.
Definition queue.hpp:728
result< void > try_send_to_front(const T &item)
Sends an item to the front of the queue, blocking indefinitely.
Definition queue.hpp:292
result< T > try_peek(const std::chrono::duration< Rep, Period > &timeout)
Peeks at the front item in the queue without removing it, with a timeout.
Definition queue.hpp:545
queue(queue &&)=delete
queue & operator=(queue &&)=delete
std::optional< T > IRAM_ATTR peek_from_isr() const noexcept
Peeks at the front item in the queue from ISR context without removing it.
Definition queue.hpp:704
T receive_until(const std::chrono::time_point< Clock, Duration > &deadline)
Receives an item from the queue with a deadline.
Definition queue.hpp:416
QueueHandle_t idf_handle() const noexcept
Returns the underlying FreeRTOS queue handle.
Definition queue.hpp:752
isr_receive_result IRAM_ATTR receive_from_isr() noexcept
Receives an item from the queue in ISR context.
Definition queue.hpp:682
result< void > try_send(const T &item, const std::chrono::duration< Rep, Period > &timeout)
Sends an item to the back of the queue with a timeout.
Definition queue.hpp:257
result< void > try_send_to_front(const T &item, const std::chrono::duration< Rep, Period > &timeout)
Sends an item to the front of the queue with a timeout.
Definition queue.hpp:308
void send_until(const T &item, const std::chrono::time_point< Clock, Duration > &deadline)
Sends an item to the back of the queue with a deadline.
Definition queue.hpp:178
result< T > try_receive()
Receives an item from the queue, blocking indefinitely.
Definition queue.hpp:429
T peek()
Peeks at the front item in the queue without removing it, blocking indefinitely.
Definition queue.hpp:481
result< T > try_receive(const std::chrono::duration< Rep, Period > &timeout)
Receives an item from the queue with a timeout.
Definition queue.hpp:443
bool IRAM_ATTR overwrite_from_isr(const T &item) noexcept
Overwrites the last item in the queue from ISR context.
Definition queue.hpp:657
isr_send_result IRAM_ATTR send_from_isr(const T &item) noexcept
Sends an item to the back of the queue from ISR context.
Definition queue.hpp:609
result< T > try_peek()
Peeks at the front item in the queue without removing it, blocking indefinitely.
Definition queue.hpp:530
result< void > try_send_until(const T &item, const std::chrono::time_point< Clock, Duration > &deadline)
Sends an item to the back of the queue with a deadline.
Definition queue.hpp:274
T receive(const std::chrono::duration< Rep, Period > &timeout)
Receives an item from the queue with a timeout.
Definition queue.hpp:398
result< T > try_receive_until(const std::chrono::time_point< Clock, Duration > &deadline)
Receives an item from the queue with a deadline.
Definition queue.hpp:459
void send(const T &item, const std::chrono::duration< Rep, Period > &timeout)
Sends an item to the back of the queue with a timeout.
Definition queue.hpp:160
void overwrite(const T &item) noexcept
Overwrites the last item in the queue, or sends if the queue is not full.
Definition queue.hpp:352
constexpr TickType_t ticks(const std::chrono::duration< Rep, Period > &d)
Converts a std::chrono duration to TickType_t ticks.
Definition chrono.hpp:33
memory_type
Memory region type for heap allocations.
Definition memory.hpp:42
@ internal
Internal DRAM (default)
constexpr std::unexpected< std::error_code > error(E e) noexcept
Creates an unexpected error from an error code enum.
Definition error.hpp:142
T unwrap(result< T > result)
Throws a std::system_error if the result is an error.
Definition error.hpp:237
@ no_mem
Out of memory.
@ timeout
Operation timed out.
@ invalid_arg
Invalid argument.
std::expected< T, std::error_code > result
result type wrapping a value or error code.
Definition error.hpp:118
Result of an ISR receive operation.
Definition queue.hpp:587
std::optional< T > item
The received item, or std::nullopt if the queue was empty.
Definition queue.hpp:588
bool yield
true if a context switch should be requested.
Definition queue.hpp:589
Result of an ISR send operation.
Definition queue.hpp:578
bool success
true if the item was sent successfully.
Definition queue.hpp:579
bool yield
true if a context switch should be requested.
Definition queue.hpp:580