Skip to content

Conversation

jerome-pouiller
Copy link
Contributor

In osEventFlagsWait(), if an event raises after the call to k_event_wait() and before the call to k_event_clear(), we may clear it while it is not going to be report to the caller.

Reported-by: Rahul Gurram [email protected]

@zephyrbot zephyrbot added area: CMSIS API Layer CMSIS-RTOS API Layer size: XS A PR changing only a single line of code area: Portability Standard compliant code, toolchain abstraction labels Oct 7, 2025
@zephyrbot zephyrbot requested a review from nashif October 7, 2025 14:22
@jerome-pouiller
Copy link
Contributor Author

Maybe we should even replace k_event_wait() by k_event_wait_safe()?

@jerome-pouiller
Copy link
Contributor Author

Cc: @ckhardin

@ckhardin
Copy link
Contributor

ckhardin commented Oct 7, 2025

probably looking for something maybe closer to this

diff --git a/subsys/portability/cmsis_rtos_v2/event_flags.c b/subsys/portability/cmsis_rtos_v2/event_flags.c
index fc3d50f067d..86dbc2846cd 100644
--- a/subsys/portability/cmsis_rtos_v2/event_flags.c
+++ b/subsys/portability/cmsis_rtos_v2/event_flags.c
@@ -79,7 +79,7 @@ uint32_t osEventFlagsClear(osEventFlagsId_t ef_id, uint32_t flags)
        }
 
        rv = k_event_test(&events->z_event, 0xFFFFFFFF);
-       k_event_clear(&events->z_event, flags);
+       k_event_clear(&events->z_event, flags & rv);
 
        return rv;
 }
@@ -115,13 +115,21 @@ uint32_t osEventFlagsWait(osEventFlagsId_t ef_id, uint32_t flags, uint32_t optio
        }
 
        if ((options & osFlagsWaitAll) != 0) {
-               rv = k_event_wait_all(&events->z_event, flags, false, event_timeout);
+               if ((options & osFlagsNoClear) != 0) {
+                       rv = k_event_wait_all(&events->z_event, flags,
+                                             false, event_timeout);
+               } else {
+                       rv = k_event_wait_all_safe(&events->z_event, flags,
+                                                  false, event_timeout);
+               }
        } else {
-               rv = k_event_wait(&events->z_event, flags, false, event_timeout);
-       }
-
-       if ((options & osFlagsNoClear) == 0) {
-               k_event_clear(&events->z_event, flags);
+               if ((options & osFlagsNoClear) != 0) {
+                       rv = k_event_wait(&events->z_event, flags,
+                                         false, event_timeout);
+               } else {
+                       rv = k_event_wait_safe(&events->z_event, flags,
+                                              false, event_timeout);
+               }
        }
 
        if (rv != 0U) {

@jerome-pouiller jerome-pouiller force-pushed the osEventFlagsWait-race branch 2 times, most recently from 7f402d2 to 387b829 Compare October 9, 2025 08:00
@jerome-pouiller
Copy link
Contributor Author

jerome-pouiller commented Oct 9, 2025

v2:

  • also fix osEventFlagsClear()
  • take advantage of *_safe() variant

@jerome-pouiller jerome-pouiller marked this pull request as draft October 10, 2025 07:47
@jerome-pouiller jerome-pouiller marked this pull request as ready for review October 10, 2025 08:24
@zephyrbot zephyrbot added the area: Tests Issues related to a particular existing or missing test label Oct 10, 2025
@jerome-pouiller
Copy link
Contributor Author

v3:

  • Also change osEventFlagsSet()
  • Fix testsuite

@rgundi, I believe you are the author of the comment in test_event_flags_signalled. Can I get your feedback about the commit 4/4?

In osEventFlagsWait(), if an event raises after the call to k_event_wait()
and before the call to k_event_clear(), we may clear it while it is not
going to be report to the caller.

Reported-by: Rahul Gurram <[email protected]>
Signed-off-by: Jérôme Pouiller <[email protected]>
The CMSIS-RTOS specification says "The function returns the event flags
before clearing".

In the original code, if another thread set an event between k_event_test()
and k_event_clear(), there was a risk the function clear a flag without
reporting it to the caller:

        T1                                   T2
  k_event_test(..) == 0                      ...
        ...                             k_event_post(.. 1)
  k_event_clear(.. 1)                        ...
  return 0 (while event 1 has been cleared)

Signed-off-by: Jérôme Pouiller <[email protected]>
@jerome-pouiller jerome-pouiller removed the size: XS A PR changing only a single line of code label Oct 10, 2025
The CMSIS-RTOS specification says "The function returns the event flags
stored in the event control block".

In the original code, osEventFlagsSet() called k_event_post() and then
k_event_test(). It worked mostly fine if the thread has higher priority
than the waiter thread. In the opposite case, the event was not reported
to the user.

With the last changes, the waiter thread use k_event_wait_safe(). So, the
event is posted and consumed in an atomic way. So, the issue above happen
even if the waiter thread has a lower priority then the poster.

This patch fixes the both cases.

Signed-off-by: Jérôme Pouiller <[email protected]>
Until now, the main thread preempted thread2 between k_event_post() and
k_event_test(). Then main thread consumed the event before it was tested by
thread2.

This behavior was not correct since the caller can't know the event is in
fact consumed (the CMSIS-RTOS specification says osEventFlagsSet() "returns
the event flags stored in the event")

This behavior is now fixed. So we can fix the test.

Signed-off-by: Jérôme Pouiller <[email protected]>
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: CMSIS API Layer CMSIS-RTOS API Layer area: Portability Standard compliant code, toolchain abstraction area: Tests Issues related to a particular existing or missing test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants