From cbde064ab6bb6240ba04ecb078deee688a25cf56 Mon Sep 17 00:00:00 2001 From: Hui Su <3164683437@qq.com> Date: Fri, 21 Aug 2026 10:33:30 +0800 Subject: [PATCH] [kernel/mutex] move waiter cleanup before READY transition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move mutex-specific waiter cleanup before a waiter enters the READY state. Whichever path wins wakeup ownership—delete/detach, timeout, or normal mutex release—cleans the mutex wait list and priority-inheritance state, then drops pending_object before the waiter can resume without the mutex lifetime being guaranteed. Return timeout and deletion errors without dereferencing a stale mutex, retry later waiters when a timeout-owned waiter is encountered, and add deterministic regression coverage for both timeout ownership states. --- include/rtthread.h | 3 + src/ipc.c | 153 ++++++++++---- src/thread.c | 13 +- src/utest/mutex_tc.c | 484 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 613 insertions(+), 40 deletions(-) diff --git a/include/rtthread.h b/include/rtthread.h index 177ea877831e..5a828ca514bd 100644 --- a/include/rtthread.h +++ b/include/rtthread.h @@ -466,6 +466,9 @@ rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag); rt_err_t rt_mutex_delete(rt_mutex_t mutex); #endif /* RT_USING_HEAP */ void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread); +#if defined(__RT_KERNEL_SOURCE__) || defined(__RT_IPC_SOURCE__) +rt_bool_t rt_mutex_cleanup_waiter(rt_thread_t thread, rt_bool_t remove_from_list); +#endif /* defined(__RT_KERNEL_SOURCE__) || defined(__RT_IPC_SOURCE__) */ rt_uint8_t rt_mutex_setprioceiling(rt_mutex_t mutex, rt_uint8_t priority); rt_uint8_t rt_mutex_getprioceiling(rt_mutex_t mutex); diff --git a/src/ipc.c b/src/ipc.c index 4e26db6691bb..47ef7319d5f9 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -922,6 +922,55 @@ rt_inline void _thread_update_priority(struct rt_thread *thread, rt_uint8_t prio } } +/* + * Detach a mutex waiter from mutex-specific state. + * + * The scheduler lock must be held. After this function returns true, + * the waiter no longer needs to access the mutex object. + */ +rt_bool_t rt_mutex_cleanup_waiter(rt_thread_t thread, rt_bool_t remove_from_list) +{ + rt_mutex_t mutex; + rt_uint8_t priority; + rt_bool_t need_update = RT_FALSE; + + RT_SCHED_DEBUG_IS_LOCKED; + + if ((thread->pending_object == RT_NULL) || + (rt_object_get_type(thread->pending_object) != RT_Object_Class_Mutex)) + { + return RT_FALSE; + } + + mutex = (rt_mutex_t)thread->pending_object; + + if (mutex->owner && + (rt_sched_thread_get_curr_prio(mutex->owner) == + rt_sched_thread_get_curr_prio(thread))) + { + need_update = RT_TRUE; + } + + if (remove_from_list) + { + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); + } + _mutex_update_priority(mutex); + + if (need_update && mutex->owner) + { + priority = _thread_get_mutex_priority(mutex->owner); + if (priority != rt_sched_thread_get_curr_prio(mutex->owner)) + { + _thread_update_priority(mutex->owner, priority, RT_UNINTERRUPTIBLE); + } + } + + thread->pending_object = RT_NULL; + + return RT_TRUE; +} + static rt_bool_t _check_and_update_prio(rt_thread_t thread, rt_mutex_t mutex) { RT_SCHED_DEBUG_IS_LOCKED; @@ -951,8 +1000,39 @@ static void _mutex_before_delete_detach(rt_mutex_t mutex) rt_bool_t need_schedule = RT_FALSE; rt_spin_lock(&(mutex->spinlock)); - /* wakeup all suspended threads */ - rt_susp_list_resume_all(&(mutex->parent.suspend_thread), RT_ERROR); + + /* + * Wake waiters and clear their mutex references under one scheduler lock. + * If timeout owns a waiter's timer, only clean the mutex state here; the + * timeout callback still owns making the thread ready. + */ + for (;;) + { + rt_thread_t thread; + rt_bool_t cleaned; + + rt_sched_lock(&slvl); + if (rt_list_isempty(&mutex->parent.suspend_thread)) + { + rt_sched_unlock(slvl); + break; + } + + thread = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next); + if (rt_sched_thread_ready(thread) == RT_EOK) + { + cleaned = rt_mutex_cleanup_waiter(thread, RT_FALSE); + RT_ASSERT(cleaned); + thread->error = RT_ERROR; + rt_sched_unlock(slvl); + } + else + { + cleaned = rt_mutex_cleanup_waiter(thread, RT_TRUE); + RT_ASSERT(cleaned); + rt_sched_unlock(slvl); + } + } rt_sched_lock(&slvl); @@ -1452,6 +1532,14 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend /* do schedule */ rt_schedule(); + /* Return the deletion error after the mutex was removed. */ + if ((thread->pending_object == RT_NULL) && + ((thread->error == RT_ERROR) || + (thread->error == -RT_ETIMEOUT))) + { + return thread->error == RT_ERROR ? -RT_ERROR : thread->error; + } + rt_spin_lock(&(mutex->spinlock)); if (mutex->owner == thread) @@ -1657,32 +1745,27 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) /* whether change the thread priority */ need_schedule = _check_and_update_prio(owner, mutex); - /* wakeup suspended thread */ - if (!rt_list_isempty(&mutex->parent.suspend_thread)) + /* wakeup the first waiter that still owns its timer */ + for (;;) { struct rt_thread *next_thread; - do - { - /* get the first suspended thread */ - next_thread = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next); - - RT_ASSERT(rt_sched_thread_is_suspended(next_thread)); + rt_bool_t cleaned; - /* remove the thread from the suspended list of mutex */ - rt_list_remove(&RT_THREAD_LIST_NODE(next_thread)); + if (rt_list_isempty(&mutex->parent.suspend_thread)) + { + /* no waiting thread is woke up, clear owner */ + mutex->owner = RT_NULL; + mutex->priority = 0xff; + rt_sched_unlock(slvl); + break; + } - /* resume thread to ready queue */ - if (rt_sched_thread_ready(next_thread) != RT_EOK) - { - /** - * a timeout timer had triggered while we try. So we skip - * this thread and try again. - */ - next_thread = RT_NULL; - } - } while (!next_thread && !rt_list_isempty(&mutex->parent.suspend_thread)); + /* get the first suspended thread */ + next_thread = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next); + RT_ASSERT(rt_sched_thread_is_suspended(next_thread)); - if (next_thread) + /* resume thread to ready queue */ + if (rt_sched_thread_ready(next_thread) == RT_EOK) { LOG_D("mutex_release: resume thread: %s", next_thread->parent.name); @@ -1709,23 +1792,17 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) } need_schedule = RT_TRUE; + rt_sched_unlock(slvl); + break; } - else - { - /* no waiting thread is woke up, clear owner */ - mutex->owner = RT_NULL; - mutex->priority = 0xff; - } - - rt_sched_unlock(slvl); - } - else - { - rt_sched_unlock(slvl); - /* clear owner */ - mutex->owner = RT_NULL; - mutex->priority = 0xff; + /** + * A timeout callback owns this waiter. Remove only the mutex + * state and leave READY/error ownership to the callback, then + * retry the list head while the mutex remains locked. + */ + cleaned = rt_mutex_cleanup_waiter(next_thread, RT_TRUE); + RT_ASSERT(cleaned); } } diff --git a/src/thread.c b/src/thread.c index 0c4a645eac01..cfd6644d8494 100644 --- a/src/thread.c +++ b/src/thread.c @@ -147,6 +147,7 @@ static void _thread_timeout(void *parameter) { struct rt_thread *thread; rt_sched_lock_level_t slvl; + rt_bool_t mutex_timeout = RT_FALSE; thread = (struct rt_thread *)parameter; @@ -168,8 +169,16 @@ static void _thread_timeout(void *parameter) /* set error number */ thread->error = -RT_ETIMEOUT; - /* remove from suspend list */ - rt_list_remove(&RT_THREAD_LIST_NODE(thread)); + /* Mutex timeout also removes the waiter from the mutex wait list. */ +#ifdef RT_USING_MUTEX + mutex_timeout = rt_mutex_cleanup_waiter(thread, RT_TRUE); +#endif /* RT_USING_MUTEX */ + + if (!mutex_timeout) + { + /* remove from suspend list */ + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); + } /* insert to schedule ready list */ rt_sched_insert_thread(thread); /* do schedule and release the scheduler lock */ diff --git a/src/utest/mutex_tc.c b/src/utest/mutex_tc.c index 02cf5571993c..c63e187b4bda 100644 --- a/src/utest/mutex_tc.c +++ b/src/utest/mutex_tc.c @@ -44,6 +44,7 @@ #define __RT_IPC_SOURCE__ #include +#include #include #include "utest.h" @@ -904,10 +905,481 @@ static void test_cross_thread_delete_mutex_owner(void) } #endif /* RT_USING_HEAP */ +#ifdef RT_USING_HEAP +static struct rt_semaphore mutex_delete_ready; +static struct rt_semaphore mutex_delete_done; +static struct rt_semaphore mutex_delete_restart; +static struct rt_semaphore mutex_delete_restart_done; +static rt_mutex_t mutex_delete_dynamic; +static void *mutex_delete_replacement_memory; +static struct rt_mutex mutex_delete_static; +static volatile rt_err_t mutex_delete_wait_result; +static volatile rt_err_t mutex_delete_next_wait_result; +static volatile rt_err_t mutex_delete_restart_result; +static rt_thread_t mutex_delete_waiter; +static rt_int32_t mutex_delete_wait_timeout; +static rt_bool_t mutex_delete_semaphores_initialized; + +static void mutex_delete_bind_waiter(rt_thread_t thread) +{ +#ifdef RT_USING_SMP + uassert_int_equal(rt_thread_control(thread, + RT_THREAD_CTRL_BIND_CPU, + (void *)(rt_ubase_t)rt_cpu_get_id()), + RT_EOK); +#else + RT_UNUSED(thread); +#endif /* RT_USING_SMP */ +} + +/* Record the result returned by a waiter after its mutex is deleted. */ +static void mutex_delete_waiter_entry(void *parameter) +{ + rt_sem_release(&mutex_delete_ready); + mutex_delete_wait_result = + rt_mutex_take((rt_mutex_t)parameter, mutex_delete_wait_timeout); + rt_sem_release(&mutex_delete_done); +} + +static void mutex_delete_next_waiter_entry(void *parameter) +{ + rt_mutex_t mutex = (rt_mutex_t)parameter; + + rt_sem_release(&mutex_delete_ready); + mutex_delete_next_wait_result = + rt_mutex_take(mutex, mutex_delete_wait_timeout); + if (mutex_delete_next_wait_result == RT_EOK) + { + mutex_delete_next_wait_result = rt_mutex_release(mutex); + } + rt_sem_release(&mutex_delete_done); +} + +/* Restart a timeout after release has observed timer ownership. */ +static void mutex_delete_restart_timeout_entry(void *parameter) +{ + rt_thread_t thread = (rt_thread_t)parameter; + rt_tick_t timeout = 1; + + rt_sem_take(&mutex_delete_restart, RT_WAITING_FOREVER); + mutex_delete_restart_result = + rt_timer_control(&thread->thread_timer, + RT_TIMER_CTRL_SET_TIME, + &timeout); + if (mutex_delete_restart_result == RT_EOK) + { + mutex_delete_restart_result = rt_timer_start(&thread->thread_timer); + } + rt_sem_release(&mutex_delete_restart_done); +} + +/* Wait until the mutex waiter enters the suspend list. */ +static void mutex_delete_wait_until_suspended(rt_thread_t thread) +{ + rt_sched_lock_level_t slvl; + + for (;;) + { + rt_sched_lock(&slvl); + if ((RT_SCHED_CTX(thread).stat & RT_THREAD_SUSPEND_MASK) == + RT_THREAD_SUSPEND_MASK) + { + rt_sched_unlock(slvl); + break; + } + rt_sched_unlock(slvl); + rt_thread_delay(1); + } +} + +/* Wait until timeout cleanup is complete while keeping the waiter READY. */ +static rt_bool_t mutex_delete_wait_until_timeout(rt_thread_t thread) +{ + rt_tick_t start; + rt_tick_t timeout = rt_tick_from_millisecond(1000); + + start = rt_tick_get(); + for (;;) + { + rt_sched_lock_level_t slvl; + rt_bool_t ready; + rt_bool_t cleaned; + + rt_sched_lock(&slvl); + ready = ((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK) == + RT_THREAD_READY); + cleaned = ((thread->pending_object == RT_NULL) && + (thread->error == -RT_ETIMEOUT)); + rt_sched_unlock(slvl); + + if (ready && cleaned) + { + return RT_TRUE; + } + + if ((rt_tick_get() - start) >= timeout) + { + return RT_FALSE; + } + + /* The controller has higher priority than the waiter. */ + rt_thread_yield(); + } +} + +/* Verify that deleted mutex waiters do not access stale objects. */ +static void test_mutex_delete_waiter(void) +{ + mutex_delete_wait_timeout = RT_WAITING_FOREVER; + mutex_delete_dynamic = rt_mutex_create("delmtx", RT_IPC_FLAG_PRIO); + uassert_true(mutex_delete_dynamic != RT_NULL); + uassert_int_equal(rt_mutex_take(mutex_delete_dynamic, + RT_WAITING_FOREVER), + RT_EOK); + + mutex_delete_wait_result = -RT_ERROR; + + mutex_delete_waiter = rt_thread_create("mtxwait", + mutex_delete_waiter_entry, + mutex_delete_dynamic, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 10); + uassert_true(mutex_delete_waiter != RT_NULL); + mutex_delete_bind_waiter(mutex_delete_waiter); + uassert_int_equal(rt_thread_startup(mutex_delete_waiter), RT_EOK); + + uassert_int_equal(rt_sem_take(&mutex_delete_ready, + RT_WAITING_FOREVER), + RT_EOK); + mutex_delete_wait_until_suspended(mutex_delete_waiter); + + uassert_int_equal(rt_mutex_delete(mutex_delete_dynamic), RT_EOK); + mutex_delete_dynamic = RT_NULL; + + mutex_delete_replacement_memory = + rt_malloc(sizeof(struct rt_mutex)); + uassert_true(mutex_delete_replacement_memory != RT_NULL); + if (mutex_delete_replacement_memory == + (void *)mutex_delete_waiter->parameter) + { + rt_memset(mutex_delete_replacement_memory, + 0xA5, + sizeof(struct rt_mutex)); + } + + uassert_int_equal(rt_sem_take(&mutex_delete_done, + rt_tick_from_millisecond(1000)), + RT_EOK); + uassert_int_equal(mutex_delete_wait_result, -RT_ERROR); + rt_free(mutex_delete_replacement_memory); + mutex_delete_replacement_memory = RT_NULL; + + uassert_int_equal(rt_mutex_init(&mutex_delete_static, + "detmtx", + RT_IPC_FLAG_PRIO), + RT_EOK); + uassert_int_equal(rt_mutex_take(&mutex_delete_static, + RT_WAITING_FOREVER), + RT_EOK); + + mutex_delete_waiter = rt_thread_create("mtxwait2", + mutex_delete_waiter_entry, + &mutex_delete_static, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 10); + uassert_true(mutex_delete_waiter != RT_NULL); + mutex_delete_bind_waiter(mutex_delete_waiter); + uassert_int_equal(rt_thread_startup(mutex_delete_waiter), RT_EOK); + + uassert_int_equal(rt_sem_take(&mutex_delete_ready, + RT_WAITING_FOREVER), + RT_EOK); + mutex_delete_wait_until_suspended(mutex_delete_waiter); + + uassert_int_equal(rt_mutex_detach(&mutex_delete_static), RT_EOK); + rt_memset(&mutex_delete_static, 0xA5, sizeof(mutex_delete_static)); + + uassert_int_equal(rt_sem_take(&mutex_delete_done, + rt_tick_from_millisecond(1000)), + RT_EOK); + uassert_int_equal(mutex_delete_wait_result, -RT_ERROR); +} + +/* Verify a timed out waiter does not access a detached mutex. */ +static void test_mutex_timeout_delete_waiter(void) +{ + rt_sched_lock_level_t slvl; + rt_thread_t waiter; + rt_thread_t restart_thread; + rt_bool_t suspended; + rt_err_t stop_result; + + mutex_delete_wait_timeout = rt_tick_from_millisecond(1000); + mutex_delete_restart_result = -RT_ERROR; + uassert_int_equal(rt_mutex_init(&mutex_delete_static, + "timemtx", + RT_IPC_FLAG_PRIO), + RT_EOK); + uassert_int_equal(rt_mutex_take(&mutex_delete_static, + RT_WAITING_FOREVER), + RT_EOK); + + mutex_delete_waiter = rt_thread_create("mtxwait3", + mutex_delete_waiter_entry, + &mutex_delete_static, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 10); + uassert_true(mutex_delete_waiter != RT_NULL); + waiter = mutex_delete_waiter; + mutex_delete_bind_waiter(waiter); + uassert_int_equal(rt_thread_startup(waiter), RT_EOK); + + uassert_int_equal(rt_sem_take(&mutex_delete_ready, + RT_WAITING_FOREVER), + RT_EOK); + mutex_delete_wait_until_suspended(waiter); + + /* Stop the timer without clearing the scheduler timer ownership flag. */ + rt_sched_lock(&slvl); + suspended = rt_sched_thread_is_suspended(waiter); + stop_result = rt_timer_stop(&waiter->thread_timer); + rt_sched_unlock(slvl); + uassert_true(suspended); + uassert_int_equal(stop_result, RT_EOK); + + restart_thread = rt_thread_create("mtxrst2", + mutex_delete_restart_timeout_entry, + waiter, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 3, + 10); + uassert_true(restart_thread != RT_NULL); + mutex_delete_bind_waiter(restart_thread); + uassert_int_equal(rt_thread_startup(restart_thread), RT_EOK); + uassert_int_equal(rt_sem_release(&mutex_delete_restart), RT_EOK); + + /* Delete owns bookkeeping; the timeout callback still owns wakeup. */ + uassert_int_equal(rt_mutex_detach(&mutex_delete_static), RT_EOK); + rt_sched_lock(&slvl); + suspended = rt_sched_thread_is_suspended(waiter); + rt_sched_unlock(slvl); + uassert_true(suspended); + uassert_true(waiter->pending_object == RT_NULL); + + /* Make a stale mutex dereference fail deterministically. */ + rt_memset(&mutex_delete_static, 0xA5, sizeof(mutex_delete_static)); + + uassert_int_equal(rt_sem_take(&mutex_delete_restart_done, + rt_tick_from_millisecond(1000)), + RT_EOK); + uassert_int_equal(mutex_delete_restart_result, RT_EOK); + uassert_int_equal(rt_sem_take(&mutex_delete_done, + rt_tick_from_millisecond(1000)), + RT_EOK); + uassert_int_equal(mutex_delete_wait_result, -RT_ETIMEOUT); + mutex_delete_wait_timeout = RT_WAITING_FOREVER; +} + +/* Verify a completed timeout cannot leave a stale mutex reference in READY. */ +static void test_mutex_timeout_ready_delete_waiter(void) +{ + rt_sched_lock_level_t slvl; + rt_thread_t waiter; + rt_bool_t ready; + rt_bool_t cleaned; + + mutex_delete_wait_timeout = rt_tick_from_millisecond(20); + mutex_delete_wait_result = -RT_ERROR; + uassert_int_equal(rt_mutex_init(&mutex_delete_static, + "rdymtx", + RT_IPC_FLAG_PRIO), + RT_EOK); + uassert_int_equal(rt_mutex_take(&mutex_delete_static, + RT_WAITING_FOREVER), + RT_EOK); + + waiter = rt_thread_create("mtxwait4", + mutex_delete_waiter_entry, + &mutex_delete_static, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 10); + uassert_true(waiter != RT_NULL); + mutex_delete_bind_waiter(waiter); + uassert_int_equal(rt_thread_startup(waiter), RT_EOK); + uassert_int_equal(rt_sem_take(&mutex_delete_ready, + RT_WAITING_FOREVER), + RT_EOK); + mutex_delete_wait_until_suspended(waiter); + + /* The high-priority controller keeps the timed-out waiter from running. */ + uassert_true(mutex_delete_wait_until_timeout(waiter)); + rt_sched_lock(&slvl); + ready = ((RT_SCHED_CTX(waiter).stat & RT_THREAD_STAT_MASK) == + RT_THREAD_READY); + cleaned = ((waiter->pending_object == RT_NULL) && + (waiter->error == -RT_ETIMEOUT)); + rt_sched_unlock(slvl); + uassert_true(ready); + uassert_true(cleaned); + + uassert_int_equal(rt_mutex_detach(&mutex_delete_static), RT_EOK); + rt_memset(&mutex_delete_static, 0xA5, sizeof(mutex_delete_static)); + + uassert_int_equal(rt_sem_take(&mutex_delete_done, + rt_tick_from_millisecond(1000)), + RT_EOK); + uassert_int_equal(mutex_delete_wait_result, -RT_ETIMEOUT); + mutex_delete_wait_timeout = RT_WAITING_FOREVER; +} + +/* Verify a timed out head waiter does not starve later mutex waiters. */ +static void test_mutex_release_timeout_waiter(void) +{ + rt_sched_lock_level_t slvl; + rt_thread_t timeout_waiter; + rt_thread_t next_waiter; + rt_thread_t restart_thread; + rt_bool_t suspended; + rt_err_t stop_result; + + mutex_delete_wait_timeout = rt_tick_from_millisecond(1000); + mutex_delete_wait_result = -RT_ERROR; + mutex_delete_next_wait_result = -RT_ERROR; + mutex_delete_restart_result = -RT_ERROR; + + uassert_int_equal(rt_mutex_init(&mutex_delete_static, + "relmtx", + RT_IPC_FLAG_PRIO), + RT_EOK); + uassert_int_equal(rt_mutex_take(&mutex_delete_static, + RT_WAITING_FOREVER), + RT_EOK); + + timeout_waiter = rt_thread_create("mtxwait5", + mutex_delete_waiter_entry, + &mutex_delete_static, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 1, + 10); + uassert_true(timeout_waiter != RT_NULL); + mutex_delete_bind_waiter(timeout_waiter); + uassert_int_equal(rt_thread_startup(timeout_waiter), RT_EOK); + uassert_int_equal(rt_sem_take(&mutex_delete_ready, + RT_WAITING_FOREVER), + RT_EOK); + mutex_delete_wait_until_suspended(timeout_waiter); + + next_waiter = rt_thread_create("mtxwait6", + mutex_delete_next_waiter_entry, + &mutex_delete_static, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 2, + 10); + uassert_true(next_waiter != RT_NULL); + mutex_delete_bind_waiter(next_waiter); + uassert_int_equal(rt_thread_startup(next_waiter), RT_EOK); + uassert_int_equal(rt_sem_take(&mutex_delete_ready, + RT_WAITING_FOREVER), + RT_EOK); + mutex_delete_wait_until_suspended(next_waiter); + + /* Stop the timer without clearing the scheduler timer ownership flag. */ + rt_sched_lock(&slvl); + suspended = rt_sched_thread_is_suspended(timeout_waiter); + stop_result = rt_timer_stop(&timeout_waiter->thread_timer); + rt_sched_unlock(slvl); + uassert_true(suspended); + uassert_int_equal(stop_result, RT_EOK); + + restart_thread = rt_thread_create("mtxrst", + mutex_delete_restart_timeout_entry, + timeout_waiter, + UTEST_THR_STACK_SIZE, + UTEST_THR_PRIORITY + 3, + 10); + uassert_true(restart_thread != RT_NULL); + mutex_delete_bind_waiter(restart_thread); + uassert_int_equal(rt_thread_startup(restart_thread), RT_EOK); + uassert_int_equal(rt_sem_release(&mutex_delete_restart), RT_EOK); + + uassert_int_equal(rt_mutex_release(&mutex_delete_static), RT_EOK); + uassert_int_equal(rt_sem_take(&mutex_delete_restart_done, + rt_tick_from_millisecond(1000)), + RT_EOK); + uassert_int_equal(mutex_delete_restart_result, RT_EOK); + + uassert_int_equal(rt_sem_take(&mutex_delete_done, + rt_tick_from_millisecond(1000)), + RT_EOK); + uassert_int_equal(rt_sem_take(&mutex_delete_done, + rt_tick_from_millisecond(1000)), + RT_EOK); + uassert_int_equal(mutex_delete_wait_result, -RT_ETIMEOUT); + uassert_int_equal(mutex_delete_next_wait_result, RT_EOK); + uassert_int_equal(rt_mutex_detach(&mutex_delete_static), RT_EOK); + mutex_delete_wait_timeout = RT_WAITING_FOREVER; +} + +#endif /* RT_USING_HEAP */ + static rt_err_t utest_tc_init(void) { #ifdef RT_USING_HEAP + rt_err_t result; + dynamic_mutex = RT_NULL; + mutex_delete_dynamic = RT_NULL; + mutex_delete_replacement_memory = RT_NULL; + mutex_delete_wait_timeout = RT_WAITING_FOREVER; + mutex_delete_semaphores_initialized = RT_FALSE; + + result = rt_sem_init(&mutex_delete_ready, + "mtxready", + 0, + RT_IPC_FLAG_FIFO); + if (result != RT_EOK) + { + return result; + } + + result = rt_sem_init(&mutex_delete_done, + "mtxdone", + 0, + RT_IPC_FLAG_FIFO); + if (result != RT_EOK) + { + rt_sem_detach(&mutex_delete_ready); + return result; + } + + result = rt_sem_init(&mutex_delete_restart, + "mtxrst", + 0, + RT_IPC_FLAG_FIFO); + if (result != RT_EOK) + { + rt_sem_detach(&mutex_delete_ready); + rt_sem_detach(&mutex_delete_done); + return result; + } + + result = rt_sem_init(&mutex_delete_restart_done, + "mtxrdone", + 0, + RT_IPC_FLAG_FIFO); + if (result != RT_EOK) + { + rt_sem_detach(&mutex_delete_ready); + rt_sem_detach(&mutex_delete_done); + rt_sem_detach(&mutex_delete_restart); + return result; + } + + mutex_delete_semaphores_initialized = RT_TRUE; #endif /* RT_USING_HEAP */ return RT_EOK; @@ -916,6 +1388,14 @@ static rt_err_t utest_tc_init(void) static rt_err_t utest_tc_cleanup(void) { #ifdef RT_USING_HEAP + if (mutex_delete_semaphores_initialized) + { + rt_sem_detach(&mutex_delete_ready); + rt_sem_detach(&mutex_delete_done); + rt_sem_detach(&mutex_delete_restart); + rt_sem_detach(&mutex_delete_restart_done); + mutex_delete_semaphores_initialized = RT_FALSE; + } dynamic_mutex = RT_NULL; #endif /* RT_USING_HEAP */ @@ -936,6 +1416,10 @@ static void testcase(void) UTEST_UNIT_RUN(test_dynamic_mutex_trytake); UTEST_UNIT_RUN(test_dynamic_pri_reverse); UTEST_UNIT_RUN(test_cross_thread_delete_mutex_owner); + UTEST_UNIT_RUN(test_mutex_delete_waiter); + UTEST_UNIT_RUN(test_mutex_timeout_delete_waiter); + UTEST_UNIT_RUN(test_mutex_timeout_ready_delete_waiter); + UTEST_UNIT_RUN(test_mutex_release_timeout_waiter); #endif UTEST_UNIT_RUN(test_recurse_lock); }