diff --git a/components/finsh/cmd.c b/components/finsh/cmd.c index 799d7b3d51..137ca3acff 100644 --- a/components/finsh/cmd.c +++ b/components/finsh/cmd.c @@ -411,9 +411,9 @@ long list_mutex(void) maxlen = RT_NAME_MAX; - rt_kprintf("%-*.s owner hold suspend thread\n", maxlen, item_title); + rt_kprintf("%-*.s owner hold suspend thread priority\n", maxlen, item_title); object_split(maxlen); - rt_kprintf(" -------- ---- --------------\n"); + rt_kprintf(" -------- ---- -------------- --------\n"); do { @@ -436,13 +436,14 @@ long list_mutex(void) rt_hw_interrupt_enable(level); m = (struct rt_mutex *)obj; - rt_kprintf("%-*.*s %-8.*s %04d %d\n", + rt_kprintf("%-*.*s %-8.*s %04d %d %d\n", maxlen, RT_NAME_MAX, m->parent.parent.name, RT_NAME_MAX, m->owner->name, m->hold, - rt_list_len(&m->parent.suspend_thread)); + rt_list_len(&m->parent.suspend_thread), + m->priority); } } diff --git a/include/rtdef.h b/include/rtdef.h index 4a13c554a5..183d80a154 100644 --- a/include/rtdef.h +++ b/include/rtdef.h @@ -673,12 +673,19 @@ struct rt_thread /* priority */ rt_uint8_t current_priority; /**< current priority */ + rt_uint8_t init_priority; /**< initialized priority */ #if RT_THREAD_PRIORITY_MAX > 32 rt_uint8_t number; rt_uint8_t high_mask; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ rt_uint32_t number_mask; +#ifdef RT_USING_MUTEX + /* object for IPC */ + rt_list_t taken_object_list; + rt_object_t pending_object; +#endif + #ifdef RT_USING_EVENT /* thread event */ rt_uint32_t event_set; @@ -772,12 +779,13 @@ struct rt_mutex { struct rt_ipc_object parent; /**< inherit from ipc_object */ - rt_uint16_t value; /**< value of mutex */ - - rt_uint8_t original_priority; /**< priority of last thread hold the mutex */ + rt_uint8_t ceiling_priority; /**< the priority ceiling of mutexe */ + rt_uint8_t priority; /**< the maximal priority for pending thread */ rt_uint8_t hold; /**< numbers of thread hold the mutex */ + rt_uint8_t reserved; /**< reserved field */ struct rt_thread *owner; /**< current owner of mutex */ + rt_list_t taken_list; /**< the object list taken by thread */ }; typedef struct rt_mutex *rt_mutex_t; #endif /* RT_USING_MUTEX */ diff --git a/include/rtthread.h b/include/rtthread.h index bdd52e8fbb..0360a72972 100644 --- a/include/rtthread.h +++ b/include/rtthread.h @@ -368,6 +368,9 @@ rt_err_t rt_mutex_detach(rt_mutex_t mutex); rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag); rt_err_t rt_mutex_delete(rt_mutex_t mutex); #endif +void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread); +rt_uint8_t rt_mutex_setprioceiling(rt_mutex_t mutex, rt_uint8_t priority); +rt_uint8_t rt_mutex_getprioceiling(rt_mutex_t mutex); rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout); rt_err_t rt_mutex_trytake(rt_mutex_t mutex); diff --git a/src/ipc.c b/src/ipc.c index 329fc7f4cf..15853ff463 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -42,6 +42,8 @@ * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to ipc.c * 2022-01-24 THEWON let rt_mutex_take return thread->error when using signal * 2022-04-08 Stanley Correct descriptions + * 2022-10-15 Bernard add nested mutex feature + * 2022-10-16 Bernard add prioceiling feature in mutex */ #include @@ -708,6 +710,83 @@ RTM_EXPORT(rt_sem_control); #endif /* RT_USING_SEMAPHORE */ #ifdef RT_USING_MUTEX +rt_inline rt_uint8_t _mutex_update_priority(struct rt_mutex *mutex) +{ + struct rt_thread *thread; + + if (!rt_list_isempty(&mutex->parent.suspend_thread)) + { + thread = rt_list_entry(mutex->parent.suspend_thread.next, struct rt_thread, tlist); + mutex->priority = thread->current_priority; + } + else + { + mutex->priority = 0xff; + } + + return mutex->priority; +} + +rt_inline rt_uint8_t _thread_get_mutex_priority(struct rt_thread* thread) +{ + rt_list_t *node = RT_NULL; + struct rt_mutex *mutex = RT_NULL; + rt_uint8_t priority = thread->init_priority; + + rt_list_for_each(node, &(thread->taken_object_list)) + { + mutex = rt_list_entry(node, struct rt_mutex, taken_list); + if (priority > mutex->priority) + { + priority = mutex->priority; + } + } + + return priority; +} + +rt_inline void _thread_update_priority(struct rt_thread *thread, rt_uint8_t priority) +{ + RT_DEBUG_LOG(RT_DEBUG_IPC, + ("thread:%s priority -> %d\n", thread->name, priority)); + + /* change priority of the thread */ + rt_thread_control(thread, + RT_THREAD_CTRL_CHANGE_PRIORITY, + &priority); + + if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND) + { + /* whether change the priority of taken mutex */ + struct rt_object* pending_obj = thread->pending_object; + + if (pending_obj && rt_object_get_type(pending_obj) == RT_Object_Class_Mutex) + { + rt_uint8_t mutex_priority; + struct rt_mutex* pending_mutex = (struct rt_mutex *)pending_obj; + + /* re-insert thread to suspended thread list */ + rt_list_remove(&(thread->tlist)); + _ipc_list_suspend(&(pending_mutex->parent.suspend_thread), + thread, + pending_mutex->parent.parent.flag); + + /* update priority */ + _mutex_update_priority(pending_mutex); + /* change the priority of mutex owner thread */ + RT_DEBUG_LOG(RT_DEBUG_IPC, + ("mutex: %s priority -> %d\n", pending_mutex->parent.parent.name, + pending_mutex->priority)); + + mutex_priority = _thread_get_mutex_priority(pending_mutex->owner); + if (mutex_priority != pending_mutex->owner->current_priority) + { + _thread_update_priority(pending_mutex->owner, mutex_priority); + } + } + } +} + /** * @addtogroup mutex */ @@ -752,10 +831,11 @@ rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag) /* initialize ipc object */ _ipc_object_init(&(mutex->parent)); - mutex->value = 1; - mutex->owner = RT_NULL; - mutex->original_priority = 0xFF; - mutex->hold = 0; + mutex->owner = RT_NULL; + mutex->priority = 0xFF; + mutex->hold = 0; + mutex->ceiling_priority = 0xFF; + rt_list_init(&(mutex->taken_list)); /* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */ mutex->parent.parent.flag = RT_IPC_FLAG_PRIO; @@ -785,13 +865,19 @@ RTM_EXPORT(rt_mutex_init); */ rt_err_t rt_mutex_detach(rt_mutex_t mutex) { + rt_ubase_t level; + /* parameter check */ RT_ASSERT(mutex != RT_NULL); RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent)); + level = rt_hw_interrupt_disable(); /* wakeup all suspended threads */ _ipc_list_resume_all(&(mutex->parent.suspend_thread)); + /* remove mutex from thread's taken list */ + rt_list_remove(&mutex->taken_list); + rt_hw_interrupt_enable(level); /* detach mutex object */ rt_object_detach(&(mutex->parent.parent)); @@ -800,6 +886,104 @@ rt_err_t rt_mutex_detach(rt_mutex_t mutex) } RTM_EXPORT(rt_mutex_detach); +/* drop a thread from the suspend list of mutex */ + +/** + * @brief drop a thread from the suspend list of mutex + * + * @param mutex is a pointer to a mutex object. + * @param thread is the thread should be dropped from mutex. + */ +void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread) +{ + rt_uint8_t priority; + rt_bool_t need_update = RT_FALSE; + + rt_list_remove(&(thread->tlist)); + + /* should change the priority of mutex owner thread */ + if (mutex->owner->current_priority == thread->current_priority) + need_update = RT_TRUE; + + /* update the priority of mutex */ + if (!rt_list_isempty(&mutex->parent.suspend_thread)) + { + /* more thread suspended in the list */ + struct rt_thread *th; + + th = rt_list_entry(mutex->parent.suspend_thread.next, + struct rt_thread, + tlist); + /* update the priority of mutex */ + mutex->priority = th->current_priority; + } + else + { + /* set mutex priority to maximal priority */ + mutex->priority = 0xff; + } + + /* try to change the priority of mutex owner thread */ + if (need_update) + { + /* get the maximal priority of mutex in thread */ + priority = _thread_get_mutex_priority(mutex->owner); + if (priority != mutex->owner->current_priority) + { + _thread_update_priority(mutex->owner, priority); + } + } +} + + +/** + * @brief set the prioceiling attribute of the mutex. + * + * @param mutex is a pointer to a mutex object. + * @param priority is the priority should be set to mutex. + * + * @return return the old priority ceiling + */ +rt_uint8_t rt_mutex_setprioceiling(rt_mutex_t mutex, rt_uint8_t priority) +{ + rt_uint8_t ret_priority = 0xFF; + + if ((mutex) && (priority < RT_THREAD_PRIORITY_MAX)) + { + ret_priority = mutex->ceiling_priority; + mutex->ceiling_priority = priority; + } + else + { + rt_set_errno(-RT_EINVAL); + } + + return ret_priority; +} +RTM_EXPORT(rt_mutex_setprioceiling); + + +/** + * @brief set the prioceiling attribute of the mutex. + * + * @param mutex is a pointer to a mutex object. + * + * @return return the current priority ceiling of the mutex. + */ +rt_uint8_t rt_mutex_getprioceiling(rt_mutex_t mutex) +{ + rt_uint8_t prio = 0xFF; + + if (mutex) + { + prio = mutex->ceiling_priority; + } + + return prio; +} +RTM_EXPORT(rt_mutex_getprioceiling); + + #ifdef RT_USING_HEAP /** * @brief This function will create a mutex object. @@ -836,10 +1020,11 @@ rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag) /* initialize ipc object */ _ipc_object_init(&(mutex->parent)); - mutex->value = 1; - mutex->owner = RT_NULL; - mutex->original_priority = 0xFF; - mutex->hold = 0; + mutex->owner = RT_NULL; + mutex->priority = 0xFF; + mutex->hold = 0; + mutex->ceiling_priority = 0xFF; + rt_list_init(&(mutex->taken_list)); /* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */ mutex->parent.parent.flag = RT_IPC_FLAG_PRIO; @@ -869,6 +1054,8 @@ RTM_EXPORT(rt_mutex_create); */ rt_err_t rt_mutex_delete(rt_mutex_t mutex) { + rt_ubase_t level; + /* parameter check */ RT_ASSERT(mutex != RT_NULL); RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); @@ -876,8 +1063,12 @@ rt_err_t rt_mutex_delete(rt_mutex_t mutex) RT_DEBUG_NOT_IN_INTERRUPT; + level = rt_hw_interrupt_disable(); /* wakeup all suspended threads */ _ipc_list_resume_all(&(mutex->parent.suspend_thread)); + /* remove mutex from thread's taken list */ + rt_list_remove(&mutex->taken_list); + rt_hw_interrupt_enable(level); /* delete mutex object */ rt_object_delete(&(mutex->parent.parent)); @@ -933,8 +1124,8 @@ rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout) RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent))); RT_DEBUG_LOG(RT_DEBUG_IPC, - ("mutex_take: current thread %s, mutex value: %d, hold: %d\n", - thread->name, mutex->value, mutex->hold)); + ("mutex_take: current thread %s, hold: %d\n", + thread->name, mutex->hold)); /* reset thread error */ thread->error = RT_EOK; @@ -954,25 +1145,24 @@ rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout) } else { - /* The value of mutex is 1 in initial status. Therefore, if the - * value is great than 0, it indicates the mutex is avaible. - */ - if (mutex->value > 0) + /* whether the mutex has owner thread. */ + if (mutex->owner == RT_NULL) { - /* mutex is available */ - mutex->value --; - /* set mutex owner and original priority */ - mutex->owner = thread; - mutex->original_priority = thread->current_priority; - if(mutex->hold < RT_MUTEX_HOLD_MAX) + mutex->owner = thread; + mutex->priority = 0xff; + mutex->hold = 1; + + if (mutex->ceiling_priority != 0xFF) { - mutex->hold ++; + /* set the priority of thread to the ceiling priority */ + if (mutex->ceiling_priority < mutex->owner->current_priority) + _thread_update_priority(mutex->owner, mutex->ceiling_priority); } else { - rt_hw_interrupt_enable(level); /* enable interrupt */ - return -RT_EFULL; /* value overflowed */ + /* insert mutex to thread's taken object list */ + rt_list_insert_after(&thread->taken_object_list, &mutex->taken_list); } } else @@ -990,23 +1180,28 @@ rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout) } else { + rt_uint8_t priority = thread->current_priority; + /* mutex is unavailable, push to suspend list */ RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_take: suspend thread: %s\n", thread->name)); - /* change the owner thread priority of mutex */ - if (thread->current_priority < mutex->owner->current_priority) - { - /* change the owner thread priority */ - rt_thread_control(mutex->owner, - RT_THREAD_CTRL_CHANGE_PRIORITY, - &thread->current_priority); - } - /* suspend current thread */ _ipc_list_suspend(&(mutex->parent.suspend_thread), thread, mutex->parent.parent.flag); + /* set pending object in thread to this mutex */ + thread->pending_object = &(mutex->parent.parent); + + /* update the priority level of mutex */ + if (priority < mutex->priority) + { + mutex->priority = priority; + if (mutex->priority < mutex->owner->current_priority) + { + _thread_update_priority(mutex->owner, priority); + } + } /* has waiting time, start thread timer */ if (timeout > 0) @@ -1028,16 +1223,57 @@ rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout) /* do schedule */ rt_schedule(); - if (thread->error != RT_EOK) + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + if (thread->error == RT_EOK) { - /* return error */ - return thread->error; + /* get mutex successfully */ } else { - /* the mutex is taken successfully. */ - /* disable interrupt */ - level = rt_hw_interrupt_disable(); + /* the mutex has not been taken and thread has detach from the pending list. */ + + rt_bool_t need_update = RT_FALSE; + + /* should change the priority of mutex owner thread */ + if (mutex->owner->current_priority == thread->current_priority) + need_update = RT_TRUE; + + /* update the priority of mutex */ + if (!rt_list_isempty(&mutex->parent.suspend_thread)) + { + /* more thread suspended in the list */ + struct rt_thread *th; + + th = rt_list_entry(mutex->parent.suspend_thread.next, + struct rt_thread, + tlist); + /* update the priority of mutex */ + mutex->priority = th->current_priority; + } + else + { + /* set mutex priority to maximal priority */ + mutex->priority = 0xff; + } + + /* try to change the priority of mutex owner thread */ + if (need_update) + { + /* get the maximal priority of mutex in thread */ + priority = _thread_get_mutex_priority(mutex->owner); + if (priority != mutex->owner->current_priority) + { + _thread_update_priority(mutex->owner, priority); + } + } + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + /* return error */ + return thread->error; } } } @@ -1109,8 +1345,8 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) level = rt_hw_interrupt_disable(); RT_DEBUG_LOG(RT_DEBUG_IPC, - ("mutex_release:current thread %s, mutex value: %d, hold: %d\n", - thread->name, mutex->value, mutex->hold)); + ("mutex_release:current thread %s, hold: %d\n", + thread->name, mutex->hold)); RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mutex->parent.parent))); @@ -1130,60 +1366,70 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) /* if no hold */ if (mutex->hold == 0) { - /* change the owner thread to original priority */ - if (mutex->original_priority != mutex->owner->current_priority) + /* remove mutex from thread's taken list */ + rt_list_remove(&mutex->taken_list); + + /* whether change the thread priority */ + if ((mutex->ceiling_priority != 0xFF) || (thread->current_priority == mutex->priority)) { - rt_thread_control(mutex->owner, + rt_uint8_t priority = 0xff; + + /* get the highest priority in the taken list of thread */ + priority = _thread_get_mutex_priority(thread); + + rt_thread_control(thread, RT_THREAD_CTRL_CHANGE_PRIORITY, - &(mutex->original_priority)); + &priority); + + need_schedule = RT_TRUE; } /* wakeup suspended thread */ if (!rt_list_isempty(&mutex->parent.suspend_thread)) { - /* get suspended thread */ - thread = rt_list_entry(mutex->parent.suspend_thread.next, + /* get the first suspended thread */ + struct rt_thread *next_thread = rt_list_entry(mutex->parent.suspend_thread.next, struct rt_thread, tlist); RT_DEBUG_LOG(RT_DEBUG_IPC, ("mutex_release: resume thread: %s\n", - thread->name)); + next_thread->name)); - /* set new owner and priority */ - mutex->owner = thread; - mutex->original_priority = thread->current_priority; + /* remove the thread from the suspended list of mutex */ + rt_list_remove(&(next_thread->tlist)); - if(mutex->hold < RT_MUTEX_HOLD_MAX) + /* set new owner and put mutex into taken list of thread */ + mutex->owner = next_thread; + mutex->hold = 1; + rt_list_insert_after(&next_thread->taken_object_list, &mutex->taken_list); + /* cleanup pending object */ + next_thread->pending_object = RT_NULL; + + /* resume thread */ + rt_thread_resume(next_thread); + + /* update mutex priority */ + if (!rt_list_isempty(&(mutex->parent.suspend_thread))) { - mutex->hold ++; + struct rt_thread *th; + + th = rt_list_entry(mutex->parent.suspend_thread.next, + struct rt_thread, + tlist); + mutex->priority = th->current_priority; } else { - rt_hw_interrupt_enable(level); /* enable interrupt */ - return -RT_EFULL; /* value overflowed */ + mutex->priority = 0xff; } - /* resume thread */ - _ipc_list_resume(&(mutex->parent.suspend_thread)); - need_schedule = RT_TRUE; } else { - if(mutex->value < RT_MUTEX_VALUE_MAX) - { - /* increase value */ - mutex->value ++; - } - else - { - rt_hw_interrupt_enable(level); /* enable interrupt */ - return -RT_EFULL; /* value overflowed */ - } - /* clear owner */ - mutex->owner = RT_NULL; - mutex->original_priority = 0xff; + mutex->owner = RT_NULL; + mutex->priority = 0xff; } } diff --git a/src/thread.c b/src/thread.c index df6de24275..9d37861c5a 100644 --- a/src/thread.c +++ b/src/thread.c @@ -31,6 +31,7 @@ * 2021-12-27 Meco Man remove .init_priority * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to thread.c * 2022-01-24 THEWON let rt_thread_sleep return thread->error when using signal + * 2022-10-15 Bernard add nested mutex feature */ #include @@ -187,10 +188,16 @@ static rt_err_t _thread_init(struct rt_thread *thread, /* priority init */ RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); + thread->init_priority = priority; thread->current_priority = priority; thread->number_mask = 0; +#ifdef RT_USING_MUTEX + rt_list_init(&thread->taken_object_list); + thread->pending_object = RT_NULL; +#endif + #ifdef RT_USING_EVENT thread->event_set = 0; thread->event_info = 0; @@ -420,6 +427,16 @@ rt_err_t rt_thread_detach(rt_thread_t thread) /* change stat */ thread->stat = RT_THREAD_CLOSE; +#ifdef RT_USING_MUTEX + if ((thread->pending_object) && + (rt_object_get_type(thread->pending_object) == RT_Object_Class_Mutex)) + { + struct rt_mutex *mutex = (struct rt_mutex*)thread->pending_object; + rt_mutex_drop_thread(mutex, thread); + thread->pending_object = RT_NULL; + } +#endif + /* insert to defunct thread list */ rt_thread_defunct_enqueue(thread); @@ -523,6 +540,16 @@ rt_err_t rt_thread_delete(rt_thread_t thread) /* change stat */ thread->stat = RT_THREAD_CLOSE; +#ifdef RT_USING_MUTEX + if ((thread->pending_object) && + (rt_object_get_type(thread->pending_object) == RT_Object_Class_Mutex)) + { + struct rt_mutex *mutex = (struct rt_mutex*)thread->pending_object; + rt_mutex_drop_thread(mutex, thread); + thread->pending_object = RT_NULL; + } +#endif + /* insert to defunct thread list */ rt_thread_defunct_enqueue(thread);