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 <type_traits>
32#include <utility>
33
34namespace idfxx {
35
40enum class wait_mode {
41 any,
42 all,
43};
44
81template<flag_enum E>
82 requires(sizeof(std::underlying_type_t<E>) <= sizeof(EventBits_t))
84public:
91 : _handle(xEventGroupCreate()) {
92 if (_handle == nullptr) {
94 }
95 }
96
104 if (_handle != nullptr) {
105 vEventGroupDelete(_handle);
106 }
107 }
108
109 event_group(const event_group&) = delete;
111
114 : _handle(std::exchange(other._handle, nullptr)) {}
115
117 event_group& operator=(event_group&& other) noexcept {
118 if (this != &other) {
119 if (_handle != nullptr) {
120 vEventGroupDelete(_handle);
121 }
122 _handle = std::exchange(other._handle, nullptr);
123 }
124 return *this;
125 }
126
127 // =========================================================================
128 // Set operations
129 // =========================================================================
130
144 if (_handle == nullptr) {
145 return {};
146 }
147 return _from_bits(xEventGroupSetBits(_handle, to_underlying(bits)));
148 }
149
150 // =========================================================================
151 // Clear operations
152 // =========================================================================
153
163 if (_handle == nullptr) {
164 return {};
165 }
166 return _from_bits(xEventGroupClearBits(_handle, to_underlying(bits)));
167 }
168
169 // =========================================================================
170 // Get operations
171 // =========================================================================
172
179 if (_handle == nullptr) {
180 return {};
181 }
182 return _from_bits(xEventGroupGetBits(_handle));
183 }
184
185 // =========================================================================
186 // Wait operations
187 // =========================================================================
188
189#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
206 return unwrap(try_wait(bits, mode, clear_on_exit));
207 }
208
225 template<typename Rep, typename Period>
227 wait(flags<E> bits, wait_mode mode, const std::chrono::duration<Rep, Period>& timeout, bool clear_on_exit = true) {
228 return unwrap(try_wait(bits, mode, timeout, clear_on_exit));
229 }
230
247 template<typename Rep, typename Period>
248 flags<E> wait(flags<E> bits, const std::chrono::duration<Rep, Period>& timeout, bool clear_on_exit = true) {
249 return unwrap(try_wait(bits, wait_mode::all, timeout, clear_on_exit));
250 }
251
268 template<typename Clock, typename Duration>
271 wait_mode mode,
272 const std::chrono::time_point<Clock, Duration>& deadline,
273 bool clear_on_exit = true
274 ) {
275 return unwrap(try_wait_until(bits, mode, deadline, clear_on_exit));
276 }
277
294 template<typename Clock, typename Duration>
296 wait_until(flags<E> bits, const std::chrono::time_point<Clock, Duration>& deadline, bool clear_on_exit = true) {
297 return unwrap(try_wait_until(bits, wait_mode::all, deadline, clear_on_exit));
298 }
299#endif
300
315 return _try_wait(bits, mode, clear_on_exit, portMAX_DELAY);
316 }
317
332 template<typename Rep, typename Period>
335 wait_mode mode,
336 const std::chrono::duration<Rep, Period>& timeout,
337 bool clear_on_exit = true
338 ) {
339 return _try_wait(bits, mode, clear_on_exit, chrono::ticks(timeout));
340 }
341
356 template<typename Rep, typename Period>
358 try_wait(flags<E> bits, const std::chrono::duration<Rep, Period>& timeout, bool clear_on_exit = true) {
360 }
361
376 template<typename Clock, typename Duration>
379 wait_mode mode,
380 const std::chrono::time_point<Clock, Duration>& deadline,
381 bool clear_on_exit = true
382 ) {
383 auto remaining = deadline - Clock::now();
384 if (remaining <= decltype(remaining)::zero()) {
385 return _try_wait(bits, mode, clear_on_exit, 0);
386 }
387 return _try_wait(bits, mode, clear_on_exit, chrono::ticks(remaining));
388 }
389
404 template<typename Clock, typename Duration>
406 try_wait_until(flags<E> bits, const std::chrono::time_point<Clock, Duration>& deadline, bool clear_on_exit = true) {
407 return try_wait_until(bits, wait_mode::all, deadline, clear_on_exit);
408 }
409
410 // =========================================================================
411 // Sync operations
412 // =========================================================================
413
414#ifdef CONFIG_COMPILER_CXX_EXCEPTIONS
433
449 template<typename Rep, typename Period>
450 flags<E> sync(flags<E> set_bits, flags<E> wait_bits, const std::chrono::duration<Rep, Period>& timeout) {
451 return unwrap(try_sync(set_bits, wait_bits, timeout));
452 }
453
469 template<typename Clock, typename Duration>
471 sync_until(flags<E> set_bits, flags<E> wait_bits, const std::chrono::time_point<Clock, Duration>& deadline) {
472 return unwrap(try_sync_until(set_bits, wait_bits, deadline));
473 }
474#endif
475
494
508 template<typename Rep, typename Period>
510 try_sync(flags<E> set_bits, flags<E> wait_bits, const std::chrono::duration<Rep, Period>& timeout) {
511 return _try_sync(set_bits, wait_bits, chrono::ticks(timeout));
512 }
513
527 template<typename Clock, typename Duration>
529 try_sync_until(flags<E> set_bits, flags<E> wait_bits, const std::chrono::time_point<Clock, Duration>& deadline) {
530 auto remaining = deadline - Clock::now();
531 if (remaining <= decltype(remaining)::zero()) {
532 return _try_sync(set_bits, wait_bits, 0);
533 }
534 return _try_sync(set_bits, wait_bits, chrono::ticks(remaining));
535 }
536
537 // =========================================================================
538 // ISR operations
539 // =========================================================================
540
546 bool success;
547 bool yield;
548 };
549
572 if (_handle == nullptr) {
573 return {false, false};
574 }
577 return {ret == pdPASS, woken == pdTRUE};
578 }
579
593 if (_handle == nullptr) {
594 return {};
595 }
596 return _from_bits(xEventGroupClearBitsFromISR(_handle, to_underlying(bits)));
597 }
598
605 if (_handle == nullptr) {
606 return {};
607 }
608 return _from_bits(xEventGroupGetBitsFromISR(_handle));
609 }
610
611 // =========================================================================
612 // Handle access
613 // =========================================================================
614
624
625private:
626 static flags<E> _from_bits(EventBits_t bits) noexcept {
627 return flags<E>(static_cast<std::underlying_type_t<E>>(bits));
628 }
629
630 [[nodiscard]] result<flags<E>> _try_wait(flags<E> bits, wait_mode mode, bool clear_on_exit, TickType_t ticks) {
631 if (_handle == nullptr) {
633 }
635 _handle,
639 ticks
640 );
641 auto result_flags = _from_bits(result_bits);
642 bool satisfied = (mode == wait_mode::all) ? result_flags.contains(bits) : result_flags.contains_any(bits);
643 if (!satisfied) {
644 return error(errc::timeout);
645 }
646 return result_flags;
647 }
648
650 if (_handle == nullptr) {
652 }
654 auto result_flags = _from_bits(result_bits);
655 if (!result_flags.contains(wait_bits)) {
656 return error(errc::timeout);
657 }
658 return result_flags;
659 }
660
661 EventGroupHandle_t _handle = nullptr;
662};
663
// end of idfxx_event_group
665
666} // namespace idfxx
Type-safe inter-task event group for bit-level synchronization.
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 > wait_until(flags< E > bits, const std::chrono::time_point< Clock, Duration > &deadline, bool clear_on_exit=true)
Waits for event bits to be set, with a deadline (mode defaults to all).
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.
result< flags< E > > try_wait_until(flags< E > bits, const std::chrono::time_point< Clock, Duration > &deadline, bool clear_on_exit=true)
Waits for event bits to be set, with a deadline (mode defaults to all).
flags< E > wait(flags< E > bits, wait_mode mode=wait_mode::all, bool clear_on_exit=true)
Waits for event bits to be set, blocking indefinitely.
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.
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 & operator=(event_group &&other) noexcept
Move assignment.
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
result< flags< E > > try_wait(flags< E > bits, const std::chrono::duration< Rep, Period > &timeout, bool clear_on_exit=true)
Waits for event bits to be set, with a timeout (mode defaults to all).
event_group(event_group &&other) noexcept
Move constructor.
result< flags< E > > try_wait(flags< E > bits, wait_mode mode=wait_mode::all, bool clear_on_exit=true)
Waits for event bits to be set, blocking indefinitely.
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, const std::chrono::duration< Rep, Period > &timeout, bool clear_on_exit=true)
Waits for event bits to be set, with a timeout (mode defaults to all).
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:187
T unwrap(result< T > result)
Throws a std::system_error if the result is an error.
Definition error.hpp:307
@ invalid_state
Invalid state.
@ timeout
Operation timed out.
std::expected< T, std::error_code > result
result type wrapping a value or error code.
Definition error.hpp:120
constexpr auto to_underlying(flags< E > f) noexcept
Returns the underlying integral value of a flags object.
Definition flags.hpp:292
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.