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