Calling task_terminate() (on other than current_task()) risks deadlocking if 
the task being terminated has multiple threads. Currently, the head of the 
thread list must terminate successfully before terminating the next in the 
list. This is not always possible, for example a thread which is not the list 
head might hold a thread_reference to the thread at the list head. In that 
case, the list head cannot be terminated until the other thread releases that 
reference which won't happen if that other thread is TH_SUSP as it is not 
rescheduled. Instead, task terminate() now attempts thread terminatation in 
list sequence (rather than just the list head) and keeps doing so until none 
remain.
---
 kern/task.c | 32 +++++++++++++++++++++++++-------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/kern/task.c b/kern/task.c
index e78e856f..421c4cd4 100644
--- a/kern/task.c
+++ b/kern/task.c
@@ -405,16 +405,34 @@ kern_return_t task_terminate(
          *      thread (e.g., the reaper) may have to run to get rid
          *      of all references to the thread; it won't vanish from
          *      the task's thread list until the last one is gone.
+        *
+        *      Occasionally there are dependencies between threads
+        *      that require a specific thread to be terminated before
+        *      others are able to. These dependencies are unknown to
+        *      the task so repeated iteration over the thread list is
+        *      required.
          */
         task_lock(task);
         while (!queue_empty(list)) {
-                thread = (thread_t) queue_first(list);
-                thread_reference(thread);
-                task_unlock(task);
-                thread_force_terminate(thread);
-                thread_deallocate(thread);
-                thread_block(thread_no_continuation);
-                task_lock(task);
+
+          thread = (thread_t) queue_first(list);
+          thread_reference(thread);
+
+          do
+            {
+              thread_t next = (thread_t) queue_next(&thread->thread_list);
+
+              if (!queue_end(list, (queue_entry_t) next))
+               thread_reference(next);
+
+              task_unlock(task);
+              thread_force_terminate(thread);
+              thread_deallocate(thread);
+              thread_block(thread_no_continuation);
+              thread = next;
+              task_lock(task);
+            }
+          while (!queue_end(list, (queue_entry_t) thread));
         }
         task_unlock(task);
 
-- 
2.47.3


Reply via email to