Skip to main content

osal_rs/freertos/
event_group.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21//! Event group synchronization primitives for FreeRTOS.
22//!
23//! Event groups allow threads to synchronize on multiple events simultaneously.
24//! Each event group contains a set of event bits (flags) that can be set, cleared,
25//! and waited upon. This is useful for complex synchronization scenarios where
26//! multiple conditions must be met.
27
28use core::fmt::{Debug, Display, Formatter};
29use core::ops::Deref;
30use core::ptr::null_mut;
31
32use super::ffi::{EventGroupHandle, pdFAIL, pdFALSE, vEventGroupDelete, xEventGroupClearBits, xEventGroupClearBitsFromISR, xEventGroupCreate, xEventGroupGetBitsFromISR, xEventGroupSetBits, xEventGroupSetBitsFromISR};
33use super::system::System;
34use super::types::{BaseType, EventBits, TickType};
35use crate::traits::{ToTick, EventGroupFn, SystemFn};
36use crate::utils::{Result, Error};
37
38/// A set of event flags for thread synchronization.
39///
40/// Event groups contain multiple event bits (typically 24 bits) that can be
41/// manipulated independently. Threads can wait for specific combinations of bits
42/// to be set, making them ideal for complex synchronization scenarios.
43///
44/// # Examples
45///
46/// ## Basic event signaling
47///
48/// ```ignore
49/// use osal_rs::os::{EventGroup, EventGroupFn};
50/// use core::time::Duration;
51/// 
52/// const EVENT_A: u32 = 0b0001;
53/// const EVENT_B: u32 = 0b0010;
54/// const EVENT_C: u32 = 0b0100;
55/// 
56/// let events = EventGroup::new().unwrap();
57/// 
58/// // Set event A
59/// events.set(EVENT_A);
60/// 
61/// // Check if event A is set
62/// let current = events.get();
63/// if current & EVENT_A != 0 {
64///     println!("Event A is set");
65/// }
66/// 
67/// // Clear event A
68/// events.clear(EVENT_A);
69/// ```
70///
71/// ## Waiting for multiple events
72///
73/// ```ignore
74/// use osal_rs::os::{EventGroup, EventGroupFn, Thread};
75/// use alloc::sync::Arc;
76/// use core::time::Duration;
77/// 
78/// const READY: u32 = 0b0001;
79/// const DATA_AVAILABLE: u32 = 0b0010;
80/// const STOP: u32 = 0b0100;
81/// 
82/// let events = Arc::new(EventGroup::new().unwrap());
83/// let events_clone = events.clone();
84/// 
85/// // Worker thread waits for events
86/// let worker = Thread::new("worker", 2048, 5, move || {
87///     loop {
88///         // Wait for either READY or STOP
89///         let bits = events_clone.wait_with_to_tick(
90///             READY | STOP,
91///             Duration::from_secs(1)
92///         );
93///         
94///         if bits & STOP != 0 {
95///             println!("Stopping...");
96///             break;
97///         }
98///         
99///         if bits & READY != 0 {
100///             println!("Ready to work!");
101///         }
102///     }
103/// }).unwrap();
104/// 
105/// worker.start().unwrap();
106/// 
107/// // Signal events
108/// events.set(READY);
109/// Duration::from_secs(2).sleep();
110/// events.set(STOP);
111/// ```
112///
113/// ## State machine synchronization
114///
115/// ```ignore
116/// use osal_rs::os::{EventGroup, EventGroupFn};
117/// use core::time::Duration;
118/// 
119/// const INIT_COMPLETE: u32 = 1 << 0;
120/// const CONFIG_LOADED: u32 = 1 << 1;
121/// const NETWORK_UP: u32 = 1 << 2;
122/// const READY_TO_RUN: u32 = INIT_COMPLETE | CONFIG_LOADED | NETWORK_UP;
123/// 
124/// let state = EventGroup::new().unwrap();
125/// 
126/// // Different subsystems set their bits
127/// state.set(INIT_COMPLETE);
128/// state.set(CONFIG_LOADED);
129/// state.set(NETWORK_UP);
130/// 
131/// // Wait for all systems to be ready
132/// let current = state.wait_with_to_tick(READY_TO_RUN, Duration::from_secs(5));
133/// 
134/// if (current & READY_TO_RUN) == READY_TO_RUN {
135///     println!("All systems ready!");
136/// }
137/// ```
138///
139/// ## ISR to thread signaling
140///
141/// ```ignore
142/// use osal_rs::os::{EventGroup, EventGroupFn, Thread};
143/// use alloc::sync::Arc;
144/// 
145/// const IRQ_EVENT: u32 = 1 << 0;
146/// 
147/// let events = Arc::new(EventGroup::new().unwrap());
148/// let events_isr = events.clone();
149/// 
150/// // In interrupt handler:
151/// // events_isr.set_from_isr(IRQ_EVENT).ok();
152/// 
153/// // Handler thread
154/// let handler = Thread::new("handler", 2048, 5, move || {
155///     loop {
156///         let bits = events.wait(IRQ_EVENT, 1000);
157///         if bits & IRQ_EVENT != 0 {
158///             println!("Handling interrupt event");
159///             events.clear(IRQ_EVENT);
160///         }
161///     }
162/// }).unwrap();
163/// ```
164pub struct EventGroup (EventGroupHandle);
165
166unsafe impl Send for EventGroup {}
167unsafe impl Sync for EventGroup {}
168
169impl EventGroup {
170
171    /// Maximum usable event bits mask.
172    /// FreeRTOS reserves the top 8 bits for internal use:
173    /// - For u32 (TickType): 0x00FFFFFF (24 bits usable)
174    /// - For u64 (TickType): 0x00FFFFFFFFFFFFFF (56 bits usable)
175    pub const MAX_MASK: EventBits = EventBits::MAX >> 8;
176
177    /// Waits for specified event bits to be set with a timeout in ticks.
178    /// This is a convenience method that converts a `ToTick` type to ticks and calls `wait`.
179    ///
180    /// # Examples
181    ////
182    /// ```ignore
183    /// use osal_rs::os::{EventGroup, EventGroupFn};
184    /// use core::time::Duration;
185    /// let events = EventGroup::new().unwrap();
186    /// let bits = events.wait_with_to_tick(0b0001, Duration::from_secs(1));
187    /// ```
188    pub fn wait_with_to_tick(&self, mask: EventBits, timeout_ticks: impl ToTick) -> EventBits {
189        self.wait(mask, timeout_ticks.to_ticks())
190    }
191}
192
193
194impl EventGroup {
195    /// Creates a new event group.
196    ///
197    /// # Returns
198    ///
199    /// * `Ok(Self)` - Successfully created event group
200    /// * `Err(Error)` - Creation failed (out of memory, etc.)
201    ///
202    /// # Examples
203    ///
204    /// ```ignore
205    /// use osal_rs::os::{EventGroup, EventGroupFn};
206    /// 
207    /// let events = EventGroup::new().unwrap();
208    /// ```
209    pub fn new() -> Result<Self> {
210        let handle = unsafe { xEventGroupCreate() };
211        if handle.is_null() {
212            Err(Error::OutOfMemory)
213        } else {
214            Ok(Self (handle))
215        }
216    }
217
218}
219impl EventGroupFn for EventGroup {
220
221    /// Sets specified event bits.
222    /// 
223    /// This function sets (raises) the specified event bits in the event group.
224    /// Any threads waiting for these bits may be unblocked.
225    /// 
226    /// # Arguments
227    /// 
228    /// * `bits` - The event bits to set (bitwise OR to set multiple bits)
229    /// 
230    /// # Returns
231    /// 
232    /// The event bits value after the set operation.
233    /// 
234    /// # Examples
235    /// 
236    /// ```ignore
237    /// use osal_rs::os::{EventGroup, EventGroupFn};
238    /// 
239    /// let events = EventGroup::new().unwrap();
240    /// events.set(0b0001);  // Set bit 0
241    /// events.set(0b0110);  // Set bits 1 and 2
242    /// ```
243    fn set(&self, bits: EventBits) -> EventBits {
244        unsafe { xEventGroupSetBits(self.0, bits) }
245    }
246
247    /// Sets specified event bits from an interrupt service routine (ISR).
248    /// 
249    /// This is the ISR-safe version of `set()`. It can be called from interrupt
250    /// context and will trigger a context switch if a higher priority thread
251    /// is unblocked by the bit setting.
252    /// 
253    /// # Arguments
254    /// 
255    /// * `bits` - The event bits to set
256    /// 
257    /// # Returns
258    /// 
259    /// * `Ok(())` - Bits were successfully set
260    /// * `Err(Error::QueueFull)` - Operation failed
261    /// 
262    /// # Examples
263    /// 
264    /// ```ignore
265    /// // In interrupt handler
266    /// use osal_rs::os::{EventGroup, EventGroupFn};
267    /// 
268    /// fn interrupt_handler(events: &EventGroup) {
269    ///     events.set_from_isr(0b0001).ok();
270    /// }
271    /// ```
272    fn set_from_isr(&self, bits: EventBits) -> Result<()> {
273
274        let mut higher_priority_task_woken: BaseType = pdFALSE;
275
276        let ret = unsafe { xEventGroupSetBitsFromISR(self.0, bits, &mut higher_priority_task_woken) };
277        if ret != pdFAIL {
278
279            System::yield_from_isr(higher_priority_task_woken);
280            
281            Ok(())
282        } else {
283            Err(Error::QueueFull)
284        }
285    }
286
287    /// Gets the current value of event bits.
288    /// 
289    /// Returns the current state of all event bits in the event group.
290    /// This is a non-blocking operation.
291    /// 
292    /// # Returns
293    /// 
294    /// The current event bits value.
295    /// 
296    /// # Examples
297    /// 
298    /// ```ignore
299    /// use osal_rs::os::{EventGroup, EventGroupFn};
300    /// 
301    /// let events = EventGroup::new().unwrap();
302    /// events.set(0b0101);
303    /// let current = events.get();
304    /// assert_eq!(current & 0b0101, 0b0101);
305    /// ```
306    fn get(&self) -> EventBits {
307        xEventGroupGetBits!(self.0) 
308    }
309
310    /// Gets the current value of event bits from an ISR.
311    /// 
312    /// This is the ISR-safe version of `get()`. It can be called from
313    /// interrupt context to read the current event bits.
314    /// 
315    /// # Returns
316    /// 
317    /// The current event bits value.
318    /// 
319    /// # Examples
320    /// 
321    /// ```ignore
322    /// // In interrupt handler
323    /// use osal_rs::os::{EventGroup, EventGroupFn};
324    /// 
325    /// fn interrupt_handler(events: &EventGroup) {
326    ///     let current = events.get_from_isr();
327    /// }
328    /// ```
329    fn get_from_isr(&self) -> EventBits {
330        unsafe { xEventGroupGetBitsFromISR(self.0) }
331    }
332
333
334    /// Clears specified event bits.
335    /// 
336    /// This function clears (lowers) the specified event bits in the event group.
337    /// 
338    /// # Arguments
339    /// 
340    /// * `bits` - The event bits to clear (bitwise OR to clear multiple bits)
341    /// 
342    /// # Returns
343    /// 
344    /// The event bits value before the clear operation.
345    /// 
346    /// # Examples
347    /// 
348    /// ```ignore
349    /// use osal_rs::os::{EventGroup, EventGroupFn};
350    /// 
351    /// let events = EventGroup::new().unwrap();
352    /// events.set(0b1111);
353    /// events.clear(0b0011);  // Clear bits 0 and 1
354    /// let current = events.get();
355    /// assert_eq!(current & 0b1111, 0b1100);
356    /// ```
357    fn clear(&self, bits: EventBits) -> EventBits {
358        unsafe { xEventGroupClearBits(self.0, bits) }
359    }
360
361    /// Clears specified event bits from an ISR.
362    /// 
363    /// This is the ISR-safe version of `clear()`. It can be called from
364    /// interrupt context to clear event bits.
365    /// 
366    /// # Arguments
367    /// 
368    /// * `bits` - The event bits to clear
369    /// 
370    /// # Returns
371    /// 
372    /// * `Ok(())` - Bits were successfully cleared
373    /// * `Err(Error::QueueFull)` - Operation failed
374    /// 
375    /// # Examples
376    /// 
377    /// ```ignore
378    /// // In interrupt handler
379    /// use osal_rs::os::{EventGroup, EventGroupFn};
380    /// 
381    /// fn interrupt_handler(events: &EventGroup) {
382    ///     events.clear_from_isr(0b0001).ok();
383    /// }
384    /// ```
385    fn clear_from_isr(&self, bits: EventBits) -> Result<()> {
386        let ret = unsafe { xEventGroupClearBitsFromISR(self.0, bits) };
387        if ret != pdFAIL {
388            Ok(())
389        } else {
390            Err(Error::QueueFull)
391        }
392    }
393
394    /// Waits for specified event bits to be set.
395    /// 
396    /// Blocks the calling thread until any of the specified bits are set,
397    /// or until the timeout expires. The bits are not cleared automatically.
398    /// 
399    /// # Arguments
400    /// 
401    /// * `mask` - The event bits to wait for (bitwise OR for multiple bits)
402    /// * `timeout_ticks` - Maximum time to wait in system ticks (0 = no wait, MAX = wait forever)
403    /// 
404    /// # Returns
405    /// 
406    /// The event bits value when the function returns. Check if the desired
407    /// bits are set to determine if the wait succeeded or timed out.
408    /// 
409    /// # Examples
410    /// 
411    /// ```ignore
412    /// use osal_rs::os::{EventGroup, EventGroupFn};
413    /// 
414    /// let events = EventGroup::new().unwrap();
415    /// 
416    /// // Wait for bit 0 or bit 1, timeout after 1000 ticks
417    /// let result = events.wait(0b0011, 1000);
418    /// if result & 0b0011 != 0 {
419    ///     println!("At least one bit was set");
420    /// }
421    /// ```
422    fn wait(&self, mask: EventBits, timeout_ticks: TickType) -> EventBits {
423        unsafe {
424            crate::freertos::ffi::xEventGroupWaitBits(
425                self.0,
426                mask,
427                pdFALSE, 
428                pdFALSE, 
429                timeout_ticks,
430            )
431        }
432    }
433
434    /// Deletes the event group and frees its resources.
435    /// 
436    /// This function destroys the event group and releases any memory
437    /// allocated for it. After calling this, the event group should not
438    /// be used. The handle is set to null after deletion.
439    /// 
440    /// # Safety
441    /// 
442    /// Ensure no threads are waiting on this event group before deleting it.
443    /// 
444    /// # Examples
445    /// 
446    /// ```ignore
447    /// use osal_rs::os::{EventGroup, EventGroupFn};
448    /// 
449    /// let mut events = EventGroup::new().unwrap();
450    /// // Use the event group...
451    /// events.delete();
452    /// ```
453    fn delete(&mut self) {
454        unsafe {
455            vEventGroupDelete(self.0);
456            self.0 = null_mut();
457        }
458    }
459}
460
461/// Automatically deletes the event group when it goes out of scope.
462/// 
463/// This ensures proper cleanup of FreeRTOS resources.
464impl Drop for EventGroup {
465    fn drop(&mut self) {
466        if self.0.is_null() {
467            return;
468        }
469        self.delete();
470    }
471}
472
473/// Allows dereferencing to the underlying FreeRTOS event group handle.
474impl Deref for EventGroup {
475    type Target = EventGroupHandle;
476
477    fn deref(&self) -> &Self::Target {
478        &self.0
479    }
480}
481
482/// Formats the event group for debugging purposes.
483impl Debug for EventGroup {
484    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
485        write!(f, "EventGroup {{ handle: {:?} }}", self.0)
486    }
487}
488
489/// Formats the event group for display purposes.
490impl Display for EventGroup {
491    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
492        write!(f, "EventGroup {{ handle: {:?} }}", self.0)
493    }
494}