Repository navigation
k_mutex: K_MUTEX_DEFINE aborts on use; unlocking an unlocked mutex returns -EPERM (follow-up to #55) #78
Copy link
Copy link
Open
Labels
zephyr-parityDivergence from upstream Zephyr API/semanticsDivergence from upstream Zephyr API/semantics
Description
Land after #55 (recursive-mutex rewrite), which touches the same code and also removes the unimplemented CONFIG_ZKERNEL_MUTEX_DEBUG ordering option. #76 is shared with #76 and #77; this issue owns K_MUTEX_DEFINE.
K_TIMER_DEFINE never fires; K_MUTEX_DEFINE / K_MSGQ_DEFINE abort on first use
medium · parity · review ID K7
- Where:
kernel.h:438-443(the timer handle is NULL),:265({0}),:288-295(no queue handle).k_timer.c:95-98/k_timer_linux.c:181-184log "Timer not initialized" and return.k_mutex.c:63andk_msgq.c:40operate on a NULL handle. - Defect: Upstream these macros produce ready-to-use objects. In Boreas they all need a runtime init call, and nothing documents that. The timer case fails silently.
- Failure scenario:
K_TIMER_DEFINE(t, cb, NULL); k_timer_start(&t, ...)does nothing apart from one log line.K_MUTEX_DEFINE(m); k_mutex_lock(&m, ...)aborts.K_MSGQ_DEFINE(q, ...); k_msgq_put(&q, ...)aborts.- The README snippet
k_msgq_init(&my_q, ...); // or use K_MSGQ_DEFINE for staticreads as if the macro alone were enough.
- Evidence: reproduced.
test_rr_timer_define_is_readyfails withExpected 1 Was 0. K_TIMER_DEFINE timer never fired.test_rr_mutex_define_is_readystops withAssertion failed: (( pxQueue )), function xQueueSemaphoreTake.test_rr_msgq_define_is_readystops withAssertion failed: (pxQueue), function xQueueGenericSend.
- Upstream reference:
include/zephyr/kernel.hK_TIMER_DEFINE,K_MUTEX_DEFINE,K_MSGQ_DEFINE: static initializers, usable with no init call. - Suggested fix: Initialize lazily on first use under a spinlock:
xSemaphoreCreateMutexStatic,xQueueCreateStatic, andesp_timer_create(silicon) or the sentinel handle (linux). Alternatively, move k_mutex / k_msgq to the owned-state design. At minimum, add an@noteon each macro. - Related: K_SEM_DEFINE is not a static initializer (requires runtime k_sem_init) #26 (fixed
K_SEM_DEFINEonly) and k_work: missing upstream APIs (K_WORK_INIT, K_WORK_DELAYABLE_DEFINE, busy_get, drain/stop, queue_start signature, system-WQ Kconfig) #25 (coversK_WORK_DELAYABLE_DEFINE). New:K_TIMER_DEFINE,K_MUTEX_DEFINE,K_MSGQ_DEFINE.
k_mutex_unlock on an unlocked mutex returns -EPERM, not -EINVAL
low · parity · review ID K20
- Where:
k_mutex.c:86-88. The declaration atkernel.h:269has no doc. - Evidence: reproduced.
test_rr_mutex_unlock_unlocked_einvalfails withExpected -22 Was -1. - Upstream reference:
kernel/mutex.cz_impl_k_mutex_unlock(CHECKIF(mutex->owner == NULL) return -EINVAL); the kernel.h doc onmainlists "-EINVAL The mutex is not locked". - Suggested fix: Check
owner == NULLfirst and return-EINVAL. Document the ISR-EWOULDBLOCKbehavior on the declaration.
Repro tests (Unity, ESP-IDF linux host target; assert upstream behavior, fail on current main)
/* ---------------------------------------------------------------- */
/* F-MUTEX-EINVAL: unlocking an unlocked mutex is -EINVAL upstream. */
/* ---------------------------------------------------------------- */
static void test_rr_mutex_unlock_unlocked_einval(void)
{
struct k_mutex m;
k_mutex_init(&m);
TEST_ASSERT_EQUAL_INT(-EINVAL, k_mutex_unlock(&m));
}
/* ---------------------------------------------------------------- */
/* F-DEFINE: K_*_DEFINE objects are ready to use upstream. */
/* ---------------------------------------------------------------- */
static volatile int define_timer_count;
static void define_timer_expiry(struct k_timer *t)
{
(void)t;
define_timer_count++;
}
K_TIMER_DEFINE(rr_defined_timer, define_timer_expiry, NULL);
static void test_rr_timer_define_is_ready(void)
{
define_timer_count = 0;
k_timer_start(&rr_defined_timer, K_MSEC(10), K_NO_WAIT); /* no k_timer_init: upstream-legal */
k_msleep(50);
int count = define_timer_count;
k_timer_stop(&rr_defined_timer);
TEST_ASSERT_EQUAL_INT_MESSAGE(1, count, "K_TIMER_DEFINE timer never fired");
}
/* The next two abort the process (FreeRTOS configASSERT on a NULL
* handle), so they only run when named explicitly in RR_TESTS. */
K_MUTEX_DEFINE(rr_defined_mutex);
static void test_rr_mutex_define_is_ready(void)
{
TEST_ASSERT_EQUAL_INT(0, k_mutex_lock(&rr_defined_mutex, K_NO_WAIT));
TEST_ASSERT_EQUAL_INT(0, k_mutex_unlock(&rr_defined_mutex));
}
K_MSGQ_DEFINE(rr_defined_msgq, sizeof(int), 2, 4);
static void test_rr_msgq_define_is_ready(void)
{
int v = 7;
TEST_ASSERT_EQUAL_INT(0, k_msgq_put(&rr_defined_msgq, &v, K_NO_WAIT));
}
#include <stdlib.h>
/* Review scaffolding: RR_TESTS=<substring> runs only matching tests (host). */
#define RR_RUN(fn) \
do { \
const char *f = getenv("RR_TESTS"); \
if (f == NULL || strstr(#fn, f) || strstr(f, #fn)) { \
RUN_TEST(fn); \
} \
} while (0)
#define RR_RUN_EXPLICIT(fn) \
do { \
const char *f = getenv("RR_TESTS"); \
if (f != NULL && strstr(f, #fn)) { \
RUN_TEST(fn); \
} \
} while (0)Found in a whole-codebase review of main @ d6ac19e (2026-10-29). Upstream compared: Zephyr v3.7.0-507 and main as of 2026-10-29. Evidence: reproduced = failing test written; traced = step-by-step trace with line numbers. Repro tests named here were written during the review and are not in the repo yet; they land with the fixes.
Metadata
Metadata
Assignees
Labels
zephyr-parityDivergence from upstream Zephyr API/semanticsDivergence from upstream Zephyr API/semantics
Activity