Skip to content

k_mutex: K_MUTEX_DEFINE aborts on use; unlocking an unlocked mutex returns -EPERM (follow-up to #55) #78

Open

Description

@swoisz

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-184 log "Timer not initialized" and return. k_mutex.c:63 and k_msgq.c:40 operate 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 static reads as if the macro alone were enough.
  • Evidence: reproduced.
    • test_rr_timer_define_is_ready fails with Expected 1 Was 0. K_TIMER_DEFINE timer never fired.
    • test_rr_mutex_define_is_ready stops with Assertion failed: (( pxQueue )), function xQueueSemaphoreTake.
    • test_rr_msgq_define_is_ready stops with Assertion failed: (pxQueue), function xQueueGenericSend.
  • Upstream reference: include/zephyr/kernel.h K_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, and esp_timer_create (silicon) or the sentinel handle (linux). Alternatively, move k_mutex / k_msgq to the owned-state design. At minimum, add an @note on each macro.
  • Related: K_SEM_DEFINE is not a static initializer (requires runtime k_sem_init) #26 (fixed K_SEM_DEFINE only) and k_work: missing upstream APIs (K_WORK_INIT, K_WORK_DELAYABLE_DEFINE, busy_get, drain/stop, queue_start signature, system-WQ Kconfig) #25 (covers K_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 at kernel.h:269 has no doc.
  • Evidence: reproduced. test_rr_mutex_unlock_unlocked_einval fails with Expected -22 Was -1.
  • Upstream reference: kernel/mutex.c z_impl_k_mutex_unlock (CHECKIF(mutex->owner == NULL) return -EINVAL); the kernel.h doc on main lists "-EINVAL The mutex is not locked".
  • Suggested fix: Check owner == NULL first and return -EINVAL. Document the ISR -EWOULDBLOCK behavior 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.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    zephyr-parityDivergence from upstream Zephyr API/semantics

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions