Skip to content

Return EOS_KERN_NO_MEMORY when queue waiter table is full - #85

Merged
srpatcha merged 1 commit into
embeddedos-org:masterfrom
swayam-2003:fix/queue-waiter-overflow
Sep 1, 2026
Merged

srpatcha merged 1 commit into
embeddedos-org:masterfrom
swayam-2003:fix/queue-waiter-overflow

Conversation

@swayam-2003

Copy link
Copy Markdown

Summary

When a message queue's send/recv waiter table was full (8 waiters), eos_queue_send() / eos_queue_receive() still blocked the caller without enqueueing it. That caller could sleep forever with no wakeup path. Mutexes and semaphores already return EOS_KERN_NO_MEMORY in this case.

Approach

Return EOS_KERN_NO_MEMORY before blocking when send_waiter_count or recv_waiter_count reaches Q_MAX_WAITERS, matching the mutex/semaphore pattern.

Testing

Added test_queue_send_waiter_overflow() in tests/test_kernel.c.

cmake -B build/host -DEOS_BUILD_TESTS=ON -DEOS_PRODUCT=vbox_test
cmake --build build/host --parallel
ctest --test-dir build/host -R test_kernel --output-on-failure

DCO

Signed-off-by: Swayam Nayak 154440440+swayam-2003@users.noreply.github.com

Copilot AI lite review requested due to automatic review settings August 29, 2026 18:22

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new test helper changes the kernel’s “current task” without restoring it, leaking global task context across the test execution path.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR fixes a deadlock risk in the kernel message queue implementation by refusing to block when the queue’s waiter table is already full, returning EOS_KERN_NO_MEMORY instead (matching the behavior already used by mutexes/semaphores). It also adds a regression test and documents the fix in the changelog.

Changes:

  • Update eos_queue_send() / eos_queue_receive() to return EOS_KERN_NO_MEMORY when send_waiter_count / recv_waiter_count reaches Q_MAX_WAITERS, instead of blocking without enqueueing.
  • Add a host-test regression case that drives the waiter table to capacity and verifies EOS_KERN_NO_MEMORY is reported.
  • Document the fix in CHANGELOG.md.
File summaries
File Description
kernel/src/ipc.c Adds waiter-table capacity checks for queue send/receive to prevent “blocked with no wakeup path” scenarios.
tests/test_kernel.c Adds a new regression test and a yield hook to simulate filling the queue waiter table in the host test harness.
CHANGELOG.md Notes the behavioral fix for queue waiter overflow returning EOS_KERN_NO_MEMORY.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/test_kernel.c
Comment on lines +151 to +156
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++;

@srpatcha srpatcha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified — this is a permanent hang, not a dropped notification

Confirmed the defect on origin/master. The enqueue is conditional and the block is not:

if (g_q[h].send_waiter_count < Q_MAX_WAITERS) {
    g_q[h].send_waiters[g_q[h].send_waiter_count++] = caller;
}
eos_task_block_with_timeout(caller, timeout_ms);   // ← unconditional
eos_port_exit_critical(crit);
eos_port_yield();

With a full waiter table the task blocks without being in the list nothing will ever scan. eos_queue_send never wakes it, because it wakes waiters it can see. With EOS_WAIT_FOREVER that is a task lost for the lifetime of the system; with a timeout it is a task stalled for the full duration for no reason the caller can observe.

Returning EOS_KERN_NO_MEMORY is the right answer, and it is the same answer #67 gives for mutexes:

eos_mutex_lock returns EOS_KERN_NO_MEMORY if the mutex's wait queue is already full. The call fails rather than blocking, because a task that blocks without being enqueued would never be granted the mutex.

So this makes queues consistent with the other synchronisation primitives rather than inventing a convention. Worth saying explicitly in the CHANGELOG line, which you did.

The test is doing real work

q7_hook driving eos_task_set_current_internal to march distinct tasks into the waiter table until it overflows is the only way to reach this path — it needs Q_MAX_WAITERS genuinely distinct blocked callers, which a single-threaded test cannot produce by calling send in a loop. That is the difference between a test that reaches the branch and one that only names it.

One thing worth knowing, since you use it: eos_task_set_current_internal is defined twice on master right now, with different behaviour, and the tree does not compile as a result. #82 resolves that in favour of the version that clears g_current and marks the task RUNNING — which is the one this test needs. Nothing to change here; it just means this cannot be verified against master alone.

Verification

Merged onto origin/master + #82: configure OK, 0 build errors, ctest 28/28. Not verified against master alone, because master does not configure.

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>
Copilot AI review requested due to automatic review settings August 31, 2026 13:30
@swayam-2003
swayam-2003 force-pushed the fix/queue-waiter-overflow branch from 11f8d2a to dcda013 Compare August 31, 2026 13:30

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The newly added test currently masks eos_task_create() failures by storing its int return value into a uint8_t handle array, and the receive-side overflow path is not covered by tests.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment thread tests/test_kernel.c
Comment on lines +168 to +171
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);
Comment thread tests/test_kernel.c
test_queue();
test_queue_full();
printf("=== ALL KERNEL TESTS PASSED (10/10) ===\n");
test_queue_send_waiter_overflow();
@swayam-2003

Copy link
Copy Markdown
Author

Rebased onto latest upstream/master (a0df2d5) and force-pushed. Ready for CI re-run.

@srpatcha
srpatcha merged commit 95cd852 into embeddedos-org:master Sep 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants