Commit d00e2ab5 authored by Roland McGrath's avatar Roland McGrath Committed by Linus Torvalds

[PATCH] fix MT reparenting when thread group leader dies

When the initial thread in a multi-threaded program dies (the thread group
leader), its child processes are wrongly orphaned, and thereafter when
other threads die their child processes are also orphaned even though live
threads remain in the parent process that can call wait.  I have a small
(under 100 lines), POSIX-compliant test program that demonstrates this
using -lpthread (NPTL) if anyone is interested in seeing it.

The bug is that forget_original_parent moves children to the dead parent's
group leader if it's alive, but if not it orphans them.  I've changed it so
it instead reparents children to any other live thread in the dead parent's
group (not even preferring the group leader).  Children go to init only if
there are no live threads in the parent's group at all.  These are the
correct semantics for fork children of POSIX threads. 

The second part of the change is to do the CLONE_PARENT behavior always for
CLONE_THREAD, i.e.  make sure that each new thread's parent link points to
the real parent of the process and never another thread in its own group.
Without this, when the group leader dies leaving a sole live thread in the
group, forget_original_parent will try to reparent that thread to itself
because it's a child of the dying group leader.  Rather handling this case
specially to reparent to the group leader's parent instead, it's more
efficient just to make sure that noone ever has a parent link to inside his
own thread group.  Now the reparenting work never needs to be done for
threads created in the same group when their creator thread dies.  The only
change from losing the who-created-whom information is when you look at
"PPid:" in /proc/PID/task/TID/status.  For purposes of all direct system
calls, it was already as if CLONE_THREAD threads had the parent of the
group leader.  (POSIX provides no way to keep track of which thread created
which other thread with pthread_create.)
Signed-off-by: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 37440b38
......@@ -528,10 +528,8 @@ static inline void choose_new_parent(task_t *p, task_t *reaper, task_t *child_re
* Make sure we're not reparenting to ourselves and that
* the parent is not a zombie.
*/
if (p == reaper || reaper->state >= TASK_ZOMBIE)
p->real_parent = child_reaper;
else
p->real_parent = reaper;
BUG_ON(p == reaper || reaper->state >= TASK_ZOMBIE);
p->real_parent = reaper;
if (p->parent == p->real_parent)
BUG();
}
......@@ -599,9 +597,13 @@ static inline void forget_original_parent(struct task_struct * father,
struct task_struct *p, *reaper = father;
struct list_head *_p, *_n;
reaper = father->group_leader;
if (reaper == father)
reaper = child_reaper;
do {
reaper = next_thread(reaper);
if (reaper == father) {
reaper = child_reaper;
break;
}
} while (reaper->state >= TASK_ZOMBIE);
/*
* There are only two places where our children can be:
......
......@@ -1075,7 +1075,7 @@ static task_t *copy_process(unsigned long clone_flags,
}
/* CLONE_PARENT re-uses the old parent */
if (clone_flags & CLONE_PARENT)
if (clone_flags & (CLONE_PARENT|CLONE_THREAD))
p->real_parent = current->real_parent;
else
p->real_parent = current;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment