You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by gn...@apache.org on 2020/06/15 13:16:29 UTC

[incubator-nuttx] branch master updated (089a633 -> fd5fc2a)

This is an automated email from the ASF dual-hosted git repository.

gnutt pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git.


    from 089a633  Fix wrong space instead of TAB
     new 309dda3  sched: pthread_cleanup_[push|pop] should be callable from main thread
     new fd5fc2a  sched: Check the mutex consistent in main thread too

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 include/nuttx/sched.h                     | 34 ++++++-------
 sched/pthread/pthread.h                   |  4 +-
 sched/pthread/pthread_cancel.c            | 20 ++++----
 sched/pthread/pthread_cleanup.c           | 32 ++++--------
 sched/pthread/pthread_exit.c              |  7 +--
 sched/pthread/pthread_mutex.c             | 84 +++++++++++--------------------
 sched/pthread/pthread_mutexinconsistent.c |  4 +-
 sched/task/exit.c                         | 13 +++++
 8 files changed, 89 insertions(+), 109 deletions(-)


[incubator-nuttx] 01/02: sched: pthread_cleanup_[push|pop] should be callable from main thread

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 309dda3ef8954ceaaad43dd1ce1ee3a6d3c599b5
Author: Xiang Xiao <xi...@xiaomi.com>
AuthorDate: Thu Jun 4 02:11:04 2020 +0800

    sched: pthread_cleanup_[push|pop] should be callable from main thread
    
    Signed-off-by: Xiang Xiao <xi...@xiaomi.com>
    Change-Id: Ifefccda6cb7e2335e11976dcec74e308d64c7f5e
---
 include/nuttx/sched.h           | 22 +++++++++++-----------
 sched/pthread/pthread.h         |  2 +-
 sched/pthread/pthread_cancel.c  |  2 +-
 sched/pthread/pthread_cleanup.c | 32 +++++++++++---------------------
 sched/pthread/pthread_exit.c    |  5 +++--
 sched/task/exit.c               |  7 +++++++
 6 files changed, 34 insertions(+), 36 deletions(-)

diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index 8650a6d..0bc3d1e 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -714,6 +714,17 @@ struct tcb_s
   FAR struct mqueue_inode_s *msgwaitq;   /* Waiting for this message queue      */
 #endif
 
+  /* Clean-up stack *************************************************************/
+
+#ifdef CONFIG_PTHREAD_CLEANUP
+  /* tos   - The index to the next available entry at the top of the stack.
+   * stack - The pre-allocated clean-up stack memory.
+   */
+
+  uint8_t tos;
+  struct pthread_cleanup_s stack[CONFIG_PTHREAD_CLEANUP_STACKSIZE];
+#endif
+
   /* Pre-emption monitor support ************************************************/
 
 #ifdef CONFIG_SCHED_CRITMONITOR
@@ -791,17 +802,6 @@ struct pthread_tcb_s
 #ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
   FAR struct pthread_mutex_s *mhead;     /* List of mutexes held by thread      */
 #endif
-
-  /* Clean-up stack *************************************************************/
-
-#ifdef CONFIG_PTHREAD_CLEANUP
-  /* tos   - The index to the next available entry at the top of the stack.
-   * stack - The pre-allocated clean-up stack memory.
-   */
-
-  uint8_t tos;
-  struct pthread_cleanup_s stack[CONFIG_PTHREAD_CLEANUP_STACKSIZE];
-#endif
 };
 #endif /* !CONFIG_DISABLE_PTHREAD */
 
diff --git a/sched/pthread/pthread.h b/sched/pthread/pthread.h
index 3a15714..82c9bd8 100644
--- a/sched/pthread/pthread.h
+++ b/sched/pthread/pthread.h
@@ -99,7 +99,7 @@ int pthread_setup_scheduler(FAR struct pthread_tcb_s *tcb, int priority,
                             start_t start, pthread_startroutine_t entry);
 
 #ifdef CONFIG_PTHREAD_CLEANUP
