Browse Source

add nested/prioceiling mutex feature(merge from rt-thread master branch)

liyansheng 2 years ago
parent
commit
4efa8efd90
4 changed files with 382 additions and 100 deletions
  1. 10 3
      include/rtdef.h
  2. 4 0
      include/rtthread.h
  3. 339 94
      src/ipc.c
  4. 29 3
      src/thread.c

+ 10 - 3
include/rtdef.h

@@ -698,6 +698,12 @@ struct rt_thread
 #endif
     rt_uint32_t number_mask;
 
+#ifdef RT_USING_MUTEX
+    /* object for IPC */
+    rt_list_t taken_object_list;
+    rt_object_t pending_object;
+#endif
+
 #if defined(RT_USING_EVENT)
     /* thread event */
     rt_uint32_t event_set;
@@ -819,12 +825,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

+ 4 - 0
include/rtthread.h

@@ -336,6 +336,10 @@ 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);
 
+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 time);
 rt_err_t rt_mutex_take_interruptible(rt_mutex_t mutex, rt_int32_t time);
 rt_err_t rt_mutex_take_killable(rt_mutex_t mutex, rt_int32_t time);

+ 339 - 94
src/ipc.c

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2018, RT-Thread Development Team
+ * Copyright (c) 2006-2022, RT-Thread Development Team
  *
  * SPDX-License-Identifier: Apache-2.0
  *
@@ -565,6 +565,83 @@ RTM_EXPORT(rt_sem_control);
 #endif /* end of 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, int suspend_flag)
+{
+    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));
+            rt_ipc_list_suspend(&(pending_mutex->parent.suspend_thread),
+                                thread,
+                                pending_mutex->parent.parent.flag,suspend_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, suspend_flag);
+            }
+        }
+    }
+}
+
 /**
  * This function will initialize a mutex and put it under control of resource
  * management.
@@ -575,7 +652,7 @@ RTM_EXPORT(rt_sem_control);
  *
  * @return the operation status, RT_EOK on successful
  */
-rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
+rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag RT_UNUSED)
 {
     /* parameter check */
     RT_ASSERT(mutex != RT_NULL);
@@ -586,13 +663,14 @@ rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag)
     /* initialize ipc object */
     rt_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));
 
-    /* set flag */
-    mutex->parent.parent.flag = flag;
+    /* 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;
 
     return RT_EOK;
 }
@@ -609,21 +687,124 @@ 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 */
     rt_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 semaphore object */
+    /* detach mutex object */
     rt_object_detach(&(mutex->parent.parent));
 
     return RT_EOK;
 }
 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,RT_UNINTERRUPTIBLE);
+        }
+    }
+}
+
+
+/**
+ * @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
 /**
  * This function will create a mutex from system resource
@@ -635,7 +816,7 @@ RTM_EXPORT(rt_mutex_detach);
  *
  * @see rt_mutex_init
  */
-rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)
+rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag RT_UNUSED)
 {
     struct rt_mutex *mutex;
 
@@ -649,13 +830,14 @@ rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag)
     /* initialize ipc object */
     rt_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));
 
-    /* set flag */
-    mutex->parent.parent.flag = flag;
+    /* 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;
 
     return mutex;
 }
@@ -672,15 +854,21 @@ RTM_EXPORT(rt_mutex_create);
  */
 rt_err_t rt_mutex_delete(rt_mutex_t mutex)
 {
-    RT_DEBUG_NOT_IN_INTERRUPT;
+    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) == RT_FALSE);
 
+    RT_DEBUG_NOT_IN_INTERRUPT;
+
+    level = rt_hw_interrupt_disable();
     /* wakeup all suspended threads */
     rt_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));
@@ -699,14 +887,15 @@ RTM_EXPORT(rt_mutex_delete);
  *
  * @return the error code
  */
