diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2008-02-08 04:19:13 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-08 09:22:27 -0800 |
commit | 5dee1707dfbfc55eb7569b9ae5abaf932bd4c377 (patch) | |
tree | 6e162f61f9645f790aa14cb4146882b918bb0286 /kernel/signal.c | |
parent | 6806aac6d282d58b97763f5e17e2787e62c3b440 (diff) | |
download | lwn-5dee1707dfbfc55eb7569b9ae5abaf932bd4c377.tar.gz lwn-5dee1707dfbfc55eb7569b9ae5abaf932bd4c377.zip |
move the related code from exit_notify() to exit_signals()
The previous bugfix was not optimal, we shouldn't care about group stop
when we are the only thread or the group stop is in progress. In that case
nothing special is needed, just set PF_EXITING and return.
Also, take the related "TIF_SIGPENDING re-targeting" code from exit_notify().
So, from the performance POV the only difference is that we don't trust
!signal_pending() until we take ->siglock. But this in fact fixes another
___pure___ theoretical minor race. __group_complete_signal() finds the
task without PF_EXITING and chooses it as the target for signal_wake_up().
But nothing prevents this task from exiting in between without noticing the
pending signal and thus unpredictably delaying the actual delivery.
Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Cc: Davide Libenzi <davidel@xmailserver.org>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 27 |
1 files changed, 22 insertions, 5 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 209eec11eef5..3d3adb94561d 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1903,19 +1903,36 @@ relock: void exit_signals(struct task_struct *tsk) { int group_stop = 0; + struct task_struct *t; - spin_lock_irq(&tsk->sighand->siglock); - if (unlikely(tsk->signal->group_stop_count) && - !--tsk->signal->group_stop_count) { - tsk->signal->flags = SIGNAL_STOP_STOPPED; - group_stop = 1; + if (thread_group_empty(tsk) || signal_group_exit(tsk->signal)) { + tsk->flags |= PF_EXITING; + return; } + spin_lock_irq(&tsk->sighand->siglock); /* * From now this task is not visible for group-wide signals, * see wants_signal(), do_signal_stop(). */ tsk->flags |= PF_EXITING; + if (!signal_pending(tsk)) + goto out; + + /* It could be that __group_complete_signal() choose us to + * notify about group-wide signal. Another thread should be + * woken now to take the signal since we will not. + */ + for (t = tsk; (t = next_thread(t)) != tsk; ) + if (!signal_pending(t) && !(t->flags & PF_EXITING)) + recalc_sigpending_and_wake(t); + + if (unlikely(tsk->signal->group_stop_count) && + !--tsk->signal->group_stop_count) { + tsk->signal->flags = SIGNAL_STOP_STOPPED; + group_stop = 1; + } +out: spin_unlock_irq(&tsk->sighand->siglock); if (unlikely(group_stop)) { |