Skip to content

Commit b1ca434

Browse files
async_context_threadsafe_background: fix incorrect mutex assertion in cross-core execute_sync() (#2528)
* async_context_threadsafe_background: fix incorrect mutex assertion in cross-core execute_sync() In multicore configurations, `async_context_threadsafe_background_execute_sync()` contained an overly strict assertion used during cross-core calls: ```c hard_assert(!recursive_mutex_enter_count(&self->lock_mutex)); ``` This check fails whenever the `lock_mutex` is held — regardless of *who* owns it — even in valid situations where the async core is processing background work. The assertion does **not check ownership**, only that the `enter_count` is zero, which leads to false-positive failures on valid cross-core calls. This patch replaces the enter-count check with a core-aware assertion: ```c hard_assert(self->lock_mutex.owner != calling_core); ``` This ensures the current core does not recursively hold the mutex, preventing deadlocks while allowing valid usage where the *other* core owns the lock. The patch ensures that both `get_core_num()` and `hard_assert()` remain inlined as in the original implementation, preserving the performance characteristics under `-Os` and `RelWithDebInfo` builds. Fixes #2527 Signed-off-by: Goran Mišković <[email protected]> * fix indents * Update async_context_threadsafe_background.c Use pre-existing mutex owner method; add a comment * oops * typo * Update async_context_threadsafe_background.c --------- Signed-off-by: Goran Mišković <[email protected]> Co-authored-by: Graham Sanderson <[email protected]>
1 parent a24bc13 commit b1ca434

File tree

1 file changed

+9
-2
lines changed

1 file changed

+9
-2
lines changed

src/rp2_common/pico_async_context/async_context_threadsafe_background.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,15 @@ static void lock_release(async_context_threadsafe_background_t *self) {
138138
uint32_t async_context_threadsafe_background_execute_sync(async_context_t *self_base, uint32_t (*func)(void *param), void *param) {
139139
async_context_threadsafe_background_t *self = (async_context_threadsafe_background_t*)self_base;
140140
#if ASYNC_CONTEXT_THREADSAFE_BACKGROUND_MULTI_CORE
141-
if (self_base->core_num != get_core_num()) {
142-
hard_assert(!recursive_mutex_enter_count(&self->lock_mutex));
141+
const uint calling_core = get_core_num();
142+
if (self_base->core_num != calling_core) {
143+
// This core must not hold the lock mutex, or this cross-core execute would deadlock. It is fine if the other core holds it.
144+
// It would be a strange set of circumstances for it to do so, hence the hard_assert
145+
146+
// Note that this read of the owner is not synchronized with the other core; however we only
147+
// care about it being set to `calling_core`, which the other core will not transition
148+
// it either from or to.
149+
hard_assert(recursive_mutex_owner(&self->lock_mutex) != calling_core);
143150
sync_func_call_t call = {0};
144151
call.worker.do_work = handle_sync_func_call;
145152
call.func = func;

0 commit comments

Comments
 (0)