Skip to content

Commit d873852

Browse files
committed
core/thread_flags_group: use portable implementation
The previous implementation relied on `thread_flag_set()` to defer the context switch when called with IRQs disabled until `irq_restore()` is called. This however can only be the case when `thread_yield_higher` triggers an IRQ and performs the context switch within the ISR. This allowed the previous implementation to continue calling `thread_flag_set()` for the remaining group members. This however is an implementation detail that is not part of the API contract. Platforms that do not have a service request IRQ may have to use other means for context switching that do not get deferred until an `irq_restore()` is called. In that case, the first call to `thread_flags_set()` even with IRQs disabled may directly trigger a context switch to the unblocked thread, even if other group members would also be unblocked and have a higher priority. This changes the implementation to manually set the flags and update the thread status without yielding and keep track whether any thread has been awoken. Only once the states of all threads have been updated, the adapted implementation will now call `thread_yield()` (unless no thread was awoken).
1 parent 39bf317 commit d873852

File tree

1 file changed

+32
-1
lines changed

1 file changed

+32
-1
lines changed

core/thread_flags_group.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,58 @@
1818
* @}
1919
*/
2020

21+
#include <stdbool.h>
22+
2123
#include "bitarithm.h"
2224
#include "irq.h"
2325
#include "thread.h"
2426
#include "thread_flags_group.h"
2527

28+
#define ENABLE_DEBUG 0
29+
#include "debug.h"
30+
2631
void thread_flags_group_set(thread_flags_group_t *group, thread_flags_t mask)
2732
{
2833
/* Interrupts must be disabled because the threads are not ordered by
2934
* priority. */
3035
unsigned irq_state = irq_disable();
3136

37+
DEBUG("thread_flags_group_set(%p, %x):\n", (void *)group, (unsigned)mask);
38+
DEBUG("| TID | Flags | Status (old) | Status (new) |\n");
39+
40+
bool yield = false;
3241
for (kernel_pid_t i = 0; i < (kernel_pid_t)ARRAY_SIZE(group->members); i++) {
3342
unsigned pid_block = group->members[i];
3443
kernel_pid_t const pid_base = i * UINT_WIDTH;
3544
uint8_t pid_offs = 0;
3645

3746
while (pid_block) {
3847
pid_block = bitarithm_test_and_clear(pid_block, &pid_offs);
39-
thread_flags_set(thread_get(pid_base + pid_offs), mask);
48+
kernel_pid_t target_pid = pid_base + pid_offs;
49+
thread_t *target = thread_get(target_pid);
50+
if (!target) {
51+
DEBUG("| %02u | n/a | n/a (dead) | n/a (dead) |\n",
52+
target_pid);
53+
continue;
54+
}
55+
thread_status_t old_status = target->status;
56+
bool awoken = thread_flags_set_internal(target, mask);
57+
thread_status_t new_status = target->status;
58+
/* NOTE: wait_data is shared by thread_flags with other
59+
* mechanisms and may e.g. be a pointer to an `msg_t`.
60+
* Some interpretation of the output is needed by the
61+
* user of the debug output. */
62+
thread_flags_t wait_data = (uint16_t)(uintptr_t)target->wait_data;
63+
DEBUG("| %02u | %04x | %12s | %12s |\n",
64+
(unsigned)target_pid, (unsigned)wait_data,
65+
thread_state_to_string(old_status),
66+
thread_state_to_string(new_status));
67+
yield = yield || awoken;
4068
}
4169
}
4270

4371
irq_restore(irq_state);
72+
if (yield) {
73+
thread_yield_higher();
74+
}
4475
}

0 commit comments

Comments
 (0)