-static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t time, int suspend_flag)
+static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend_flag)
 {
-    register rt_base_t temp;
+    rt_base_t level;
     struct rt_thread *thread;
     rt_err_t ret;
 
     /* this function must not be used in interrupt even if time = 0 */
-    RT_DEBUG_IN_THREAD_CONTEXT;
+    /* current context checking */
+    RT_DEBUG_NOT_IN_INTERRUPT;
 
     /* parameter check */
     RT_ASSERT(mutex != RT_NULL);
@@ -716,16 +905,16 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t time, int suspend_fl
     thread = rt_thread_self();
 
     /* disable interrupt */
-    temp = rt_hw_interrupt_disable();
+    level = rt_hw_interrupt_disable();
 
     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_EINTR;
+    thread->error = RT_EOK;
 
     if (mutex->owner == thread)
     {
@@ -736,61 +925,53 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t time, int suspend_fl
         }
         else
         {
-            rt_hw_interrupt_enable(temp); /* enable interrupt */
+            rt_hw_interrupt_enable(level); /* enable interrupt */
             return -RT_EFULL; /* value overflowed */
         }
     }
     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->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, suspend_flag);
             }
             else
             {
-                rt_hw_interrupt_enable(temp); /* 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
         {
             /* no waiting, return with timeout */
-            if (time == 0)
+            if (timeout == 0)
             {
                 /* set error as timeout */
                 thread->error = -RT_ETIMEOUT;
 
                 /* enable interrupt */
-                rt_hw_interrupt_enable(temp);
+                rt_hw_interrupt_enable(level);
 
                 return -RT_ETIMEOUT;
             }
             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 */
                 ret = rt_ipc_list_suspend(&(mutex->parent.suspend_thread),
                                     thread,
@@ -798,12 +979,24 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t time, int suspend_fl
                                     suspend_flag);
                 if (ret != RT_EOK)
                 {
-                    rt_hw_interrupt_enable(temp);
+                    rt_hw_interrupt_enable(level);
                     return ret;
                 }
+                /* 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, suspend_flag);
+                    }
+                }
 
                 /* has waiting time, start thread timer */
-                if (time > 0)
+                if (timeout > 0)
                 {
                     RT_DEBUG_LOG(RT_DEBUG_IPC,
                                  ("mutex_take: start the timer of thread:%s\n",
@@ -812,33 +1005,74 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t time, int suspend_fl
                     /* reset the timeout of thread timer and start it */
                     rt_timer_control(&(thread->thread_timer),
                                      RT_TIMER_CTRL_SET_TIME,
-                                     &time);
+                                     &timeout);
                     rt_timer_start(&(thread->thread_timer));
                 }
 
                 /* enable interrupt */
-                rt_hw_interrupt_enable(temp);
+                rt_hw_interrupt_enable(level);
 
                 /* 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 */
-                    temp = 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, suspend_flag);
+                        }
+                    }
+
+                    /* enable interrupt */
+                    rt_hw_interrupt_enable(level);
+
+                    /* return error */
+                    return thread->error;
                 }
             }
         }
     }
 
     /* enable interrupt */
-    rt_hw_interrupt_enable(temp);
+    rt_hw_interrupt_enable(level);
 
     RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mutex->parent.parent)));
 
@@ -873,7 +1107,7 @@ RTM_EXPORT(rt_mutex_take_killable);
  */
 rt_err_t rt_mutex_release(rt_mutex_t mutex)
 {
-    register rt_base_t temp;
+    rt_base_t level;
     struct rt_thread *thread;
     rt_bool_t need_schedule;
 
@@ -890,11 +1124,11 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex)
     thread = rt_thread_self();
 
     /* disable interrupt */
-    temp = rt_hw_interrupt_disable();
+    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)));
 
@@ -904,7 +1138,7 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex)
         thread->error = -RT_ERROR;
 
         /* enable interrupt */
-        rt_hw_interrupt_enable(temp);
+        rt_hw_interrupt_enable(level);
 
         return -RT_ERROR;
     }
@@ -914,64 +1148,75 @@ 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;
-            if(mutex->hold < RT_MUTEX_HOLD_MAX)
+            /* remove the thread from the suspended list of mutex */
+            rt_list_remove(&(next_thread->tlist));
+
+            /* 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(temp); /* enable interrupt */
-                return -RT_EFULL; /* value overflowed */
+                mutex->priority = 0xff;
             }
 
-            /* resume thread */
-            rt_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(temp); /* enable interrupt */
-                return -RT_EFULL; /* value overflowed */
-            }
-
             /* clear owner */
             mutex->owner             = RT_NULL;
-            mutex->original_priority = 0xff;
+            mutex->priority = 0xff;
         }
     }
 
     /* enable interrupt */
-    rt_hw_interrupt_enable(temp);
+    rt_hw_interrupt_enable(level);
 
     /* perform a schedule */
     if (need_schedule == RT_TRUE)

+ 29 - 3
src/thread.c

@@ -153,6 +153,12 @@ static rt_err_t _rt_thread_init(struct rt_thread *thread,
     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
+
 #if RT_THREAD_PRIORITY_MAX > 32
     thread->number = 0;
     thread->high_mask = 0;
@@ -361,6 +367,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
+
     if ((rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) &&
         thread->cleanup == RT_NULL)
     {
@@ -457,15 +473,25 @@ rt_err_t rt_thread_delete(rt_thread_t thread)
         rt_schedule_remove_thread(thread);
     }
 
-    /* release thread timer */
-    rt_timer_detach(&(thread->thread_timer));
-
     /* disable interrupt */
     lock = rt_hw_interrupt_disable();
 
+    /* release thread timer */
+    rt_timer_detach(&(thread->thread_timer));
+
     /* 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);