From dcda013e002d199d5a25105ab4b3ab0feb63e3eb Mon Sep 17 00:00:00 2001 From: Swayam Nayak <154440440+swayam-2003@users.noreply.github.com> Date: Sat, 29 Aug 2026 22:13:39 +0530 Subject: [PATCH] Return EOS_KERN_NO_MEMORY when queue waiter table is full Message queues previously blocked callers even when the send/recv waiter tables were full, which could strand tasks with no wakeup path. Match the mutex and semaphore behavior by failing immediately with EOS_KERN_NO_MEMORY. Signed-off-by: Swayam Nayak <154440440+swayam-2003@users.noreply.github.com> --- CHANGELOG.md | 1 + kernel/src/ipc.c | 18 ++++++++++----- tests/test_kernel.c | 53 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cea9107..d22be4a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] ### Fixed +- **`eos_queue_send` / `eos_queue_receive`:** A full send/recv waiter table now returns `EOS_KERN_NO_MEMORY` instead of blocking a task that can never be woken, matching mutex and semaphore behavior. - **`eos_queue_create`:** Size check now uses division so `item_size * capacity` cannot wrap `size_t` on 32-bit targets and overflow the 1024-byte queue store. - **`eos_sem_create`:** Reject `initial > max` and `max` values that do not fit in `int32_t`, so the counting-semaphore invariant cannot be created already broken. - **`eos_mutex_lock`:** Recursive lock returns `EOS_KERN_FULL` at `uint8_t` saturation instead of wrapping `rec_count` to 0 and leaving the mutex stuck. diff --git a/kernel/src/ipc.c b/kernel/src/ipc.c index 81d243c3..d74fc801 100644 --- a/kernel/src/ipc.c +++ b/kernel/src/ipc.c @@ -85,10 +85,13 @@ int eos_queue_send(eos_queue_handle_t h, const void *item, uint32_t timeout_ms) return EOS_KERN_FULL; } - uint8_t caller = (uint8_t)eos_task_get_current(); - if (g_q[h].send_waiter_count < Q_MAX_WAITERS) { - g_q[h].send_waiters[g_q[h].send_waiter_count++] = caller; + if (g_q[h].send_waiter_count >= Q_MAX_WAITERS) { + eos_port_exit_critical(crit); + return EOS_KERN_NO_MEMORY; } + + uint8_t caller = (uint8_t)eos_task_get_current(); + g_q[h].send_waiters[g_q[h].send_waiter_count++] = caller; eos_task_block_with_timeout(caller, timeout_ms); eos_port_exit_critical(crit); eos_port_yield(); @@ -146,10 +149,13 @@ int eos_queue_receive(eos_queue_handle_t h, void *item, uint32_t timeout_ms) return EOS_KERN_EMPTY; } - uint8_t caller = (uint8_t)eos_task_get_current(); - if (g_q[h].recv_waiter_count < Q_MAX_WAITERS) { - g_q[h].recv_waiters[g_q[h].recv_waiter_count++] = caller; + if (g_q[h].recv_waiter_count >= Q_MAX_WAITERS) { + eos_port_exit_critical(crit); + return EOS_KERN_NO_MEMORY; } + + uint8_t caller = (uint8_t)eos_task_get_current(); + g_q[h].recv_waiters[g_q[h].recv_waiter_count++] = caller; eos_task_block_with_timeout(caller, timeout_ms); eos_port_exit_critical(crit); eos_port_yield(); diff --git a/tests/test_kernel.c b/tests/test_kernel.c index 4eaa1f57..e4fb4ac4 100644 --- a/tests/test_kernel.c +++ b/tests/test_kernel.c @@ -138,6 +138,55 @@ static void test_queue_full(void) { printf("[PASS] queue full/peek\n"); } +/* ---- Queue send waiter overflow ---- */ + +static eos_queue_handle_t q7_q; +static eos_task_handle_t q7_waiters[EOS_MAX_TASKS]; +static int q7_next, q7_total, q7_overflow_reported, q7_enqueued; +static void (*q7_yield_hook)(void); + +static void q7_hook(void) +{ + if (q7_next >= q7_total) return; + eos_task_handle_t w = q7_waiters[q7_next++]; + eos_task_set_current_internal(w); + int item = 0; + int rc = eos_queue_send(q7_q, &item, EOS_WAIT_FOREVER); + if (rc == EOS_KERN_NO_MEMORY) q7_overflow_reported = 1; + else q7_enqueued++; +} + +static void test_queue_send_waiter_overflow(void) { + eos_kernel_init(); + assert(eos_queue_create(&q7_q, sizeof(int), 1) == EOS_KERN_OK); + + int filler = 42; + assert(eos_queue_send(q7_q, &filler, EOS_NO_WAIT) == EOS_KERN_OK); + + q7_total = 0; + for (int i = 0; i < EOS_MAX_TASKS - 2; i++) { + char name[8]; + snprintf(name, sizeof(name), "qs%d", i); + q7_waiters[q7_total++] = eos_task_create(name, test_entry, NULL, (uint8_t)(10 + i), 512); + assert(q7_waiters[q7_total - 1] >= 0); + } + q7_next = 0; + q7_enqueued = 0; + q7_overflow_reported = 0; + + q7_yield_hook = q7_hook; + eos_task_set_current_internal(q7_waiters[q7_next++]); + int item = 1; + int rc = eos_queue_send(q7_q, &item, EOS_WAIT_FOREVER); + if (rc == EOS_KERN_NO_MEMORY) q7_overflow_reported = 1; + q7_yield_hook = NULL; + + assert(q7_enqueued > 0); + assert(q7_overflow_reported); + assert(eos_queue_delete(q7_q) == EOS_KERN_OK); + printf("[PASS] queue send waiter overflow\n"); +} + static void test_task_stats(void) { eos_kernel_init(); int h = eos_task_create("stats_task", test_entry, NULL, 3, 1024); @@ -222,6 +271,7 @@ void eos_port_yield(void) { if (g_yield_owner >= 0) g_yield_prio = eos_task_get_priority_internal((eos_task_handle_t)g_yield_owner); + if (q7_yield_hook) q7_yield_hook(); } void eos_port_start_scheduler(void) {} uint32_t *eos_port_init_stack(uint32_t *s, void (*e)(void*), void *a) { (void)e; (void)a; return s - 17; } @@ -268,6 +318,7 @@ int main(void) { test_semaphore(); test_queue(); test_queue_full(); - printf("=== ALL KERNEL TESTS PASSED (10/10) ===\n"); + test_queue_send_waiter_overflow(); + printf("=== ALL KERNEL TESTS PASSED (11/11) ===\n"); return 0; } \ No newline at end of file