-void pthread_cleanup_popall(FAR struct pthread_tcb_s *tcb);
+void pthread_cleanup_popall(FAR struct tcb_s *tcb);
 #endif
 
 int pthread_completejoin(pid_t pid, FAR void *exit_value);
diff --git a/sched/pthread/pthread_cancel.c b/sched/pthread/pthread_cancel.c
index b2e947d..ab64a4e 100644
--- a/sched/pthread/pthread_cancel.c
+++ b/sched/pthread/pthread_cancel.c
@@ -141,7 +141,7 @@ int pthread_cancel(pthread_t thread)
    * function will be unable to unlock its own mutexes.
    */
 
-  pthread_cleanup_popall(tcb);
+  pthread_cleanup_popall((FAR struct tcb_s *)tcb);
 #endif
 
   /* Complete pending join operations */
diff --git a/sched/pthread/pthread_cleanup.c b/sched/pthread/pthread_cleanup.c
index cbfc255..c22e67c 100644
--- a/sched/pthread/pthread_cleanup.c
+++ b/sched/pthread/pthread_cleanup.c
@@ -57,8 +57,8 @@
  * Name: pthread_cleanup_pop_tcb
  *
  * Description:
- *   The pthread_cleanup_pop_tcb() function will remove the routine at the top
- *   of the calling thread's cancellation cleanup stack and optionally
+ *   The pthread_cleanup_pop_tcb() function will remove the routine at the
+ *   top of the calling thread's cancellation cleanup stack and optionally
  *   invoke it (if 'execute' is non-zero).
  *
  * Input Parameters:
@@ -72,7 +72,7 @@
  *
  ****************************************************************************/
 
