idfxx 1.0.0
Modern C++23 components for ESP-IDF
Loading...
Searching...
No Matches
event_group.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
23#include <idfxx/chrono>
24#include <idfxx/error>
25#include <idfxx/flags>
26
27#include <chrono>
28#include <esp_attr.h>
29#include <freertos/FreeRTOS.h>
30#include <freertos/event_groups.h>
31#include <memory>
32#include <type_traits>
33
34namespace idfxx {
35
40enum class wait_mode {
41 any,
42 all,
43};
44
78template<flag_enum E>
79 requires(sizeof(std::underlying_type_t<E>) <= sizeof(EventBits_t))
81public:
82#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
89 [[nodiscard]] event_group()
90 : _handle(xEventGroupCreate()) {
91 if (_handle == nullptr) {
92 throw std::system_error(errc::no_mem);
93 }
94 }
95#endif
96
104 auto eg = std::unique_ptr<event_group>(new event_group(private_tag{}));
105 if (eg->_handle == nullptr) {
106 return error(errc::no_mem);
107 }
108 return eg;
109 }
110
118 if (_handle != nullptr) {
119 vEventGroupDelete(_handle);
120 }
121 }
122
123 // Non-copyable and non-movable
124 event_group(const event_group&) = delete;
128
129 // =========================================================================
130 // Set operations
131 // =========================================================================
132
145 flags<E> set(flags<E> bits) noexcept {
146 return flags<E>::from_raw(static_cast<std::underlying_type_t<E>>(xEventGroupSetBits(_handle, bits.value())));
147 }
148
149 // =========================================================================
150 // Clear operations
151 // =========================================================================
152
161 flags<E> clear(flags<E> bits) noexcept {
162 return flags<E>::from_raw(static_cast<std::underlying_type_t<E>>(xEventGroupClearBits(_handle, bits.value())));
163 }
164
165 // =========================================================================
166 // Get operations
167 // =========================================================================
168
174 [[nodiscard]] flags<E> get() const noexcept {
175 return flags<E>::from_raw(static_cast<std::underlying_type_t<E>>(xEventGroupGetBits(_handle)));
176 }
177
178 // =========================================================================
179 // Wait operations
180 // =========================================================================
181
182#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
196 [[nodiscard]] flags<E> wait(flags<E> bits, wait_mode mode, bool clear_on_exit = true) {
197 return unwrap(try_wait(bits, mode, clear_on_exit));
198 }
199
216 template<typename Rep, typename Period>
217 [[nodiscard]] flags<E>
218 wait(flags<E> bits, wait_mode mode, const std::chrono::duration<Rep, Period>& timeout, bool clear_on_exit = true) {
219 return unwrap(try_wait(bits, mode, timeout, clear_on_exit));
220 }
221
238 template<typename Clock, typename Duration>
239 [[nodiscard]] flags<E> wait_until(
240 flags<E> bits,
241 wait_mode mode,
242 const std::chrono::time_point<Clock, Duration>& deadline,
243 bool clear_on_exit = true
244 ) {
245 return unwrap(try_wait_until(bits, mode, deadline, clear_on_exit));
246 }
247#endif
248
260 [[nodiscard]] result<flags<E>> try_wait(flags<E> bits, wait_mode mode, bool clear_on_exit = true) {
261 return _try_wait(bits, mode, clear_on_exit, portMAX_DELAY);
262 }
263
278 template<typename Rep, typename Period>
279 [[nodiscard]] result<flags<E>> try_wait(
280 flags<E> bits,
281 wait_mode mode,
282 const std::chrono::duration<Rep, Period>& timeout,
283 bool clear_on_exit = true
284 ) {
285 return _try_wait(bits, mode, clear_on_exit, chrono::ticks(timeout));
286 }
287
302 template<typename Clock, typename Duration>
304 flags<E> bits,
305 wait_mode mode,
306 const std::chrono::time_point<Clock, Duration>& deadline,
307 bool clear_on_exit = true
308 ) {
309 auto remaining = deadline - Clock::now();
310 if (remaining <= decltype(remaining)::zero()) {
311 return _try_wait(bits, mode, clear_on_exit, 0);
312 }
313 return _try_wait(bits, mode, clear_on_exit, chrono::ticks(remaining));
314 }
315
316 // =========================================================================
317 // Sync operations
318 // =========================================================================
319
320#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
338 [[nodiscard]] flags<E> sync(flags<E> set_bits, flags<E> wait_bits) { return unwrap(try_sync(set_bits, wait_bits)); }
339
355 template<typename Rep, typename Period>
356 [[nodiscard]] flags<E>
357 sync(flags<E> set_bits, flags<E> wait_bits, const std::chrono::duration<Rep, Period>& timeout) {
358 return unwrap(try_sync(set_bits, wait_bits, timeout));
359 }
360
376 template<typename Clock, typename Duration>
377 [[nodiscard]] flags<E>
378 sync_until(flags<E> set_bits, flags<E> wait_bits, const std::chrono::time_point<Clock, Duration>& deadline) {
379 return unwrap(try_sync_until(set_bits, wait_bits, deadline));
380 }
381#endif
382
398 [[nodiscard]] result<flags<E>> try_sync(flags<E> set_bits, flags<E> wait_bits) {
399 return _try_sync(set_bits, wait_bits, portMAX_DELAY);
400 }
401
415 template<typename Rep, typename Period>
416 [[nodiscard]] result<flags<E>>
417 try_sync(flags<E> set_bits, flags<E> wait_bits, const std::chrono::duration<Rep, Period>& timeout) {
418 return _try_sync(set_bits, wait_bits, chrono::ticks(timeout));
419 }
420
434 template<typename Clock, typename Duration>
435 [[nodiscard]] result<flags<E>>
436 try_sync_until(flags<E> set_bits, flags<E> wait_bits, const std::chrono::time_point<Clock, Duration>& deadline) {
437 auto remaining = deadline - Clock::now();
438 if (remaining <= decltype(remaining)::zero()) {
439 return _try_sync(set_bits, wait_bits, 0);
440 }
441 return _try_sync(set_bits, wait_bits, chrono::ticks(remaining));
442 }
443
444 // =========================================================================
445 // ISR operations
446 // =========================================================================
447
453 bool success;
454 bool yield;
455 };
456
478 [[nodiscard]] isr_set_result IRAM_ATTR set_from_isr(flags<E> bits) noexcept {
479 BaseType_t woken = pdFALSE;
480 BaseType_t ret = xEventGroupSetBitsFromISR(_handle, bits.value(), &woken);
481 return {ret == pdPASS, woken == pdTRUE};
482 }
483
496 [[nodiscard]] flags<E> IRAM_ATTR clear_from_isr(flags<E> bits) noexcept {
497 return flags<E>::from_raw(
498 static_cast<std::underlying_type_t<E>>(xEventGroupClearBitsFromISR(_handle, bits.value()))
499 );
500 }
501
507 [[nodiscard]] flags<E> IRAM_ATTR get_from_isr() const noexcept {
508 return flags<E>::from_raw(static_cast<std::underlying_type_t<E>>(xEventGroupGetBitsFromISR(_handle)));
509 }
510
511 // =========================================================================
512 // Handle access
513 // =========================================================================
514
523 [[nodiscard]] EventGroupHandle_t idf_handle() const noexcept { return _handle; }
524
525private:
526 struct private_tag {};
527
528 event_group(private_tag) noexcept
529 : _handle(xEventGroupCreate()) {}
530
531 [[nodiscard]] result<flags<E>> _try_wait(flags<E> bits, wait_mode mode, bool clear_on_exit, TickType_t ticks) {
532 EventBits_t result_bits = xEventGroupWaitBits(
533 _handle, bits.value(), clear_on_exit ? pdTRUE : pdFALSE, mode == wait_mode::all ? pdTRUE : pdFALSE, ticks
534 );
535 auto result_flags = flags<E>::from_raw(static_cast<std::underlying_type_t<E>>(result_bits));
536 bool satisfied = (mode == wait_mode::all) ? result_flags.contains(bits) : result_flags.contains_any(bits);
537 if (!satisfied) {
538 return error(errc::timeout);
539 }
540 return result_flags;
541 }
542
543 [[nodiscard]] result<flags<E>> _try_sync(flags<E> set_bits, flags<E> wait_bits, TickType_t ticks) {
544 EventBits_t result_bits = xEventGroupSync(_handle, set_bits.value(), wait_bits.value(), ticks);
545 auto result_flags = flags<E>::from_raw(static_cast<std::underlying_type_t<E>>(result_bits));
546 if (!result_flags.contains(wait_bits)) {
547 return error(errc::timeout);
548 }
549 return result_flags;
550 }
551
552 EventGroupHandle_t _handle;
553};
554
// end of idfxx_event_group
556
557} // namespace idfxx
Type-safe inter-task event group for bit-level synchronization.
event_group & operator=(event_group &&)=delete
flags< E > get() const noexcept
Returns the current event bits.
flags< E > sync(flags< E > set_bits, flags< E > wait_bits, const std::chrono::duration< Rep, Period > &timeout)
Atomically sets bits and waits for other bits, with a timeout.
result< flags< E > > try_wait(flags< E > bits, wait_mode mode, const std::chrono::duration< Rep, Period > &timeout, bool clear_on_exit=true)
Waits for event bits to be set, with a timeout.
flags< E > clear(flags< E > bits) noexcept
Clears event bits in the event group.
flags< E > IRAM_ATTR clear_from_isr(flags< E > bits) noexcept
Clears event bits from ISR context.
result< flags< E > > try_sync(flags< E > set_bits, flags< E > wait_bits)
Atomically sets bits and waits for other bits, blocking indefinitely.
flags< E > sync(flags< E > set_bits, flags< E > wait_bits)
Atomically sets bits and waits for other bits, blocking indefinitely.
event_group(event_group &&)=delete
EventGroupHandle_t idf_handle() const noexcept
Returns the underlying FreeRTOS event group handle.
flags< E > IRAM_ATTR get_from_isr() const noexcept
Returns the current event bits from ISR context.
flags< E > sync_until(flags< E > set_bits, flags< E > wait_bits, const std::chrono::time_point< Clock, Duration > &deadline)
Atomically sets bits and waits for other bits, with a deadline.
result< flags< E > > try_sync_until(flags< E > set_bits, flags< E > wait_bits, const std::chrono::time_point< Clock, Duration > &deadline)
Atomically sets bits and waits for other bits, with a deadline.
~event_group()
Destroys the event group and releases all resources.
event_group(const event_group &)=delete
flags< E > set(flags< E > bits) noexcept
Sets event bits in the event group.
result< flags< E > > try_sync(flags< E > set_bits, flags< E > wait_bits, const std::chrono::duration< Rep, Period > &timeout)
Atomically sets bits and waits for other bits, with a timeout.
static result< std::unique_ptr< event_group > > make()
Creates an event group.
result< flags< E > > try_wait_until(flags< E > bits, wait_mode mode, const std::chrono::time_point< Clock, Duration > &deadline, bool clear_on_exit=true)
Waits for event bits to be set, with a deadline.
event_group()
Creates an event group.
flags< E > wait(flags< E > bits, wait_mode mode, const std::chrono::duration< Rep, Period > &timeout, bool clear_on_exit=true)
Waits for event bits to be set, with a timeout.
event_group & operator=(const event_group &)=delete
isr_set_result IRAM_ATTR set_from_isr(flags< E > bits) noexcept
Sets event bits from ISR context.
flags< E > wait_until(flags< E > bits, wait_mode mode, const std::chrono::time_point< Clock, Duration > &deadline, bool clear_on_exit=true)
Waits for event bits to be set, with a deadline.
flags< E > wait(flags< E > bits, wait_mode mode, bool clear_on_exit=true)
Waits for event bits to be set, blocking indefinitely.
result< flags< E > > try_wait(flags< E > bits, wait_mode mode, bool clear_on_exit=true)
Waits for event bits to be set, blocking indefinitely.
Type-safe set of flags from a scoped enum.
Definition flags.hpp:88
static constexpr flags from_raw(underlying v) noexcept
Constructs flags from a raw underlying value.
Definition flags.hpp:273
constexpr TickType_t ticks(const std::chrono::duration< Rep, Period > &d)
Converts a std::chrono duration to TickType_t ticks.
Definition chrono.hpp:33
wait_mode
Specifies whether to wait for any or all of the requested bits.
@ any
Wait for any of the specified bits to be set.
@ all
Wait for all of the specified bits to be set.
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.
std::expected< T, std::error_code > result
result type wrapping a value or error code.
Definition error.hpp:118
Result of setting event bits from ISR context.
bool yield
true if a context switch should be requested.
bool success
true if the set was posted to the timer daemon task successfully.