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

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


The following commit(s) were added to refs/heads/master by this push:
     new 31f3857cb41 sched: Fix deadlock cycle collection.
31f3857cb41 is described below

commit 31f3857cb41ebd4b561e5fe40a8ec31d5ebd6d98
Author: yushuailong <[email protected]>
AuthorDate: Mon Sep 28 21:26:02 2026 +0800

    sched: Fix deadlock cycle collection.
    
    Use Floyd cycle detection on the mutex wait-for chain so only threads that 
actually participate in a cycle are reported. This avoids omitting the last 
cycle member and incorrectly including threads that merely lead into a deadlock.
    
    Also handle empty output buffers and document truncation semantics.
    
    Assisted-by: OpenAI Codex
    Signed-off-by: yushuailong <[email protected]>
---
 include/nuttx/sched.h |   9 ++--
 sched/misc/deadlock.c | 144 ++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 108 insertions(+), 45 deletions(-)

diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index 390bb00a99a..09916b833d6 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -1611,14 +1611,15 @@ pid_t nxsched_getppid(void);
  * Name: nxsched_collect_deadlock
  *
  * Description:
- *   Check if there is a deadlock and get the thread pid of the deadlock.
+ *   Find mutex deadlocks and collect the IDs of participating threads.
  *
  * Input parameters:
- *   pid   - The array to store the thread pid of the deadlock.
- *   count - The size of the pid array.
+ *   pid   - The array to store deadlocked thread IDs.
+ *   count - The maximum number of thread IDs to store.
  *
  * Returned Value:
- *   The number of thread deadlocks.
+ *   The number of thread IDs stored in pid.  A return value equal to count
+ *   may indicate that the result was truncated.
  *
  ****************************************************************************/
 
diff --git a/sched/misc/deadlock.c b/sched/misc/deadlock.c
index 68dd7798404..0f8e350d1ac 100644
--- a/sched/misc/deadlock.c
+++ b/sched/misc/deadlock.c
@@ -68,72 +68,128 @@ static FAR mutex_t *getmutex(FAR struct tcb_s *tcb)
 }
 
 /****************************************************************************
- * Name: collect_deadlock
+ * Name: deadlock_next
  ****************************************************************************/
 
-static void collect_deadlock(FAR struct tcb_s *tcb, FAR void *arg)
+static FAR struct tcb_s *deadlock_next(FAR struct tcb_s *tcb)
 {
-  FAR struct deadlock_info_s *info = arg;
   FAR mutex_t *mutex;
-  size_t index;
+  pid_t holder;
 
   mutex = getmutex(tcb);
   if (mutex == NULL)
     {
-      return;
+      return NULL;
     }
 
-  /* Check previous deadlock holder list. */
+  holder = nxmutex_get_holder(mutex);
+  if (holder < 0)
+    {
+      return NULL;
+    }
 
-  for (index = 0; index < info->holdercnt; index++)
+  return nxsched_get_tcb(holder);
+}
+
+/****************************************************************************
+ * Name: find_deadlock_cycle
+ ****************************************************************************/
+
+static FAR struct tcb_s *find_deadlock_cycle(FAR struct tcb_s *tcb)
+{
+  FAR struct tcb_s *slow = tcb;
+  FAR struct tcb_s *fast = tcb;
+
+  /* Each thread has at most one outgoing edge in the mutex wait-for graph.
+   * Use Floyd's algorithm to determine whether the chain contains a cycle.
+   */
+
+  do
     {
-      if (info->holders[index] == tcb->pid)
+      slow = deadlock_next(slow);
+      fast = deadlock_next(fast);
+      if (fast != NULL)
         {
-          return;
+          fast = deadlock_next(fast);
+        }
+
+      if (slow == NULL || fast == NULL)
+        {
+          return NULL;
         }
     }
+  while (slow != fast);
 
-  /* Append the holders for this tcb to list. */
+  /* Then locate the first TCB in the cycle. */
 
-  for (index = info->holdercnt; index < info->arraylen; index++)
+  slow = tcb;
+  while (slow != fast)
     {
-      pid_t holder;
-      size_t i;
+      slow = deadlock_next(slow);
+      fast = deadlock_next(fast);
+    }
 
-      holder = nxmutex_get_holder(mutex);
-      if (holder < 0)
-        {
-          break;
-        }
+  return slow;
+}
 
-      /* Check if this holder is already held. */
+/****************************************************************************
+ * Name: deadlock_contains
+ ****************************************************************************/
 
-      for (i = info->holdercnt; i < index; i++)
+static bool deadlock_contains(FAR const struct deadlock_info_s *info,
+                              pid_t pid)
+{
+  size_t index;
+
+  for (index = 0; index < info->holdercnt; index++)
+    {
+      if (info->holders[index] == pid)
         {
-          if (info->holders[i] == holder)
-            {
-              info->holdercnt = index;
-              return;
-            }
+          return true;
         }
+    }
 
-      /* Add holder to list and continue to holder's holder. */
+  return false;
+}
 
-      info->holders[index] = tcb->pid;
-      tcb = nxsched_get_tcb(holder);
-      mutex = getmutex(tcb);
-      if (mutex == NULL)
-        {
-          /* If this holder isn't waiting for mutex, it's over. */
+/****************************************************************************
+ * Name: collect_deadlock
+ ****************************************************************************/
 
-          break;
-        }
+static void collect_deadlock(FAR struct tcb_s *tcb, FAR void *arg)
+{
+  FAR struct deadlock_info_s *info = arg;
+  FAR struct tcb_s *entry;
+  FAR struct tcb_s *current;
+
+  if (info->holdercnt >= info->arraylen ||
+      deadlock_contains(info, tcb->pid))
+    {
+      return;
     }
 
-  /* If no deadlock, clear the holders of this tcb. */
+  entry = find_deadlock_cycle(tcb);
+  if (entry == NULL || deadlock_contains(info, entry->pid))
+    {
+      return;
+    }
+
+  /* Only copy TCBs which are members of the cycle.  Threads which merely
+   * wait on a deadlocked thread are not themselves part of the deadlock.
+   */
+
+  current = entry;
+  do
+    {
+      if (info->holdercnt >= info->arraylen)
+        {
+          return;
+        }
 
-  memset(&info->holders[info->holdercnt], 0,
-         (info->arraylen - info->holdercnt) * sizeof(pid_t));
+      info->holders[info->holdercnt++] = current->pid;
+      current = deadlock_next(current);
+    }
+  while (current != entry);
 }
 
 /****************************************************************************
@@ -144,14 +200,15 @@ static void collect_deadlock(FAR struct tcb_s *tcb, FAR 
void *arg)
  * Name: nxsched_collect_deadlock
  *
  * Description:
- *   Check if there is a deadlock and get the thread pid of the deadlock.
+ *   Find mutex deadlocks and collect the IDs of participating threads.
  *
  * Input parameters:
- *   pid   - The array to store the thread pid of the deadlock.
- *   count - The size of the pid array.
+ *   pid   - The array to store deadlocked thread IDs.
+ *   count - The maximum number of thread IDs to store.
  *
  * Returned Value:
- *   The number of thread deadlocks.
+ *   The number of thread IDs stored in pid.  A return value equal to count
+ *   may indicate that the result was truncated.
  *
  ****************************************************************************/
 
@@ -159,6 +216,11 @@ size_t nxsched_collect_deadlock(FAR pid_t *pid, size_t 
count)
 {
   struct deadlock_info_s info;
 
+  if (pid == NULL || count == 0)
+    {
+      return 0;
+    }
+
   info.holders = pid;
   info.arraylen = count;
   info.holdercnt = 0;

Reply via email to