-static void pthread_cleanup_pop_tcb(FAR struct pthread_tcb_s *tcb, int execute)
+static void pthread_cleanup_pop_tcb(FAR struct tcb_s *tcb, int execute)
 {
   if (tcb->tos > 0)
     {
@@ -124,7 +124,7 @@ static void pthread_cleanup_pop_tcb(FAR struct pthread_tcb_s *tcb, int execute)
  *
  *   - The thread exits (that is, calls pthread_exit()).
  *   - The thread acts upon a cancellation request.
- *   - The thread calls pthread_cleanup_pop() with a non-zero execute argument.
+ *   - The thread calls pthread_cleanup_pop() with non-zero execute argument.
  *
  * Input Parameters:
  *   routine - The cleanup routine to be pushed on the cleanup stack.
@@ -138,9 +138,7 @@ static void pthread_cleanup_pop_tcb(FAR struct pthread_tcb_s *tcb, int execute)
 
 void pthread_cleanup_pop(int execute)
 {
-  FAR struct pthread_tcb_s *tcb = (FAR struct pthread_tcb_s *)this_task();
-
-  /* We don't assert if called from a non-pthread; we just don't do anything */
+  FAR struct tcb_s *tcb = this_task();
 
   DEBUGASSERT(tcb != NULL);
 
@@ -150,19 +148,13 @@ void pthread_cleanup_pop(int execute)
    */
 
   sched_lock();
-  if ((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
-    {
-      pthread_cleanup_pop_tcb(tcb, execute);
-    }
-
+  pthread_cleanup_pop_tcb(tcb, execute);
   sched_unlock();
 }
 
 void pthread_cleanup_push(pthread_cleanup_t routine, FAR void *arg)
 {
-  FAR struct pthread_tcb_s *tcb = (FAR struct pthread_tcb_s *)this_task();
-
-  /* We don't assert if called from a non-pthread; we just don't do anything */
+  FAR struct tcb_s *tcb = this_task();
 
   DEBUGASSERT(tcb != NULL);
   DEBUGASSERT(tcb->tos < CONFIG_PTHREAD_CLEANUP_STACKSIZE);
@@ -173,8 +165,7 @@ void pthread_cleanup_push(pthread_cleanup_t routine, FAR void *arg)
    */
 
   sched_lock();
-  if ((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD &&
-      tcb->tos < CONFIG_PTHREAD_CLEANUP_STACKSIZE)
+  if (tcb->tos < CONFIG_PTHREAD_CLEANUP_STACKSIZE)
     {
       unsigned int ndx = tcb->tos;
 
@@ -191,8 +182,8 @@ void pthread_cleanup_push(pthread_cleanup_t routine, FAR void *arg)
  *
  * Description:
  *   The pthread_cleanup_popall() is an internal function that will pop and
- *   execute all clean-up functions.  This function is only called from within
- *   the pthread_exit() and pthread_cancellation() logic
+ *   execute all clean-up functions.  This function is only called from
+ *   within the pthread_exit() and pthread_cancellation() logic
  *
  * Input Parameters:
  *   tcb - The TCB of the pthread that is exiting or being canceled.
@@ -202,10 +193,9 @@ void pthread_cleanup_push(pthread_cleanup_t routine, FAR void *arg)
  *
  ****************************************************************************/
 
-void pthread_cleanup_popall(FAR struct pthread_tcb_s *tcb)
+void pthread_cleanup_popall(FAR struct tcb_s *tcb)
 {
   DEBUGASSERT(tcb != NULL);
-  DEBUGASSERT((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD);
 
   /* Pop and execute each cleanup routine/
    *
diff --git a/sched/pthread/pthread_exit.c b/sched/pthread/pthread_exit.c
index 87f4052..740bec8 100644
--- a/sched/pthread/pthread_exit.c
+++ b/sched/pthread/pthread_exit.c
@@ -1,7 +1,8 @@
 /****************************************************************************
  * sched/pthread/pthread_exit.c
  *
- *   Copyright (C) 2007, 2009, 2011-2013, 2017 Gregory Nutt. All rights reserved.
+ *   Copyright (C) 2007, 2009, 2011-2013, 2017 Gregory Nutt.
+ *   All rights reserved.
  *   Author: Gregory Nutt <gn...@nuttx.org>
  *
  * Redistribution and use in source and binary forms, with or without
@@ -105,7 +106,7 @@ void pthread_exit(FAR void *exit_value)
 #ifdef CONFIG_PTHREAD_CLEANUP
   /* Perform any stack pthread clean-up callbacks */
 
-  pthread_cleanup_popall((FAR struct pthread_tcb_s *)tcb);
+  pthread_cleanup_popall(tcb);
 #endif
 
   /* Complete pending join operations */
diff --git a/sched/task/exit.c b/sched/task/exit.c
index 3d9f5ec..5bb9b1c 100644
--- a/sched/task/exit.c
+++ b/sched/task/exit.c
@@ -34,6 +34,7 @@
 #include "task/task.h"
 #include "group/group.h"
 #include "sched/sched.h"
+#include "pthread/pthread.h"
 
 /****************************************************************************
  * Public Functions
@@ -86,6 +87,12 @@ void exit(int status)
   group_kill_children(tcb);
 #endif
 
+#ifdef CONFIG_PTHREAD_CLEANUP
+  /* Perform any stack pthread clean-up callbacks */
+
+  pthread_cleanup_popall(tcb);
+#endif
+
   /* Perform common task termination logic.  This will get called again later
    * through logic kicked off by _exit().  However, we need to call it before
    * calling _exit() in order to handle atexit() and on_exit() callbacks and


[incubator-nuttx] 02/02: sched: Check the mutex consistent in main thread too

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

gnutt pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit fd5fc2ab07d96421b8df89fe01cd988a9b71c607
Author: Xiang Xiao <xi...@xiaomi.com>
AuthorDate: Thu Jun 4 02:38:39 2020 +0800

    sched: Check the mutex consistent in main thread too
    
    Signed-off-by: Xiang Xiao <xi...@xiaomi.com>
    Change-Id: I2d91154572805699237cfc028202021c8f8eee40
---
 include/nuttx/sched.h                     | 12 ++---
 sched/pthread/pthread.h                   |  2 +-
 sched/pthread/pthread_cancel.c            | 22 ++++----
 sched/pthread/pthread_exit.c              |  2 +-
 sched/pthread/pthread_mutex.c             | 84 +++++++++++--------------------
 sched/pthread/pthread_mutexinconsistent.c |  4 +-
 sched/task/exit.c                         |  6 +++
 7 files changed, 57 insertions(+), 75 deletions(-)

diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index 0bc3d1e..c7865f2 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -714,6 +714,12 @@ struct tcb_s
   FAR struct mqueue_inode_s *msgwaitq;   /* Waiting for this message queue      */
 #endif
 
+  /* Robust mutex support *******************************************************/
+
+#if !defined(CONFIG_DISABLE_PTHREAD) && !defined(CONFIG_PTHREAD_MUTEX_UNSAFE)
+  FAR struct pthread_mutex_s *mhead;     /* List of mutexes held by thread      */
+#endif
+
   /* Clean-up stack *************************************************************/
 
 #ifdef CONFIG_PTHREAD_CLEANUP
@@ -796,12 +802,6 @@ struct pthread_tcb_s
 
   pthread_addr_t arg;                    /* Startup argument                    */
   FAR void *joininfo;                    /* Detach-able info to support join    */
-
-  /* Robust mutex support *******************************************************/
-
-#ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
-  FAR struct pthread_mutex_s *mhead;     /* List of mutexes held by thread      */
-#endif
 };
 #endif /* !CONFIG_DISABLE_PTHREAD */
 
diff --git a/sched/pthread/pthread.h b/sched/pthread/pthread.h
index 82c9bd8..f2fd415 100644
--- a/sched/pthread/pthread.h
+++ b/sched/pthread/pthread.h
@@ -121,7 +121,7 @@ int pthread_mutex_take(FAR struct pthread_mutex_s *mutex,
                        FAR const struct timespec *abs_timeout, bool intr);
 int pthread_mutex_trytake(FAR struct pthread_mutex_s *mutex);
 int pthread_mutex_give(FAR struct pthread_mutex_s *mutex);
-void pthread_mutex_inconsistent(FAR struct pthread_tcb_s *tcb);
+void pthread_mutex_inconsistent(FAR struct tcb_s *tcb);
 #else
 #  define pthread_mutex_take(m,abs_timeout,i)  pthread_sem_take(&(m)->sem,(abs_timeout),(i))
 #  define pthread_mutex_trytake(m)             pthread_sem_trytake(&(m)->sem)
diff --git a/sched/pthread/pthread_cancel.c b/sched/pthread/pthread_cancel.c
index ab64a4e..02b8087 100644
--- a/sched/pthread/pthread_cancel.c
+++ b/sched/pthread/pthread_cancel.c
@@ -39,7 +39,7 @@
 
 int pthread_cancel(pthread_t thread)
 {
-  FAR struct pthread_tcb_s *tcb;
+  FAR struct tcb_s *tcb;
 
   /* First, make sure that the handle references a valid thread */
 
@@ -52,7 +52,7 @@ int pthread_cancel(pthread_t thread)
       return ESRCH;
     }
 
-  tcb = (FAR struct pthread_tcb_s *)nxsched_get_tcb((pid_t)thread);
+  tcb = nxsched_get_tcb((pid_t)thread);
   if (tcb == NULL)
     {
       /* The pid does not correspond to any known thread.  The thread
@@ -64,7 +64,7 @@ int pthread_cancel(pthread_t thread)
 
   /* Only pthreads should use this interface */
 
-  DEBUGASSERT((tcb->cmn.flags & TCB_FLAG_TTYPE_MASK) ==
+  DEBUGASSERT((tcb->flags & TCB_FLAG_TTYPE_MASK) ==
                TCB_FLAG_TTYPE_PTHREAD);
 
   /* Check to see if this thread has the non-cancelable bit set in its
@@ -73,7 +73,7 @@ int pthread_cancel(pthread_t thread)
    */
 
   sched_lock();
-  if ((tcb->cmn.flags & TCB_FLAG_NONCANCELABLE) != 0)
+  if ((tcb->flags & TCB_FLAG_NONCANCELABLE) != 0)
     {
       /* Then we cannot cancel the thread now.  Here is how this is
        * supposed to work:
@@ -88,7 +88,7 @@ int pthread_cancel(pthread_t thread)
        *  immediately, interrupting the thread with its processing."
        */
 
-      tcb->cmn.flags |= TCB_FLAG_CANCEL_PENDING;
+      tcb->flags |= TCB_FLAG_CANCEL_PENDING;
       sched_unlock();
       return OK;
     }
@@ -96,21 +96,21 @@ int pthread_cancel(pthread_t thread)
 #ifdef CONFIG_CANCELLATION_POINTS
   /* Check if this thread supports deferred cancellation */
 
-  if ((tcb->cmn.flags & TCB_FLAG_CANCEL_DEFERRED) != 0)
+  if ((tcb->flags & TCB_FLAG_CANCEL_DEFERRED) != 0)
     {
       /* Then we cannot cancel the thread asynchronously.  Mark the
        * cancellation as pending.
        */
 
-      tcb->cmn.flags |= TCB_FLAG_CANCEL_PENDING;
+      tcb->flags |= TCB_FLAG_CANCEL_PENDING;
 
       /* If the thread is waiting at a cancellation point, then notify of the
        * cancellation thereby waking the task up with an ECANCELED error.
        */
 
-      if (tcb->cmn.cpcount > 0)
+      if (tcb->cpcount > 0)
         {
-          nxnotify_cancellation(&tcb->cmn);
+          nxnotify_cancellation(tcb);
         }
 
       sched_unlock();
@@ -126,7 +126,7 @@ int pthread_cancel(pthread_t thread)
    * same as pthread_exit(PTHREAD_CANCELED).
    */
 
-  if (tcb == (FAR struct pthread_tcb_s *)this_task())
+  if (tcb == this_task())
     {
       pthread_exit(PTHREAD_CANCELED);
     }
@@ -141,7 +141,7 @@ int pthread_cancel(pthread_t thread)
    * function will be unable to unlock its own mutexes.
    */
 
-  pthread_cleanup_popall((FAR struct tcb_s *)tcb);
+  pthread_cleanup_popall(tcb);
 #endif
 
   /* Complete pending join operations */
diff --git a/sched/pthread/pthread_exit.c b/sched/pthread/pthread_exit.c
index 740bec8..b77f2c8 100644
--- a/sched/pthread/pthread_exit.c
+++ b/sched/pthread/pthread_exit.c
@@ -124,7 +124,7 @@ void pthread_exit(FAR void *exit_value)
 #ifndef CONFIG_PTHREAD_MUTEX_UNSAFE
   /* Recover any mutexes still held by the canceled thread */
 
-  pthread_mutex_inconsistent((FAR struct pthread_tcb_s *)tcb);
+  pthread_mutex_inconsistent(tcb);
 #endif
 
   /* Perform common task termination logic.  This will get called again later
diff --git a/sched/pthread/pthread_mutex.c b/sched/pthread/pthread_mutex.c
index 01b3d37..5648a7d 100644
--- a/sched/pthread/pthread_mutex.c
+++ b/sched/pthread/pthread_mutex.c
@@ -57,30 +57,16 @@
 static void pthread_mutex_add(FAR struct pthread_mutex_s *mutex)
 {
   FAR struct tcb_s *rtcb = this_task();
+  irqstate_t flags;
 
   DEBUGASSERT(mutex->flink == NULL);
 
-  /* Check if this is a pthread.  The main thread may also lock and unlock
-   * mutexes.  The main thread, however, does not participate in the mutex
-   * consistency logic.  Presumably, when the main thread exits, all of the
-   * child pthreads will also terminate.
-   *
-   * REVISIT:  NuttX does not support that behavior at present; child
-   * pthreads will persist after the main thread exits.
-   */
-
-  if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
-    {
-      FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)rtcb;
-      irqstate_t flags;
-
-      /* Add the mutex to the list of mutexes held by this pthread */
+  /* Add the mutex to the list of mutexes held by this pthread */
 
-      flags        = enter_critical_section();
-      mutex->flink = ptcb->mhead;
-      ptcb->mhead  = mutex;
-      leave_critical_section(flags);
-    }
+  flags        = enter_critical_section();
+  mutex->flink = rtcb->mhead;
+  rtcb->mhead  = mutex;
+  leave_critical_section(flags);
 }
 
 /****************************************************************************
@@ -100,47 +86,37 @@ static void pthread_mutex_add(FAR struct pthread_mutex_s *mutex)
 static void pthread_mutex_remove(FAR struct pthread_mutex_s *mutex)
 {
   FAR struct tcb_s *rtcb = this_task();
+  FAR struct pthread_mutex_s *curr;
+  FAR struct pthread_mutex_s *prev;
+  irqstate_t flags;
 
-  /* Check if this is a pthread.  The main thread may also lock and unlock
-   * mutexes.  The main thread, however, does not participate in the mutex
-   * consistency logic.
-   */
+  flags = enter_critical_section();
 
-  if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)
-    {
-      FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)rtcb;
-      FAR struct pthread_mutex_s *curr;
-      FAR struct pthread_mutex_s *prev;
-      irqstate_t flags;
-
-      flags = enter_critical_section();
-
-      /* Remove the mutex from the list of mutexes held by this task */
-
-      for (prev = NULL, curr = ptcb->mhead;
-           curr != NULL && curr != mutex;
-           prev = curr, curr = curr->flink)
-        {
-        }
+  /* Remove the mutex from the list of mutexes held by this task */
 
-      DEBUGASSERT(curr == mutex);
+  for (prev = NULL, curr = rtcb->mhead;
+       curr != NULL && curr != mutex;
+       prev = curr, curr = curr->flink)
+    {
+    }
 
-      /* Remove the mutex from the list.  prev == NULL means that the mutex
-       * to be removed is at the head of the list.
-       */
+  DEBUGASSERT(curr == mutex);
 
-      if (prev == NULL)
-        {
-          ptcb->mhead = mutex->flink;
-        }
-      else
-        {
-          prev->flink = mutex->flink;
-        }
+  /* Remove the mutex from the list.  prev == NULL means that the mutex
+   * to be removed is at the head of the list.
+   */
 
-      mutex->flink = NULL;
-      leave_critical_section(flags);
+  if (prev == NULL)
+    {
+      rtcb->mhead = mutex->flink;
     }
+  else
+    {
+      prev->flink = mutex->flink;
+    }
+
+  mutex->flink = NULL;
+  leave_critical_section(flags);
 }
 
 /****************************************************************************
diff --git a/sched/pthread/pthread_mutexinconsistent.c b/sched/pthread/pthread_mutexinconsistent.c
index eb50056..d5d7c0a 100644
--- a/sched/pthread/pthread_mutexinconsistent.c
+++ b/sched/pthread/pthread_mutexinconsistent.c
@@ -71,7 +71,7 @@
  *
  ****************************************************************************/
 
-void pthread_mutex_inconsistent(FAR struct pthread_tcb_s *tcb)
+void pthread_mutex_inconsistent(FAR struct tcb_s *tcb)
 {
   FAR struct pthread_mutex_s *mutex;
   irqstate_t flags;
@@ -80,7 +80,7 @@ void pthread_mutex_inconsistent(FAR struct pthread_tcb_s *tcb)
 
   sched_lock();
 
-  /* Remove and process each mutex from the list of mutexes held by this task */
+  /* Remove and process each mutex held by this task */
 
   while (tcb->mhead != NULL)
     {
diff --git a/sched/task/exit.c b/sched/task/exit.c
index 5bb9b1c..fed287a 100644
--- a/sched/task/exit.c
+++ b/sched/task/exit.c
@@ -93,6 +93,12 @@ void exit(int status)
   pthread_cleanup_popall(tcb);
 #endif
 
+#if !defined(CONFIG_DISABLE_PTHREAD) && !defined(CONFIG_PTHREAD_MUTEX_UNSAFE)
+  /* Recover any mutexes still held by the canceled thread */
+
+  pthread_mutex_inconsistent(tcb);
+#endif
+
   /* Perform common task termination logic.  This will get called again later
    * through logic kicked off by _exit().  However, we need to call it before
    * calling _exit() in order to handle atexit() and on_exit() callbacks and