summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorari.liu <[email protected]>2026-08-21 11:16:10 +0800
committerRbb666 <[email protected]>2026-08-21 16:52:01 +0800
commitba79f69477686196da1aa1aca3792e8f6c4e3085 (patch)
tree4f1de264b2f2a123013cc757bd6bcad139158680
parent5ae08713c8b409aef8ba1d887c3f228566537909 (diff)
[kernel][ipc] Fix timeout wakeup ownership race
Keep thread timer ownership when timer stop races with an in-flight timeout callback, and consume IPC resources only after a waiter is resumed successfully. Complete ownership cleanup in the generic and RT-Smart channel timeout callbacks. Add deterministic scheduler and LWP regressions for Event, Semaphore, Mailbox, Message Queue, sender, and receiver paths.
-rw-r--r--components/lwp/lwp_ipc.c6
-rw-r--r--components/lwp/utest/SConscript3
-rw-r--r--components/lwp/utest/channel_timeout_tc.c329
-rw-r--r--src/ipc.c30
-rw-r--r--src/scheduler_comm.c7
-rw-r--r--src/thread.c3
-rw-r--r--src/utest/SConscript1
-rw-r--r--src/utest/sched_timeout_race_tc.c356
8 files changed, 721 insertions, 14 deletions
diff --git a/components/lwp/lwp_ipc.c b/components/lwp/lwp_ipc.c
index b995188b0c..707a6e52a7 100644
--- a/components/lwp/lwp_ipc.c
+++ b/components/lwp/lwp_ipc.c
@@ -366,6 +366,9 @@ static void sender_timeout(void *parameter)
rt_sched_lock(&slvl);
+ /* The timeout callback now owns this thread timer. */
+ RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0;
+
ch = (rt_channel_t)(thread->wakeup_handle.user_data);
if (ch->stat == RT_IPC_STAT_ACTIVE && ch->reply == thread)
{
@@ -783,6 +786,9 @@ static void receiver_timeout(void *parameter)
rt_sched_lock(&slvl);
+ /* The timeout callback now owns this thread timer. */
+ RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0;
+
ch = (rt_channel_t)(thread->wakeup_handle.user_data);
thread->error = -RT_ETIMEOUT;
diff --git a/components/lwp/utest/SConscript b/components/lwp/utest/SConscript
index ac5760a5d5..b944afe189 100644
--- a/components/lwp/utest/SConscript
+++ b/components/lwp/utest/SConscript
@@ -8,6 +8,9 @@ CPPPATH = [cwd]
if GetDepend(['RT_UTEST_LWP', 'RT_USING_SMART']):
src += ['condvar_timedwait_tc.c', 'condvar_broadcast_tc.c', 'condvar_signal_tc.c']
+if GetDepend(['RT_UTEST_SCHEDULER', 'RT_USING_SMART']):
+ src += ['channel_timeout_tc.c']
+
if GetDepend(['RT_UTEST_LWP_TTY_PTMX', 'RT_USING_SMART']):
src += ['tty_ptmx_tc.c']
diff --git a/components/lwp/utest/channel_timeout_tc.c b/components/lwp/utest/channel_timeout_tc.c
new file mode 100644
index 0000000000..c3e2c57e2b
--- /dev/null
+++ b/components/lwp/utest/channel_timeout_tc.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-08-20 illustriousness the first version
+ */
+
+/**
+ * Test Case Name: LWP Channel Thread Timer Ownership Test
+ *
+ * Test Objectives:
+ * - Verify that LWP channel timeout callbacks release thread timer ownership.
+ * - Verify that the same thread can enter a permanent channel wait after a
+ * finite wait times out and can still be woken by a channel operation.
+ *
+ * Test Scenarios:
+ * - Let a receiver time out, then receive forever and wake it with a message.
+ * - Let a sender time out waiting for a reply, then wait forever for another
+ * reply and wake it with a receiver and reply.
+ *
+ * Dependencies:
+ * - RT_USING_SMART and RT_UTEST_SCHEDULER must be enabled.
+ *
+ * Expected Results:
+ * - Both timeout callbacks clear sched_flag_ttmr_set.
+ * - Both permanent waits complete successfully without another timeout.
+ */
+
+#define __RT_KERNEL_SOURCE__
+#include <fcntl.h>
+#include <rtthread.h>
+
+#include "../lwp_ipc.h"
+#include "utest.h"
+
+#define TEST_TIMEOUT_TICKS 5
+#define TEST_FINISH_TICKS 20
+#define TEST_THREAD_TICK 5
+#define TEST_RECEIVER_VALUE 0x1234U
+#define TEST_SENDER_VALUE 0x5678U
+#define TEST_REPLY_VALUE 0x9abcU
+
+static struct rt_semaphore _timeout_sem;
+static struct rt_semaphore _continue_sem;
+static struct rt_semaphore _waiting_sem;
+static struct rt_semaphore _done_sem;
+
+static rt_uint8_t _thread_timer_flag(rt_thread_t thread)
+{
+ rt_sched_lock_level_t slvl;
+ rt_uint8_t timer_flag;
+
+ rt_sched_lock(&slvl);
+ timer_flag = RT_SCHED_CTX(thread).sched_flag_ttmr_set;
+ rt_sched_unlock(slvl);
+
+ return timer_flag;
+}
+
+static void _clear_thread_timer_flag(rt_thread_t thread)
+{
+ rt_sched_lock_level_t slvl;
+
+ rt_sched_lock(&slvl);
+ RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0;
+ rt_sched_unlock(slvl);
+}
+
+static rt_bool_t _thread_is_suspended(rt_thread_t thread)
+{
+ rt_sched_lock_level_t slvl;
+ rt_bool_t suspended;
+
+ rt_sched_lock(&slvl);
+ suspended = rt_sched_thread_is_suspended(thread);
+ rt_sched_unlock(slvl);
+
+ return suspended;
+}
+
+static rt_bool_t _wait_until_suspended(rt_thread_t thread)
+{
+ rt_tick_t deadline;
+
+ deadline = rt_tick_get() + TEST_FINISH_TICKS;
+ do
+ {
+ if (_thread_is_suspended(thread))
+ {
+ return RT_TRUE;
+ }
+ rt_thread_delay(1);
+ } while ((rt_int32_t)(deadline - rt_tick_get()) > 0);
+
+ return RT_FALSE;
+}
+
+static rt_bool_t _is_timeout_result(rt_err_t result)
+{
+ return result == RT_ETIMEOUT || result == -RT_ETIMEOUT;
+}
+
+static struct rt_thread _receiver_thread;
+rt_align(RT_ALIGN_SIZE) static rt_uint8_t _receiver_stack[UTEST_THR_STACK_SIZE];
+static rt_channel_t _receiver_channel;
+static volatile rt_err_t _receiver_timeout_result;
+static volatile rt_err_t _receiver_wait_result;
+static volatile rt_ubase_t _receiver_value;
+static volatile rt_uint8_t _receiver_timer_flag;
+
+static void _receiver_entry(void *parameter)
+{
+ struct rt_channel_msg message = { 0 };
+
+ RT_UNUSED(parameter);
+
+ _receiver_timeout_result = rt_raw_channel_recv_timeout(_receiver_channel,
+ &message,
+ TEST_TIMEOUT_TICKS);
+ _receiver_timer_flag = _thread_timer_flag(rt_thread_self());
+ rt_sem_release(&_timeout_sem);
+
+ rt_sem_take(&_continue_sem, RT_WAITING_FOREVER);
+ rt_sem_release(&_waiting_sem);
+
+ _receiver_wait_result = rt_raw_channel_recv(_receiver_channel, &message);
+ if (_receiver_wait_result == RT_EOK)
+ {
+ _receiver_value = (rt_ubase_t)message.u.d;
+ }
+ rt_sem_release(&_done_sem);
+}
+
+static void _test_receiver_timeout_reuse(void)
+{
+ struct rt_channel_msg message = { 0 };
+
+ _receiver_timeout_result = RT_EOK;
+ _receiver_wait_result = -RT_ERROR;
+ _receiver_value = 0;
+ _receiver_timer_flag = 0;
+
+ _receiver_channel = rt_raw_channel_open("to_recv", O_CREAT | O_EXCL);
+ uassert_not_null(_receiver_channel);
+ if (_receiver_channel == RT_NULL)
+ {
+ return;
+ }
+
+ uassert_int_equal(rt_thread_init(&_receiver_thread,
+ "to_recv",
+ _receiver_entry,
+ RT_NULL,
+ _receiver_stack,
+ sizeof(_receiver_stack),
+ UTEST_THR_PRIORITY + 1,
+ TEST_THREAD_TICK),
+ RT_EOK);
+ uassert_int_equal(rt_thread_startup(&_receiver_thread), RT_EOK);
+ uassert_int_equal(rt_sem_take(&_timeout_sem, TEST_FINISH_TICKS), RT_EOK);
+
+ uassert_true(_is_timeout_result(_receiver_timeout_result));
+ uassert_int_equal(_receiver_timer_flag, 0);
+ if (_receiver_timer_flag != 0)
+ {
+ /* Keep the negative-path test recoverable on an unfixed kernel. */
+ _clear_thread_timer_flag(&_receiver_thread);
+ }
+
+ uassert_int_equal(rt_sem_release(&_continue_sem), RT_EOK);
+ uassert_int_equal(rt_sem_take(&_waiting_sem, TEST_FINISH_TICKS), RT_EOK);
+ uassert_true(_wait_until_suspended(&_receiver_thread));
+
+ message.type = RT_CHANNEL_RAW;
+ message.u.d = (void *)(rt_ubase_t)TEST_RECEIVER_VALUE;
+ uassert_int_equal(rt_raw_channel_send(_receiver_channel, &message), RT_EOK);
+ uassert_int_equal(rt_sem_take(&_done_sem, TEST_FINISH_TICKS), RT_EOK);
+ uassert_int_equal(_receiver_wait_result, RT_EOK);
+ uassert_int_equal(_receiver_value, TEST_RECEIVER_VALUE);
+ uassert_int_equal(rt_raw_channel_close(_receiver_channel), RT_EOK);
+}
+
+static struct rt_thread _sender_thread;
+rt_align(RT_ALIGN_SIZE) static rt_uint8_t _sender_stack[UTEST_THR_STACK_SIZE];
+static rt_channel_t _sender_channel;
+static volatile rt_err_t _sender_timeout_result;
+static volatile rt_err_t _sender_wait_result;
+static volatile rt_ubase_t _sender_reply_value;
+static volatile rt_uint8_t _sender_timer_flag;
+
+static void _sender_entry(void *parameter)
+{
+ struct rt_channel_msg request = { 0 };
+ struct rt_channel_msg reply = { 0 };
+
+ RT_UNUSED(parameter);
+
+ request.type = RT_CHANNEL_RAW;
+ request.u.d = (void *)(rt_ubase_t)TEST_SENDER_VALUE;
+ _sender_timeout_result = rt_raw_channel_send_recv_timeout(_sender_channel,
+ &request,
+ &reply,
+ TEST_TIMEOUT_TICKS);
+ _sender_timer_flag = _thread_timer_flag(rt_thread_self());
+ rt_sem_release(&_timeout_sem);
+
+ rt_sem_take(&_continue_sem, RT_WAITING_FOREVER);
+ rt_sem_release(&_waiting_sem);
+
+ _sender_wait_result = rt_raw_channel_send_recv(_sender_channel, &request, &reply);
+ if (_sender_wait_result == RT_EOK)
+ {
+ _sender_reply_value = (rt_ubase_t)reply.u.d;
+ }
+ rt_sem_release(&_done_sem);
+}
+
+static void _test_sender_timeout_reuse(void)
+{
+ struct rt_channel_msg request = { 0 };
+ struct rt_channel_msg reply = { 0 };
+
+ _sender_timeout_result = RT_EOK;
+ _sender_wait_result = -RT_ERROR;
+ _sender_reply_value = 0;
+ _sender_timer_flag = 0;
+
+ _sender_channel = rt_raw_channel_open("to_send", O_CREAT | O_EXCL);
+ uassert_not_null(_sender_channel);
+ if (_sender_channel == RT_NULL)
+ {
+ return;
+ }
+
+ uassert_int_equal(rt_thread_init(&_sender_thread,
+ "to_send",
+ _sender_entry,
+ RT_NULL,
+ _sender_stack,
+ sizeof(_sender_stack),
+ UTEST_THR_PRIORITY + 1,
+ TEST_THREAD_TICK),
+ RT_EOK);
+ uassert_int_equal(rt_thread_startup(&_sender_thread), RT_EOK);
+ uassert_int_equal(rt_sem_take(&_timeout_sem, TEST_FINISH_TICKS), RT_EOK);
+
+ uassert_true(_is_timeout_result(_sender_timeout_result));
+ uassert_int_equal(_sender_timer_flag, 0);
+ if (_sender_timer_flag != 0)
+ {
+ /* Keep the negative-path test recoverable on an unfixed kernel. */
+ _clear_thread_timer_flag(&_sender_thread);
+ }
+
+ uassert_int_equal(rt_sem_release(&_continue_sem), RT_EOK);
+ uassert_int_equal(rt_sem_take(&_waiting_sem, TEST_FINISH_TICKS), RT_EOK);
+ uassert_true(_wait_until_suspended(&_sender_thread));
+
+ uassert_int_equal(rt_raw_channel_recv_timeout(_sender_channel,
+ &request,
+ TEST_FINISH_TICKS),
+ RT_EOK);
+ uassert_int_equal(request.type, RT_CHANNEL_RAW);
+ uassert_int_equal((rt_ubase_t)request.u.d, TEST_SENDER_VALUE);
+
+ reply.type = RT_CHANNEL_RAW;
+ reply.u.d = (void *)(rt_ubase_t)TEST_REPLY_VALUE;
+ uassert_int_equal(rt_raw_channel_reply(_sender_channel, &reply), RT_EOK);
+ uassert_int_equal(rt_sem_take(&_done_sem, TEST_FINISH_TICKS), RT_EOK);
+ uassert_int_equal(_sender_wait_result, RT_EOK);
+ uassert_int_equal(_sender_reply_value, TEST_REPLY_VALUE);
+ uassert_int_equal(rt_raw_channel_close(_sender_channel), RT_EOK);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+ rt_err_t result;
+
+ result = rt_sem_init(&_timeout_sem, "to_tmo", 0, RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ return result;
+ }
+ result = rt_sem_init(&_continue_sem, "to_cont", 0, RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ rt_sem_detach(&_timeout_sem);
+ return result;
+ }
+ result = rt_sem_init(&_waiting_sem, "to_wait", 0, RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ rt_sem_detach(&_continue_sem);
+ rt_sem_detach(&_timeout_sem);
+ return result;
+ }
+ result = rt_sem_init(&_done_sem, "to_done", 0, RT_IPC_FLAG_FIFO);
+ if (result != RT_EOK)
+ {
+ rt_sem_detach(&_waiting_sem);
+ rt_sem_detach(&_continue_sem);
+ rt_sem_detach(&_timeout_sem);
+ }
+
+ return result;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ rt_sem_detach(&_done_sem);
+ rt_sem_detach(&_waiting_sem);
+ rt_sem_detach(&_continue_sem);
+ return rt_sem_detach(&_timeout_sem);
+}
+
+static void testcase(void)
+{
+ UTEST_UNIT_RUN(_test_receiver_timeout_reuse);
+ UTEST_UNIT_RUN(_test_sender_timeout_reuse);
+}
+
+UTEST_TC_EXPORT(testcase,
+ "components.lwp.channel_timeout_reuse",
+ utest_tc_init,
+ utest_tc_cleanup,
+ 10);
diff --git a/src/ipc.c b/src/ipc.c
index a8fde2a8cf..4e26db6691 100644
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -140,7 +140,10 @@ struct rt_thread *rt_susp_list_dequeue(rt_list_t *susp_list, rt_err_t thread_err
}
rt_sched_unlock(slvl);
- LOG_D("resume thread:%s\n", thread->parent.name);
+ if (thread)
+ {
+ LOG_D("resume thread:%s\n", thread->parent.name);
+ }
return thread;
}
@@ -713,10 +716,9 @@ rt_err_t rt_sem_release(rt_sem_t sem)
sem->parent.parent.name,
sem->value);
- if (!rt_list_isempty(&sem->parent.suspend_thread))
+ if (!rt_list_isempty(&sem->parent.suspend_thread) &&
+ rt_susp_list_dequeue(&(sem->parent.suspend_thread), RT_EOK) != RT_NULL)
{
- /* resume the suspended thread */
- rt_susp_list_dequeue(&(sem->parent.suspend_thread), RT_EOK);
need_schedule = RT_TRUE;
}
else
@@ -2053,16 +2055,20 @@ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)
/* condition is satisfied, resume thread */
if (status == RT_EOK)
{
- /* clear event */
- if (thread->event_info & RT_EVENT_FLAG_CLEAR)
- need_clear_set |= thread->event_set;
+ status = rt_sched_thread_ready(thread);
+ if (status == RT_EOK)
+ {
+ /* clear event */
+ if (thread->event_info & RT_EVENT_FLAG_CLEAR)
+ {
+ need_clear_set |= thread->event_set;
+ }
- /* resume thread, and thread list breaks out */
- rt_sched_thread_ready(thread);
- thread->error = RT_EOK;
+ thread->error = RT_EOK;
- /* need do a scheduling */
- need_schedule = RT_TRUE;
+ /* need do a scheduling */
+ need_schedule = RT_TRUE;
+ }
}
}
if (need_clear_set)
diff --git a/src/scheduler_comm.c b/src/scheduler_comm.c
index 84ac0a7416..5986f5a62c 100644
--- a/src/scheduler_comm.c
+++ b/src/scheduler_comm.c
@@ -81,8 +81,11 @@ rt_err_t rt_sched_thread_timer_stop(struct rt_thread *thread)
{
error = rt_timer_stop(&thread->thread_timer);
- /* mask out timer flag no matter stop success or not */
- RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0;
+ /* A failed stop means the timeout callback owns the thread timer. */
+ if (error == RT_EOK)
+ {
+ RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0;
+ }
}
else
{
diff --git a/src/thread.c b/src/thread.c
index 745e3f774f..0c4a645eac 100644
--- a/src/thread.c
+++ b/src/thread.c
@@ -162,6 +162,9 @@ static void _thread_timeout(void *parameter)
*/
RT_ASSERT(rt_sched_thread_is_suspended(thread));
+ /* The timeout callback now owns this thread timer. */
+ RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0;
+
/* set error number */
thread->error = -RT_ETIMEOUT;
diff --git a/src/utest/SConscript b/src/utest/SConscript
index 7a532625d1..52aca3dcf6 100644
--- a/src/utest/SConscript
+++ b/src/utest/SConscript
@@ -60,6 +60,7 @@ if GetDepend(['RT_UTEST_MEMPOOL']):
# Stressful testcase for scheduler (MP/UP)
if GetDepend(['RT_UTEST_SCHEDULER']):
+ src += ['sched_timeout_race_tc.c']
src += ['sched_timed_sem_tc.c']
src += ['sched_timed_mtx_tc.c']
src += ['sched_mtx_tc.c']
diff --git a/src/utest/sched_timeout_race_tc.c b/src/utest/sched_timeout_race_tc.c
new file mode 100644
index 0000000000..24327bad61
--- /dev/null
+++ b/src/utest/sched_timeout_race_tc.c
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2006-2026, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2026-07-17 illustriousness the first version
+ */
+
+/**
+ * Test Case Name: Scheduler IPC Timeout Ownership Race Test
+ *
+ * Test Objectives:
+ * - Verify that an in-flight timeout remains the only owner of a suspended
+ * thread after its timer has been removed from the timer list.
+ * - Verify that competing IPC producers do not resume the same thread twice.
+ * - Verify that IPC resources are retained when the timeout wins the race.
+ *
+ * Test Scenarios:
+ * - Suspend a thread on an IPC object with a finite timeout.
+ * - Stop its thread timer directly, reproducing the state after timer expiry
+ * removal and before the timeout callback executes.
+ * - Produce two events, semaphore tokens, mailbox messages, or queue messages.
+ * - Restart the thread timer to let the timeout callback complete normally.
+ * - Verify that the waiter times out once and all produced resources remain.
+ *
+ * Dependencies:
+ * - RT_UTEST_SCHEDULER must be enabled.
+ * - The individual IPC tests are compiled when their IPC feature is enabled.
+ *
+ * Expected Results:
+ * - No kernel assertion or duplicate thread resume occurs.
+ * - The final output reports a passed core.scheduler_timeout_race testcase.
+ */
+
+#define __RT_KERNEL_SOURCE__
+#include <rtthread.h>
+#include "utest.h"
+
+#define TEST_WAIT_TICKS 100
+#define TEST_FINISH_TICKS 10
+#define TEST_THREAD_TICK 5
+#define TEST_EVENT_FLAG 0x01
+#define TEST_MQ_MSG_SIZE sizeof(rt_uint32_t)
+#define TEST_MQ_MSG_COUNT 2
+
+static struct rt_semaphore _exit_sem;
+
+static rt_bool_t _thread_is_suspended(rt_thread_t thread)
+{
+ rt_sched_lock_level_t slvl;
+ rt_bool_t suspended;
+
+ rt_sched_lock(&slvl);
+ suspended = rt_sched_thread_is_suspended(thread);
+ rt_sched_unlock(slvl);
+
+ return suspended;
+}
+
+static rt_bool_t _wait_until_suspended(rt_thread_t thread)
+{
+ rt_tick_t deadline;
+
+ deadline = rt_tick_get() + TEST_FINISH_TICKS;
+ do
+ {
+ if (_thread_is_suspended(thread))
+ {
+ return RT_TRUE;
+ }
+ rt_thread_delay(1);
+ } while ((rt_int32_t)(deadline - rt_tick_get()) > 0);
+
+ return RT_FALSE;
+}
+
+static void _take_timeout_ownership(rt_thread_t thread)
+{
+ rt_sched_lock_level_t slvl;
+ rt_bool_t suspended;
+ rt_err_t stop_result;
+ rt_uint8_t timer_flag;
+
+ rt_sched_lock(&slvl);
+ suspended = rt_sched_thread_is_suspended(thread);
+ stop_result = rt_timer_stop(&thread->thread_timer);
+ timer_flag = RT_SCHED_CTX(thread).sched_flag_ttmr_set;
+ rt_sched_unlock(slvl);
+
+ uassert_true(suspended);
+ uassert_int_equal(stop_result, RT_EOK);
+ uassert_int_equal(timer_flag, 1);
+}
+
+static void _complete_timeout(rt_thread_t thread)
+{
+ rt_tick_t timeout = 1;
+
+ uassert_int_equal(rt_timer_control(&thread->thread_timer,
+ RT_TIMER_CTRL_SET_TIME,
+ &timeout),
+ RT_EOK);
+ uassert_int_equal(rt_timer_start(&thread->thread_timer), RT_EOK);
+ uassert_int_equal(rt_sem_take(&_exit_sem, TEST_FINISH_TICKS), RT_EOK);
+}
+
+#ifdef RT_USING_EVENT
+static struct rt_event _event;
+static struct rt_thread _event_waiter;
+rt_align(RT_ALIGN_SIZE) static rt_uint8_t _event_waiter_stack[UTEST_THR_STACK_SIZE];
+static volatile rt_err_t _event_wait_result;
+
+static void _event_waiter_entry(void *parameter)
+{
+ rt_uint32_t received;
+
+ RT_UNUSED(parameter);
+ _event_wait_result = rt_event_recv(&_event,
+ TEST_EVENT_FLAG,
+ RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
+ TEST_WAIT_TICKS,
+ &received);
+ rt_sem_release(&_exit_sem);
+}
+
+static void _test_event_timeout_ownership(void)
+{
+ rt_uint32_t received = 0;
+
+ _event_wait_result = RT_EOK;
+ uassert_int_equal(rt_event_init(&_event, "to_evt", RT_IPC_FLAG_FIFO), RT_EOK);
+ uassert_int_equal(rt_thread_init(&_event_waiter,
+ "to_evt",
+ _event_waiter_entry,
+ RT_NULL,
+ _event_waiter_stack,
+ sizeof(_event_waiter_stack),
+ UTEST_THR_PRIORITY + 1,
+ TEST_THREAD_TICK),
+ RT_EOK);
+ uassert_int_equal(rt_thread_startup(&_event_waiter), RT_EOK);
+ uassert_true(_wait_until_suspended(&_event_waiter));
+
+ _take_timeout_ownership(&_event_waiter);
+ uassert_int_equal(rt_event_send(&_event, TEST_EVENT_FLAG), RT_EOK);
+ uassert_int_equal(rt_event_send(&_event, TEST_EVENT_FLAG), RT_EOK);
+ uassert_true(_thread_is_suspended(&_event_waiter));
+
+ _complete_timeout(&_event_waiter);
+ uassert_int_equal(_event_wait_result, -RT_ETIMEOUT);
+ uassert_int_equal(rt_event_recv(&_event,
+ TEST_EVENT_FLAG,
+ RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
+ RT_WAITING_NO,
+ &received),
+ RT_EOK);
+ uassert_int_equal(received, TEST_EVENT_FLAG);
+ uassert_int_equal(rt_event_detach(&_event), RT_EOK);
+}
+#endif /* RT_USING_EVENT */
+
+#ifdef RT_USING_SEMAPHORE
+static struct rt_semaphore _sem;
+static struct rt_thread _sem_waiter;
+rt_align(RT_ALIGN_SIZE) static rt_uint8_t _sem_waiter_stack[UTEST_THR_STACK_SIZE];
+static volatile rt_err_t _sem_wait_result;
+
+static void _sem_waiter_entry(void *parameter)
+{
+ RT_UNUSED(parameter);
+ _sem_wait_result = rt_sem_take(&_sem, TEST_WAIT_TICKS);
+ rt_sem_release(&_exit_sem);
+}
+
+static void _test_sem_timeout_ownership(void)
+{
+ _sem_wait_result = RT_EOK;
+ uassert_int_equal(rt_sem_init(&_sem, "to_sem", 0, RT_IPC_FLAG_FIFO), RT_EOK);
+ uassert_int_equal(rt_thread_init(&_sem_waiter,
+ "to_sem",
+ _sem_waiter_entry,
+ RT_NULL,
+ _sem_waiter_stack,
+ sizeof(_sem_waiter_stack),
+ UTEST_THR_PRIORITY + 1,
+ TEST_THREAD_TICK),
+ RT_EOK);
+ uassert_int_equal(rt_thread_startup(&_sem_waiter), RT_EOK);
+ uassert_true(_wait_until_suspended(&_sem_waiter));
+
+ _take_timeout_ownership(&_sem_waiter);
+ uassert_int_equal(rt_sem_release(&_sem), RT_EOK);
+ uassert_int_equal(rt_sem_release(&_sem), RT_EOK);
+ uassert_true(_thread_is_suspended(&_sem_waiter));
+ uassert_int_equal(_sem.value, 2);
+
+ _complete_timeout(&_sem_waiter);
+ uassert_int_equal(_sem_wait_result, -RT_ETIMEOUT);
+ uassert_int_equal(rt_sem_trytake(&_sem), RT_EOK);
+ uassert_int_equal(rt_sem_trytake(&_sem), RT_EOK);
+ uassert_int_equal(rt_sem_trytake(&_sem), -RT_ETIMEOUT);
+ uassert_int_equal(rt_sem_detach(&_sem), RT_EOK);
+}
+#endif /* RT_USING_SEMAPHORE */
+
+#ifdef RT_USING_MAILBOX
+static struct rt_mailbox _mailbox;
+static rt_ubase_t _mailbox_pool[TEST_MQ_MSG_COUNT];
+static struct rt_thread _mailbox_waiter;
+rt_align(RT_ALIGN_SIZE) static rt_uint8_t _mailbox_waiter_stack[UTEST_THR_STACK_SIZE];
+static volatile rt_err_t _mailbox_wait_result;
+
+static void _mailbox_waiter_entry(void *parameter)
+{
+ rt_ubase_t value;
+
+ RT_UNUSED(parameter);
+ _mailbox_wait_result = rt_mb_recv(&_mailbox, &value, TEST_WAIT_TICKS);
+ rt_sem_release(&_exit_sem);
+}
+
+static void _test_mailbox_timeout_ownership(void)
+{
+ rt_ubase_t value;
+
+ _mailbox_wait_result = RT_EOK;
+ uassert_int_equal(rt_mb_init(&_mailbox,
+ "to_mb",
+ _mailbox_pool,
+ sizeof(_mailbox_pool) / sizeof(_mailbox_pool[0]),
+ RT_IPC_FLAG_FIFO),
+ RT_EOK);
+ uassert_int_equal(rt_thread_init(&_mailbox_waiter,
+ "to_mb",
+ _mailbox_waiter_entry,
+ RT_NULL,
+ _mailbox_waiter_stack,
+ sizeof(_mailbox_waiter_stack),
+ UTEST_THR_PRIORITY + 1,
+ TEST_THREAD_TICK),
+ RT_EOK);
+ uassert_int_equal(rt_thread_startup(&_mailbox_waiter), RT_EOK);
+ uassert_true(_wait_until_suspended(&_mailbox_waiter));
+
+ _take_timeout_ownership(&_mailbox_waiter);
+ uassert_int_equal(rt_mb_send(&_mailbox, 1), RT_EOK);
+ uassert_int_equal(rt_mb_send(&_mailbox, 2), RT_EOK);
+ uassert_true(_thread_is_suspended(&_mailbox_waiter));
+ uassert_int_equal(_mailbox.entry, 2);
+
+ _complete_timeout(&_mailbox_waiter);
+ uassert_int_equal(_mailbox_wait_result, -RT_ETIMEOUT);
+ uassert_int_equal(rt_mb_recv(&_mailbox, &value, RT_WAITING_NO), RT_EOK);
+ uassert_int_equal(value, 1);
+ uassert_int_equal(rt_mb_recv(&_mailbox, &value, RT_WAITING_NO), RT_EOK);
+ uassert_int_equal(value, 2);
+ uassert_int_equal(rt_mb_detach(&_mailbox), RT_EOK);
+}
+#endif /* RT_USING_MAILBOX */
+
+#ifdef RT_USING_MESSAGEQUEUE
+static struct rt_messagequeue _messagequeue;
+static rt_uint8_t _messagequeue_pool[RT_MQ_BUF_SIZE(TEST_MQ_MSG_SIZE, TEST_MQ_MSG_COUNT)];
+static struct rt_thread _messagequeue_waiter;
+rt_align(RT_ALIGN_SIZE) static rt_uint8_t _messagequeue_waiter_stack[UTEST_THR_STACK_SIZE];
+static volatile rt_ssize_t _messagequeue_wait_result;
+
+static void _messagequeue_waiter_entry(void *parameter)
+{
+ rt_uint32_t value;
+
+ RT_UNUSED(parameter);
+ _messagequeue_wait_result = rt_mq_recv(&_messagequeue,
+ &value,
+ sizeof(value),
+ TEST_WAIT_TICKS);
+ rt_sem_release(&_exit_sem);
+}
+
+static void _test_messagequeue_timeout_ownership(void)
+{
+ rt_uint32_t value;
+ rt_uint32_t first = 1;
+ rt_uint32_t second = 2;
+
+ _messagequeue_wait_result = 0;
+ uassert_int_equal(rt_mq_init(&_messagequeue,
+ "to_mq",
+ _messagequeue_pool,
+ TEST_MQ_MSG_SIZE,
+ sizeof(_messagequeue_pool),
+ RT_IPC_FLAG_FIFO),
+ RT_EOK);
+ uassert_int_equal(rt_thread_init(&_messagequeue_waiter,
+ "to_mq",
+ _messagequeue_waiter_entry,
+ RT_NULL,
+ _messagequeue_waiter_stack,
+ sizeof(_messagequeue_waiter_stack),
+ UTEST_THR_PRIORITY + 1,
+ TEST_THREAD_TICK),
+ RT_EOK);
+ uassert_int_equal(rt_thread_startup(&_messagequeue_waiter), RT_EOK);
+ uassert_true(_wait_until_suspended(&_messagequeue_waiter));
+
+ _take_timeout_ownership(&_messagequeue_waiter);
+ uassert_int_equal(rt_mq_send(&_messagequeue, &first, sizeof(first)), RT_EOK);
+ uassert_int_equal(rt_mq_send(&_messagequeue, &second, sizeof(second)), RT_EOK);
+ uassert_true(_thread_is_suspended(&_messagequeue_waiter));
+ uassert_int_equal(_messagequeue.entry, 2);
+
+ _complete_timeout(&_messagequeue_waiter);
+ uassert_int_equal(_messagequeue_wait_result, -RT_ETIMEOUT);
+ uassert_int_equal(rt_mq_recv(&_messagequeue, &value, sizeof(value), RT_WAITING_NO),
+ sizeof(value));
+ uassert_int_equal(value, first);
+ uassert_int_equal(rt_mq_recv(&_messagequeue, &value, sizeof(value), RT_WAITING_NO),
+ sizeof(value));
+ uassert_int_equal(value, second);
+ uassert_int_equal(rt_mq_detach(&_messagequeue), RT_EOK);
+}
+#endif /* RT_USING_MESSAGEQUEUE */
+
+static rt_err_t utest_tc_init(void)
+{
+ return rt_sem_init(&_exit_sem, "to_exit", 0, RT_IPC_FLAG_FIFO);
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+ return rt_sem_detach(&_exit_sem);
+}
+
+static void testcase(void)
+{
+#ifdef RT_USING_EVENT
+ UTEST_UNIT_RUN(_test_event_timeout_ownership);
+#endif
+#ifdef RT_USING_SEMAPHORE
+ UTEST_UNIT_RUN(_test_sem_timeout_ownership);
+#endif
+#ifdef RT_USING_MAILBOX
+ UTEST_UNIT_RUN(_test_mailbox_timeout_ownership);
+#endif
+#ifdef RT_USING_MESSAGEQUEUE
+ UTEST_UNIT_RUN(_test_messagequeue_timeout_ownership);
+#endif
+}
+
+UTEST_TC_EXPORT(testcase,
+ "core.scheduler_timeout_race",
+ utest_tc_init,
+ utest_tc_cleanup,
+ 10);