This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH v2] Always pass signals to the right thread.
- From: Pedro Alves <palves at redhat dot com>
- To: gdb-patches at sourceware dot org
- Cc: Eli Zaretskii <eliz at gnu dot org>
- Date: Thu, 24 Jul 2014 16:40:42 +0100
- Subject: Re: [PATCH v2] Always pass signals to the right thread.
- Authentication-results: sourceware.org; auth=none
- References: <1403183341-12581-1-git-send-email-palves at redhat dot com> <53A790DB dot 40908 at codesourcery dot com> <53A9648F dot 1030006 at redhat dot com>
Hi Eli,
Could you take a look at the documentation bits of this patch?
Thanks!
Pedro Alves
On 06/24/2014 12:44 PM, Pedro Alves wrote:
> On 06/23/2014 03:28 AM, Yao Qi wrote:
>> On 06/19/2014 09:09 PM, Pedro Alves wrote:
>>> (gdb) thread 1
>>> [Switching to thread 1 (Thread 10979)]
>>> (gdb) signal SIGUSR2
>>> Note:
>>> Thread 3 previously stopped with signal SIGUSR2, User defined signal 2.
>>> Thread 2 previously stopped with signal SIGUSR1, User defined signal 1.
>>> Continuing thread 1 (the current thread) with specified signal will
>>> still deliver the signals noted above to their respective threads.
>>> Continue anyway? (y or n)
>>
>> This message isn't clear to me, because user has to read this sentence
>> and think what GDB will do for these threads.
>
> Well, stop and think is the point of the warning/query. :-)
>
>> IMO, it is better to say
>> explicitly what GDB will do for each thread, like
>>
>> (gdb) signal SIGUSR2
>> Continuing Thread 3 with signal SIGUSR2, User defined signal 2.
>> Continuing Thread 2 with signal SIGUSR1, User defined signal 1.
>> Continuing Thread 1 with signal SIGUSR2, User defined signal 2.
>> Continue anyway? (y or n)
>
> Thanks. Even though it first appeared promising, I've been playing
> with this since yesterday and in the end I can't seem to find it
> really better. :-/
> (I had already contemplated lots of different options before
> posting the original patch.)
>
> That kind of reads to me like only those three threads
> are continued. With my user hat on, that then makes me wonder what
> will happen to all the other threads not listed. And why is GDB
> really telling about these signal that I didn't specify? And which
> is the one that is getting the signal because I'm overriding
> it? Thread 3 or thread 1? And that output would seem to make more
> sense to me if we always asked for confirmation even if no thread
> other than the current would get a signal, otherwise, the user has
> no clue really why he's being asked for confirmation), but I'm not sure
> that'd be a good idea. (Also, I think the output should still
> make sense if we ever find a need to add a cap on the number of
> threads listed and say "many threads" instead of showing a list.)
>
> In any case, I still tried this direction, by doing two passes
> over the threads, to determine whether we need to ask for confirmation,
> and added an intro text:
>
> (gdb) signal SIGUSR1
> Some of the threads other than the current that are also about to be resumed had previously stopped for signals.
> If you proceed, those signals will be delivered to the respective threads.
> Continuing thread 3 with signal SIGUSR2.
> Continuing thread 1 with signal SIGUSR1.
> Continue anyway? (y or n)
>
> But, what about "signal 0" ? That results in:
>
> (gdb) signal 0
> Some of the threads other than the current that are also about to be resumed had previously stopped for signals.
> If you proceed, those signals will be delivered to the respective threads.
> Continuing thread 3 with signal SIGUSR2.
> Continuing thread 1 with no signal.
> Continue anyway? (y or n)
>
> And again, that makes me wonder why "no signal" is mentioned for only
> one thread and not any other that will be resumed with no signal
> either (because it hadn't stopped with one).
>
> We could then list what will happen to _all_ threads that will be
> resumed, with or without signal, but I _really_ don't think it's
> a good idea -- that set will usually be _much_ larger than the
> signalled threads, which will normally be just one.
>
> So all in all, in the end, I still prefer my original output.
>
>> We may need a NEWS entry, as this is a user-visible change.
>
> Done.
>
>>> + ALL_NON_EXITED_THREADS (tp)
>>> + {
>>
>> Replace eight spaces with a tab.
>
> Thanks, fixed.
>
>>
>>> @@ -5190,6 +5161,10 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
>>> what keep_going does as well, if we call it. */
>>> ecs->event_thread->control.trap_expected = 0;
>>>
>>> + /* Likewise, clear the signal if it should not be passed. */
>>> + if (!signal_program[ecs->event_thread->suspend.stop_signal])
>>> + ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
>>> +
>>
>> I can understand this patch except this part. Why do we need to set
>> stop_signal here?
>
> We can get here with the stop signal set to GDB_SIGNAL_TRAP due
> to a breakpoint of a finished step. If we don't clear it now,
> the next time we'll resume the target, and this thread isn't
> current, we'd pass the trap to the thread. See below for further
> explanation.
>
>>
>>> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
>>> index c9677ca..8f2600d 100644
>>> --- a/gdb/linux-nat.c
>>> +++ b/gdb/linux-nat.c
>>> @@ -1694,8 +1694,7 @@ linux_nat_resume_callback (struct lwp_info *lp, void *except)
>>> thread = find_thread_ptid (lp->ptid);
>>> if (thread != NULL)
>>> {
>>> - if (signal_pass_state (thread->suspend.stop_signal))
>>> - signo = thread->suspend.stop_signal;
>>> + signo = thread->suspend.stop_signal;
>>> thread->suspend.stop_signal = GDB_SIGNAL_0;
>>> }
>
>
>>
>> You didn't say much about these two changes. Is it because we've
>> already set thread->suspend.stop_signal to GDB_SIGNAL_0 if
>> signal_pass_state (tp->suspend.stop_signal) is false,
>
> ...
>
>>
>> + /* If this signal should not be seen by program, give it zero.
>> + Used for debugging signals. */
>> + if (!signal_pass_state (tp->suspend.stop_signal))
>> + tp->suspend.stop_signal = GDB_SIGNAL_0;
>> +
>>
>> that means (tp->suspend.stop_signal == GDB_SIGNAL_0 ||
>> signal_pass_state (tp->suspend.stop_signal)) is constantly true.
>> so in the callees of target_resume, signal_pass_state
>> (thread->suspend.stop_signal) is always true if
>> thread->suspend.stop_signal isn't GDB_SIGNAL_0?
>
> Nope, "signal SIGFOO" should still pass SIGFOO even if
> "handle SIGFOO nopass". So in that case,
> signal_pass_state (thread->suspend.stop_signal) is false,
> but we should still pass stop_signal. We assume the thread
> that was current at "signal SIGFOO" time will be resumed immediately,
> because GDB may need to e.g., step-over a breakpoint for another
> thread first. So we store the intent to pass SIGFOO once the
> step over is finished in stop_signal.
>
> I've added this comment:
>
> --- c/gdb/gdbthread.h
> +++ w/gdb/gdbthread.h
> @@ -135,7 +135,13 @@ struct thread_control_state
>
> struct thread_suspend_state
> {
> - /* Last signal that the inferior received (why it stopped). */
> + /* Last signal that the inferior received (why it stopped). When
> + the thread is resumed, this signal is delivered. Note: the
> + target should not check whether the signal is in pass state,
> + because the signal may have been explicitly passed with the
> + "signal" command, which overrides "handle nopass". If the signal
> + should be suppressed, the core will take care of clearing this
> + before the target is resumed. */
> enum gdb_signal stop_signal;
> };
>
> Does that make things clearer ?
>
>
>>> +
>>> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
>>> + executable { debug }] != "" } {
>>> + return -1
>>> +}
>>
>> Need [target_info exists gdb,nosignals] check?
>
> Yeah, thanks.
>
>>
>>> +
>>> +# Run the test proper. STEP_OVER indicates whether we leave in place
>>> +# a breakpoint that needs to be stepped over when we explicitly
>>> +# request a signal be delivered with the "signal" command.
>>> +
>>> +proc test { step_over } {
>>> + global srcfile binfile
>>> +
>>> + with_test_prefix "step-over $step_over" {
>>> + clean_restart ${binfile}
>>> +
>>> + if ![runto_main] then {
>>> + fail "Can't run to main"
>>> + return 0
>>> + }
>>> +
>>> + gdb_test "handle SIGUSR1 stop print nopass"
>>> +
>>> + gdb_test "b thread_function" "Breakpoint .* at .*$srcfile.*"
>>> + gdb_test "continue" "thread_function.*" "stopped in thread"
>>> +
>>> + # Thread 2 is stopped at a breakpoint. If we leave the
>>> + # breakpoint in place, GDB needs to move thread 2 past the
>>> + # breakpoint before delivering the signal to thread 1. We
>>> + # want to be sure that GDB doesn't mistakenly deliver the
>>> + # signal to thread 1 while doing that.
>>> + if { $step_over == "no" } {
>>> + delete_breakpoints
>>> + }
>>> +
>>> + gdb_test "break handler" "Breakpoint .* at .*$srcfile.*"
>>> +
>>> + gdb_test "thread 1" "Switching to thread 1.*"
>>> +
>>> + set ws "\[ \t\]+"
>>> + set line "\[^\r\n\]+"
>>> +
>>> + set pattern ""
>>> + append pattern "${ws}2${ws}Thread ${line}\r\n"
>>> + append pattern "\\\* 1${ws}Thread ${line}.*"
>>> +
>>> + gdb_test "info threads" $pattern "thread 1 selected"
>>
>> We want to know whether thread 1 is selected, so using "info threads 1"
>> is simpler in the regexp pattern, like
>>
>> gdb_test "info threads 1" "\\\* 1${ws}Thread .*" "thread 1 selected"
>
> The reason I don't like "info threads 1" this is that if the test fails,
> when you go look at the logs, you don't see which thread was actually
> current, which tends to make debugging things a little easier. But we
> can still simplify the patterns. I was aiming at having no wildcards in
> the middle of the output, but that's really not necessary.
> I've simplified the patterns now.
>
> Here's the updated patch.
>
> 8<-------------------------------
> From 5834f1fea7d303e3964751d6af505cac44fd1754 Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Tue, 24 Jun 2014 12:12:57 +0100
> Subject: [PATCH] Always pass signals to the right thread.
>
> Currently, GDB can pass a signal to the wrong thread in several
> different but related scenarios.
>
> E.g., if thread 1 stops for signal SIGFOO, the user switches to thread
> 2, and then issues "continue", SIGFOO is actually delivered to thread
> 2, not thread 1. This obviously messes up programs that use
> pthread_kill to send signals to specific threads.
>
> This has been a known issue for a long while. Back in 2008 when I
> made stop_signal be per-thread (2020b7ab), I kept the behavior -- see
> code in 'proceed' being removed -- wanting to come back to it later.
> The time has finally come now.
>
> The patch fixes this -- on resumption, intercepted signals are always
> delivered to the thread that had intercepted them.
>
> Another example: if thread 1 stops for a breakpoint, the user switches
> to thread 2, and then issues "signal SIGFOO", SIGFOO is actually
> delivered to thread 1, not thread 2, because 'proceed' first switches
> to thread 1 to step over its breakpoint... If the user deletes the
> breakpoint before issuing "signal FOO", then the signal is delivered
> to thread 2 (the current thread).
>
> "signal SIGFOO" can be used for two things: inject a signal in the
> program while the program/thread had stopped for none, bypassing
> "handle nopass"; or changing/suppressing a signal the program had
> stopped for. These scenarios are really two faces of the same coin,
> and GDB can't really guess what the user is trying to do. GDB might
> have intercepted signals in more than one thread even (see the new
> signal-command-multiple-signals-pending.exp test). At least in the
> inject case, it's obviously clear to me that the user means to deliver
> the signal to the currently selected thread, so best is to make the
> command's behavior consistent and easy to explain.
>
> Then, if the user is trying to suppress/change a signal the program
> had stopped for instead of injecting a new signal, but, the user had
> changed threads meanwhile, then she will be surprised that with:
>
> (gdb) continue
> Thread 1 stopped for signal SIGFOO.
> (gdb) thread 2
> (gdb) signal SIGBAR
>
> ... GDB actually delivers SIGFOO to thread 1, and SIGBAR to thread 2
> (with scheduler-locking off, which is the default, because then
> "signal" or any other resumption command resumes all threads).
>
> So the patch makes GDB detect that, and ask for confirmation:
>
> (gdb) thread 1
> [Switching to thread 1 (Thread 10979)]
> (gdb) signal SIGUSR2
> Note:
> Thread 3 previously stopped with signal SIGUSR2, User defined signal 2.
> Thread 2 previously stopped with signal SIGUSR1, User defined signal 1.
> Continuing thread 1 (the current thread) with specified signal will
> still deliver the signals noted above to their respective threads.
> Continue anyway? (y or n)
>
> All these scenarios are covered by the new tests.
>
> Tested on x86_64 Fedora 20, native and gdbserver.
>
> gdb/
> 2014-06-24 Pedro Alves <palves@redhat.com>
>
> * NEWS: Mention signal passing and "signal" command changes.
>
> * gdbthread.h (struct thread_suspend_state) <stop_signal>: Extend
> comment.
> * breakpoint.c (until_break_command): Adjust clear_proceed_status
> call.
> * infcall.c (run_inferior_call): Adjust clear_proceed_status call.
> * infcmd.c (proceed_thread_callback, continue_1, step_once)
> (jump_command): Adjust clear_proceed_status call.
> (signal_command): Warn if other thread that are resumed have
> signals that will be delivered. Adjust clear_proceed_status call.
> (until_next_command, finish_command)
> (proceed_after_attach_callback, attach_command_post_wait)
> (attach_command): Adjust clear_proceed_status call.
> * infrun.c (proceed_after_vfork_done): Likewise.
> (proceed_after_attach_callback): Adjust comment.
> (clear_proceed_status_thread): Clear stop_signal if not in pass
> state.
> (clear_proceed_status_callback): Delete.
> (clear_proceed_status): New 'step' parameter. Only clear the
> proceed status of threads the command being prepared is about to
> resume.
> (proceed): If passed in an explicit signal, override stop_signal
> with it. Don't pass the last stop signal to the thread we're
> resuming.
> (init_wait_for_inferior): Adjust clear_proceed_status call.
> (switch_back_to_stepped_thread): Clear the signal if it should not
> be passed.
> * infrun.h (clear_proceed_status): New 'step' parameter.
> (user_visible_resume_ptid): Add comment.
> * linux-nat.c (linux_nat_resume_callback): Don't check whether the
> signal is in pass state.
> * remote.c (append_pending_thread_resumptions): Likewise.
> * mi/mi-main.c (proceed_thread): Adjust clear_proceed_status call.
>
>
> gdb/doc/
> 2014-06-24 Pedro Alves <palves@redhat.com>
>
> * gdb.texinfo (Signaling) <signal command>: Explain what happens
> with multi-threaded programs.
>
> gdb/testsuite/
> 2014-06-24 Pedro Alves <palves@redhat.com>
>
> * gdb.threads/signal-command-handle-nopass.c: New file.
> * gdb.threads/signal-command-handle-nopass.exp: New file.
> * gdb.threads/signal-command-multiple-signals-pending.c: New file.
> * gdb.threads/signal-command-multiple-signals-pending.exp: New file.
> * gdb.threads/signal-delivered-right-thread.c: New file.
> * gdb.threads/signal-delivered-right-thread.exp: New file.
> ---
> gdb/NEWS | 11 ++
> gdb/breakpoint.c | 2 +-
> gdb/doc/gdb.texinfo | 10 +-
> gdb/gdbthread.h | 8 +-
> gdb/infcall.c | 2 +-
> gdb/infcmd.c | 64 ++++++--
> gdb/infrun.c | 93 +++++-------
> gdb/infrun.h | 8 +-
> gdb/linux-nat.c | 3 +-
> gdb/mi/mi-main.c | 2 +-
> gdb/remote.c | 3 +-
> .../gdb.threads/signal-command-handle-nopass.c | 49 ++++++
> .../gdb.threads/signal-command-handle-nopass.exp | 78 ++++++++++
> .../signal-command-multiple-signals-pending.c | 98 ++++++++++++
> .../signal-command-multiple-signals-pending.exp | 166 +++++++++++++++++++++
> .../gdb.threads/signal-delivered-right-thread.c | 61 ++++++++
> .../gdb.threads/signal-delivered-right-thread.exp | 85 +++++++++++
> 17 files changed, 664 insertions(+), 79 deletions(-)
> create mode 100644 gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
> create mode 100644 gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp
> create mode 100644 gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.c
> create mode 100644 gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp
> create mode 100644 gdb/testsuite/gdb.threads/signal-delivered-right-thread.c
> create mode 100644 gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index d9a19ae..2b61ff4 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,17 @@
>
> *** Changes since GDB 7.8
>
> +* On resume, GDB now always passes the signal the program had stopped
> + for to the thread the signal was sent to, even if the user changed
> + threads before resuming. Previously GDB would often (but not
> + always) deliver the signal to the thread that happens to be current
> + at resume time.
> +
> +* Conversely, the "signal" command now consistently delivers the
> + requested signal to the current thread. GDB now asks for
> + confirmation if the program had stopped for a signal and the user
> + switched threads meanwhile.
> +
> *** Changes in GDB 7.8
>
> * New command line options
> diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
> index b04f3b7..d75e8c7 100644
> --- a/gdb/breakpoint.c
> +++ b/gdb/breakpoint.c
> @@ -11670,7 +11670,7 @@ until_break_command (char *arg, int from_tty, int anywhere)
> int thread;
> struct thread_info *tp;
>
> - clear_proceed_status ();
> + clear_proceed_status (0);
>
> /* Set a breakpoint where the user wants it and at return from
> this function. */
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 8588f73..d0afc3d 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -16546,7 +16546,7 @@ detail.
> @table @code
> @kindex signal
> @item signal @var{signal}
> -Resume execution where your program stopped, but immediately give it the
> +Resume execution where your program is stopped, but immediately give it the
> signal @var{signal}. The @var{signal} can be the name or the number of a
> signal. For example, on many systems @code{signal 2} and @code{signal
> SIGINT} are both ways of sending an interrupt signal.
> @@ -16557,6 +16557,14 @@ a signal and would ordinarily see the signal when resumed with the
> @code{continue} command; @samp{signal 0} causes it to resume without a
> signal.
>
> +@emph{Note:} When debugging multi-threaded programs, @var{signal} is
> +delivered to the currently selected thread, not the thread that last
> +reported a stop. If for example the program last stopped on account
> +of a signal in thread 2 and you want to continue execution suppressing
> +that signal, you must have thread 2 selected as current before issuing
> +the @samp{signal 0} command. @value{GDBN} detects the scenario and
> +asks for confirmation if you had switched threads meanwhile.
> +
> @code{signal} does not repeat when you press @key{RET} a second time
> after executing the command.
> @end table
> diff --git a/gdb/gdbthread.h b/gdb/gdbthread.h
> index 9ef74cd..522b674 100644
> --- a/gdb/gdbthread.h
> +++ b/gdb/gdbthread.h
> @@ -135,7 +135,13 @@ struct thread_control_state
>
> struct thread_suspend_state
> {
> - /* Last signal that the inferior received (why it stopped). */
> + /* Last signal that the inferior received (why it stopped). When
> + the thread is resumed, this signal is delivered. Note: the
> + target should not check whether the signal is in pass state,
> + because the signal may have been explicitly passed with the
> + "signal" command, which overrides "handle nopass". If the signal
> + should be suppressed, the core will take care of clearing this
> + before the target is resumed. */
> enum gdb_signal stop_signal;
> };
>
> diff --git a/gdb/infcall.c b/gdb/infcall.c
> index 685b8a4..2aaac85 100644
> --- a/gdb/infcall.c
> +++ b/gdb/infcall.c
> @@ -391,7 +391,7 @@ run_inferior_call (struct thread_info *call_thread, CORE_ADDR real_pc)
>
> call_thread->control.in_infcall = 1;
>
> - clear_proceed_status ();
> + clear_proceed_status (0);
>
> disable_watchpoints_before_interactive_call_start ();
>
> diff --git a/gdb/infcmd.c b/gdb/infcmd.c
> index c4bb401..5c32c5c 100644
> --- a/gdb/infcmd.c
> +++ b/gdb/infcmd.c
> @@ -679,7 +679,7 @@ proceed_thread_callback (struct thread_info *thread, void *arg)
> return 0;
>
> switch_to_thread (thread->ptid);
> - clear_proceed_status ();
> + clear_proceed_status (0);
> proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
> return 0;
> }
> @@ -745,7 +745,7 @@ continue_1 (int all_threads)
> {
> ensure_valid_thread ();
> ensure_not_running ();
> - clear_proceed_status ();
> + clear_proceed_status (0);
> proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
> }
> }
> @@ -1013,7 +1013,7 @@ step_once (int skip_subroutines, int single_inst, int count, int thread)
> THREAD is set. */
> struct thread_info *tp = inferior_thread ();
>
> - clear_proceed_status ();
> + clear_proceed_status (!skip_subroutines);
> set_step_frame ();
>
> if (!single_inst)
> @@ -1186,7 +1186,7 @@ jump_command (char *arg, int from_tty)
> printf_filtered (".\n");
> }
>
> - clear_proceed_status ();
> + clear_proceed_status (0);
> proceed (addr, GDB_SIGNAL_0, 0);
> }
>
> @@ -1245,6 +1245,50 @@ signal_command (char *signum_exp, int from_tty)
> oursig = gdb_signal_from_command (num);
> }
>
> + /* Look for threads other than the current that this command ends up
> + resuming too (due to schedlock off), and warn if they'll get a
> + signal delivered. "signal 0" is used to suppress a previous
> + signal, but if the current thread is no longer the one that got
> + the signal, then the user is potentially suppressing the signal
> + of the wrong thread. */
> + if (!non_stop)
> + {
> + struct thread_info *tp;
> + ptid_t resume_ptid;
> + int must_confirm = 0;
> +
> + /* This indicates what will be resumed. Either a single thread,
> + a whole process, or all threads of all processes. */
> + resume_ptid = user_visible_resume_ptid (0);
> +
> + ALL_NON_EXITED_THREADS (tp)
> + {
> + if (ptid_equal (tp->ptid, inferior_ptid))
> + continue;
> + if (!ptid_match (tp->ptid, resume_ptid))
> + continue;
> +
> + if (tp->suspend.stop_signal != GDB_SIGNAL_0
> + && signal_pass_state (tp->suspend.stop_signal))
> + {
> + if (!must_confirm)
> + printf_unfiltered (_("Note:\n"));
> + printf_unfiltered (_(" Thread %d previously stopped with signal %s, %s.\n"),
> + tp->num,
> + gdb_signal_to_name (tp->suspend.stop_signal),
> + gdb_signal_to_string (tp->suspend.stop_signal));
> + must_confirm = 1;
> + }
> + }
> +
> + if (must_confirm
> + && !query (_("Continuing thread %d (the current thread) with specified signal will\n"
> + "still deliver the signals noted above to their respective threads.\n"
> + "Continue anyway? "),
> + inferior_thread ()->num))
> + error (_("Not confirmed."));
> + }
> +
> if (from_tty)
> {
> if (oursig == GDB_SIGNAL_0)
> @@ -1254,7 +1298,7 @@ signal_command (char *signum_exp, int from_tty)
> gdb_signal_to_name (oursig));
> }
>
> - clear_proceed_status ();
> + clear_proceed_status (0);
> proceed ((CORE_ADDR) -1, oursig, 0);
> }
>
> @@ -1295,7 +1339,7 @@ until_next_command (int from_tty)
> int thread = tp->num;
> struct cleanup *old_chain;
>
> - clear_proceed_status ();
> + clear_proceed_status (0);
> set_step_frame ();
>
> frame = get_current_frame ();
> @@ -1687,7 +1731,7 @@ finish_command (char *arg, int from_tty)
> if (frame == 0)
> error (_("\"finish\" not meaningful in the outermost frame."));
>
> - clear_proceed_status ();
> + clear_proceed_status (0);
>
> /* Finishing from an inline frame is completely different. We don't
> try to show the "return value" - no way to locate it. So we do
> @@ -2299,7 +2343,7 @@ proceed_after_attach_callback (struct thread_info *thread,
> && thread->suspend.stop_signal == GDB_SIGNAL_0)
> {
> switch_to_thread (thread->ptid);
> - clear_proceed_status ();
> + clear_proceed_status (0);
> proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
> }
>
> @@ -2399,7 +2443,7 @@ attach_command_post_wait (char *args, int from_tty, int async_exec)
> {
> if (inferior_thread ()->suspend.stop_signal == GDB_SIGNAL_0)
> {
> - clear_proceed_status ();
> + clear_proceed_status (0);
> proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
> }
> }
> @@ -2502,7 +2546,7 @@ attach_command (char *args, int from_tty)
> /* Set up execution context to know that we should return from
> wait_for_inferior as soon as the target reports a stop. */
> init_wait_for_inferior ();
> - clear_proceed_status ();
> + clear_proceed_status (0);
>
> if (non_stop)
> {
> diff --git a/gdb/infrun.c b/gdb/infrun.c
> index bef69a8..2626233 100644
> --- a/gdb/infrun.c
> +++ b/gdb/infrun.c
> @@ -623,7 +623,7 @@ proceed_after_vfork_done (struct thread_info *thread,
> target_pid_to_str (thread->ptid));
>
> switch_to_thread (thread->ptid);
> - clear_proceed_status ();
> + clear_proceed_status (0);
> proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
> }
>
> @@ -1721,15 +1721,6 @@ maybe_software_singlestep (struct gdbarch *gdbarch, CORE_ADDR pc)
> return hw_step;
> }
>
> -/* Return a ptid representing the set of threads that we will proceed,
> - in the perspective of the user/frontend. We may actually resume
> - fewer threads at first, e.g., if a thread is stopped at a
> - breakpoint that needs stepping-off, but that should not be visible
> - to the user/frontend, and neither should the frontend/user be
> - allowed to proceed any of the threads that happen to be stopped for
> - internal run control handling, if a previous command wanted them
> - resumed. */
> -
> ptid_t
> user_visible_resume_ptid (int step)
> {
> @@ -1757,6 +1748,12 @@ user_visible_resume_ptid (int step)
> resume_ptid = inferior_ptid;
> }
>
> + /* We may actually resume fewer threads at first, e.g., if a thread
> + is stopped at a breakpoint that needs stepping-off, but that
> + should not be visible to the user/frontend, and neither should
> + the frontend/user be allowed to proceed any of the threads that
> + happen to be stopped for internal run control handling, if a
> + previous command wanted them resumed. */
> return resume_ptid;
> }
>
> @@ -2032,6 +2029,11 @@ clear_proceed_status_thread (struct thread_info *tp)
> "infrun: clear_proceed_status_thread (%s)\n",
> target_pid_to_str (tp->ptid));
>
> + /* If this signal should not be seen by program, give it zero.
> + Used for debugging signals. */
> + if (!signal_pass_state (tp->suspend.stop_signal))
> + tp->suspend.stop_signal = GDB_SIGNAL_0;
> +
> tp->control.trap_expected = 0;
> tp->control.step_range_start = 0;
> tp->control.step_range_end = 0;
> @@ -2051,26 +2053,24 @@ clear_proceed_status_thread (struct thread_info *tp)
> bpstat_clear (&tp->control.stop_bpstat);
> }
>
> -static int
> -clear_proceed_status_callback (struct thread_info *tp, void *data)
> -{
> - if (is_exited (tp->ptid))
> - return 0;
> -
> - clear_proceed_status_thread (tp);
> - return 0;
> -}
> -
> void
> -clear_proceed_status (void)
> +clear_proceed_status (int step)
> {
> if (!non_stop)
> {
> - /* In all-stop mode, delete the per-thread status of all
> - threads, even if inferior_ptid is null_ptid, there may be
> - threads on the list. E.g., we may be launching a new
> - process, while selecting the executable. */
> - iterate_over_threads (clear_proceed_status_callback, NULL);
> + struct thread_info *tp;
> + ptid_t resume_ptid;
> +
> + resume_ptid = user_visible_resume_ptid (step);
> +
> + /* In all-stop mode, delete the per-thread status of all threads
> + we're about to resume, implicitly and explicitly. */
> + ALL_NON_EXITED_THREADS (tp)
> + {
> + if (!ptid_match (tp->ptid, resume_ptid))
> + continue;
> + clear_proceed_status_thread (tp);
> + }
> }
>
> if (!ptid_equal (inferior_ptid, null_ptid))
> @@ -2252,6 +2252,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
> regcache_write_pc (regcache, addr);
> }
>
> + if (siggnal != GDB_SIGNAL_DEFAULT)
> + tp->suspend.stop_signal = siggnal;
> +
> /* Record the interpreter that issued the execution command that
> caused this thread to resume. If the top level interpreter is
> MI/async, and the execution command was a CLI command
> @@ -2318,38 +2321,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal, int step)
>
> tp->control.trap_expected = tp->stepping_over_breakpoint;
>
> - if (!non_stop)
> - {
> - /* Pass the last stop signal to the thread we're resuming,
> - irrespective of whether the current thread is the thread that
> - got the last event or not. This was historically GDB's
> - behaviour before keeping a stop_signal per thread. */
> -
> - struct thread_info *last_thread;
> - ptid_t last_ptid;
> - struct target_waitstatus last_status;
> -
> - get_last_target_status (&last_ptid, &last_status);
> - if (!ptid_equal (inferior_ptid, last_ptid)
> - && !ptid_equal (last_ptid, null_ptid)
> - && !ptid_equal (last_ptid, minus_one_ptid))
> - {
> - last_thread = find_thread_ptid (last_ptid);
> - if (last_thread)
> - {
> - tp->suspend.stop_signal = last_thread->suspend.stop_signal;
> - last_thread->suspend.stop_signal = GDB_SIGNAL_0;
> - }
> - }
> - }
> -
> - if (siggnal != GDB_SIGNAL_DEFAULT)
> - tp->suspend.stop_signal = siggnal;
> - /* If this signal should not be seen by program,
> - give it zero. Used for debugging signals. */
> - else if (!signal_program[tp->suspend.stop_signal])
> - tp->suspend.stop_signal = GDB_SIGNAL_0;
> -
> annotate_starting ();
>
> /* Make sure that output from GDB appears before output from the
> @@ -2443,7 +2414,7 @@ init_wait_for_inferior (void)
>
> breakpoint_init_inferior (inf_starting);
>
> - clear_proceed_status ();
> + clear_proceed_status (0);
>
> target_last_wait_ptid = minus_one_ptid;
>
> @@ -5190,6 +5161,10 @@ switch_back_to_stepped_thread (struct execution_control_state *ecs)
> what keep_going does as well, if we call it. */
> ecs->event_thread->control.trap_expected = 0;
>
> + /* Likewise, clear the signal if it should not be passed. */
> + if (!signal_program[ecs->event_thread->suspend.stop_signal])
> + ecs->event_thread->suspend.stop_signal = GDB_SIGNAL_0;
> +
> /* If scheduler locking applies even if not stepping, there's no
> need to walk over threads. Above we've checked whether the
> current thread is stepping. If some other thread not the
> diff --git a/gdb/infrun.h b/gdb/infrun.h
> index 66612a8..35b2246 100644
> --- a/gdb/infrun.h
> +++ b/gdb/infrun.h
> @@ -83,7 +83,11 @@ extern struct regcache *stop_registers;
>
> extern void start_remote (int from_tty);
>
> -extern void clear_proceed_status (void);
> +/* Clear out all variables saying what to do when inferior is
> + continued or stepped. First do this, then set the ones you want,
> + then call `proceed'. STEP indicates whether we're preparing for a
> + step/stepi command. */
> +extern void clear_proceed_status (int step);
>
> extern void proceed (CORE_ADDR, enum gdb_signal, int);
>
> @@ -91,6 +95,8 @@ extern void proceed (CORE_ADDR, enum gdb_signal, int);
> Normally, use `proceed', which handles a lot of bookkeeping. */
> extern void resume (int, enum gdb_signal);
>
> +/* Return a ptid representing the set of threads that we will proceed,
> + in the perspective of the user/frontend. */
> extern ptid_t user_visible_resume_ptid (int step);
>
> extern void wait_for_inferior (void);
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index c9677ca..8f2600d 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -1694,8 +1694,7 @@ linux_nat_resume_callback (struct lwp_info *lp, void *except)
> thread = find_thread_ptid (lp->ptid);
> if (thread != NULL)
> {
> - if (signal_pass_state (thread->suspend.stop_signal))
> - signo = thread->suspend.stop_signal;
> + signo = thread->suspend.stop_signal;
> thread->suspend.stop_signal = GDB_SIGNAL_0;
> }
> }
> diff --git a/gdb/mi/mi-main.c b/gdb/mi/mi-main.c
> index 96dfc71..26389f1 100644
> --- a/gdb/mi/mi-main.c
> +++ b/gdb/mi/mi-main.c
> @@ -253,7 +253,7 @@ proceed_thread (struct thread_info *thread, int pid)
> return;
>
> switch_to_thread (thread->ptid);
> - clear_proceed_status ();
> + clear_proceed_status (0);
> proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT, 0);
> }
>
> diff --git a/gdb/remote.c b/gdb/remote.c
> index b5318f1..e56cfea 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -4629,8 +4629,7 @@ append_pending_thread_resumptions (char *p, char *endp, ptid_t ptid)
> ALL_NON_EXITED_THREADS (thread)
> if (ptid_match (thread->ptid, ptid)
> && !ptid_equal (inferior_ptid, thread->ptid)
> - && thread->suspend.stop_signal != GDB_SIGNAL_0
> - && signal_pass_state (thread->suspend.stop_signal))
> + && thread->suspend.stop_signal != GDB_SIGNAL_0)
> {
> p = append_resumption (p, endp, thread->ptid,
> 0, thread->suspend.stop_signal);
> diff --git a/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
> new file mode 100644
> index 0000000..abca2f9
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.c
> @@ -0,0 +1,49 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> + Copyright 2014 Free Software Foundation, Inc.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <pthread.h>
> +#include <signal.h>
> +
> +void
> +handler (int sig)
> +{
> +}
> +
> +void *
> +thread_function (void *arg)
> +{
> + volatile unsigned int i = 1;
> +
> + while (i != 0)
> + usleep (1);
> +}
> +
> +int
> +main (void)
> +{
> + pthread_t child_thread;
> + int i;
> +
> + signal (SIGUSR1, handler);
> + pthread_create (&child_thread, NULL, thread_function, NULL);
> + pthread_join (child_thread, NULL);
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp
> new file mode 100644
> index 0000000..3ea9cf8
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-command-handle-nopass.exp
> @@ -0,0 +1,78 @@
> +# Copyright (C) 2014 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +# Test that an explicit "signal FOO" delivers FOO even if "handle" for
> +# that same signal is set to "nopass". Also make sure the signal is
> +# delivered to the right thread, even if GDB has to step over a
> +# breakpoint in some other thread first.
> +
> +if [target_info exists gdb,nosignals] {
> + verbose "Skipping ${testfile}.exp because of nosignals."
> + return -1
> +}
> +
> +standard_testfile
> +
> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
> + executable { debug }] != "" } {
> + return -1
> +}
> +
> +# Run the test proper. STEP_OVER indicates whether we leave in place
> +# a breakpoint that needs to be stepped over when we explicitly
> +# request a signal be delivered with the "signal" command.
> +
> +proc test { step_over } {
> + global srcfile binfile
> +
> + with_test_prefix "step-over $step_over" {
> + clean_restart ${binfile}
> +
> + if ![runto_main] then {
> + fail "Can't run to main"
> + return 0
> + }
> +
> + gdb_test "handle SIGUSR1 stop print nopass"
> +
> + gdb_test "b thread_function" "Breakpoint .* at .*$srcfile.*"
> + gdb_test "continue" "thread_function.*" "stopped in thread"
> +
> + # Thread 2 is stopped at a breakpoint. If we leave the
> + # breakpoint in place, GDB needs to move thread 2 past the
> + # breakpoint before delivering the signal to thread 1. We
> + # want to be sure that GDB doesn't mistakenly deliver the
> + # signal to thread 1 while doing that.
> + if { $step_over == "no" } {
> + delete_breakpoints
> + }
> +
> + gdb_test "break handler" "Breakpoint .* at .*$srcfile.*"
> +
> + gdb_test "thread 1" "Switching to thread 1.*"
> +
> + set pattern "\\\* 1\[ \t\]+Thread.*"
> +
> + gdb_test "info threads" $pattern "thread 1 selected"
> +
> + gdb_test "signal SIGUSR1" "handler .*"
> +
> + gdb_test "info threads" $pattern "thread 1 got the signal"
> + }
> +}
> +
> +foreach stepover {"yes" "no"} {
> + test $stepover
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.c b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.c
> new file mode 100644
> index 0000000..2fc5f53
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.c
> @@ -0,0 +1,98 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> + Copyright 2014 Free Software Foundation, Inc.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <pthread.h>
> +#include <signal.h>
> +
> +pthread_barrier_t barrier;
> +sig_atomic_t got_sigusr1;
> +sig_atomic_t got_sigusr2;
> +
> +void
> +handler_sigusr1 (int sig)
> +{
> + got_sigusr1 = 1;
> +}
> +
> +void
> +handler_sigusr2 (int sig)
> +{
> + got_sigusr2 = 1;
> +}
> +
> +void *
> +thread_function (void *arg)
> +{
> + volatile unsigned int count = 1;
> +
> + pthread_barrier_wait (&barrier);
> +
> + while (count++ != 0)
> + {
> + if (got_sigusr1 && got_sigusr2)
> + break;
> + usleep (1);
> + }
> +}
> +
> +void
> +all_threads_started (void)
> +{
> +}
> +
> +void
> +all_threads_signalled (void)
> +{
> +}
> +
> +void
> +end (void)
> +{
> +}
> +
> +int
> +main (void)
> +{
> + pthread_t child_thread[2];
> + int i;
> +
> + signal (SIGUSR1, handler_sigusr1);
> + signal (SIGUSR2, handler_sigusr2);
> +
> + pthread_barrier_init (&barrier, NULL, 3);
> +
> + for (i = 0; i < 2; i++)
> + pthread_create (&child_thread[i], NULL, thread_function, NULL);
> +
> + pthread_barrier_wait (&barrier);
> +
> + all_threads_started ();
> +
> + pthread_kill (child_thread[0], SIGUSR1);
> + pthread_kill (child_thread[1], SIGUSR2);
> +
> + all_threads_signalled ();
> +
> + for (i = 0; i < 2; i++)
> + pthread_join (child_thread[i], NULL);
> +
> + end ();
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp
> new file mode 100644
> index 0000000..b5ec00a
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-command-multiple-signals-pending.exp
> @@ -0,0 +1,166 @@
> +# Copyright (C) 2014 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +# Test that "signal FOO" behaves correctly when we have multiple
> +# threads that have stopped for a signal.
> +
> +if [target_info exists gdb,nosignals] {
> + verbose "Skipping ${testfile}.exp because of nosignals."
> + return -1
> +}
> +
> +standard_testfile
> +
> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
> + executable { debug }] != "" } {
> + return -1
> +}
> +
> +# Run the test proper. SCHEDLOCK indicates which variant (around
> +# scheduler-locking) of the test to perform.
> +
> +proc test { schedlock } {
> + global srcfile binfile
> +
> + with_test_prefix "schedlock $schedlock" {
> + clean_restart ${binfile}
> +
> + if ![runto_main] then {
> + fail "Can't run to main"
> + return 0
> + }
> +
> + gdb_test "handle SIGUSR1 stop print pass"
> + gdb_test "handle SIGUSR2 stop print pass"
> +
> + gdb_test "break all_threads_started" "Breakpoint .* at .*$srcfile.*"
> + gdb_test "continue" "all_threads_started.*"
> +
> + # Using schedlock, let the main thread queue a signal for each
> + # non-main thread.
> + gdb_test_no_output "set scheduler-locking on"
> +
> + gdb_test "break all_threads_signalled" "Breakpoint .* at .*$srcfile.*"
> + gdb_test "continue" "all_threads_signalled.*"
> +
> + gdb_test "info threads" "\\\* 1\[ \t\]+Thread.*" "thread 1 selected"
> +
> + # With schedlock still enabled, let each thread report its
> + # signal.
> +
> + gdb_test "thread 3" "Switching to thread 3.*"
> + gdb_test "continue" "Program received signal SIGUSR2.*" "stop with SIGUSR2"
> + gdb_test "thread 2" "Switching to thread 2.*"
> + gdb_test "continue" "Program received signal SIGUSR1.*" "stop with SIGUSR1"
> +
> + gdb_test "break handler_sigusr1" "Breakpoint .* at .*$srcfile.*"
> + gdb_test "break handler_sigusr2" "Breakpoint .* at .*$srcfile.*"
> +
> + set handler_re "Breakpoint .*, handler_sigusr. \\(sig=.*\\) at .*"
> +
> + # Now test the "signal" command with either scheduler locking
> + # enabled or disabled.
> +
> + if { $schedlock == "off" } {
> + # With scheduler locking off, switch to the main thread
> + # and issue "signal 0". "signal 0" should then warn that
> + # two threads have signals that will be delivered. When
> + # we let the command proceed, a signal should be
> + # delivered, and thus the corresponding breakpoint in the
> + # signal handler should trigger.
> +
> + gdb_test_no_output "set scheduler-locking off"
> + gdb_test "thread 1" "Switching to thread 1.*"
> +
> + set queried 0
> + set test "signal command queries"
> + gdb_test_multiple "signal 0" $test {
> + -re "stopped with.*stopped with.*stopped with.*Continue anyway.*y or n. $" {
> + fail "$test (too many threads noted)"
> + set queried 1
> + }
> + -re "stopped with signal SIGUSR.*\r\nContinuing .*still deliver .*Continue anyway.*y or n. $" {
> + pass $test
> + set queried 1
> + }
> + -re "Continue anyway.*y or n. $" {
> + fail "$test (no threads noted)"
> + set queried 1
> + }
> + }
> +
> + # Continuing should stop in one of the signal handlers.
> + # Which thread runs first is not determinate.
> + if {$queried} {
> + gdb_test "y" "$handler_re" "one signal delivered"
> + }
> +
> + # Continuing a second time should stop in the other
> + # handler.
> + with_test_prefix "second signal" {
> + gdb_test "continue" "$handler_re" "signal delivered"
> + }
> + } else {
> + # With scheduler locking on, stay with thread 2 selected,
> + # and try to deliver its signal explicitly. The "signal"
> + # command should then warn that one other thread has a
> + # signal that will be delivered. When we let the command
> + # proceed, the current thread's signal should be
> + # delivered, and thus the corresponding breakpoint in the
> + # signal handler should trigger.
> + gdb_test "signal SIGUSR1" \
> + "Breakpoint .*, handler_sigusr1 \\(sig=.*\\) at .*" \
> + "signal command does not query, signal delivered"
> +
> + with_test_prefix "second signal" {
> + # The other thread had stopped for a signal too, and
> + # it wasn't resumed yet. Disabling schedlock and
> + # trying "signal 0" from the main thread should warn
> + # again.
> + gdb_test_no_output "set scheduler-locking off"
> +
> + set queried 0
> + set test "signal command queries"
> + gdb_test_multiple "signal 0" $test {
> + -re "stopped with.*stopped with.*Continue anyway.*y or n. $" {
> + fail "$test (too many threads noted)"
> + set queried 1
> + }
> + -re "stopped with signal SIGUSR.*\r\nContinuing .*still deliver .*Continue anyway.*y or n. $" {
> + pass $test
> + set queried 1
> + }
> + -re "Continue anyway.*y or n. $" {
> + fail "$test (no threads noted)"
> + set queried 1
> + }
> + }
> +
> + if {$queried} {
> + gdb_test "y" "Breakpoint .*, handler_sigusr2 \\(sig=.*\\) at .*" "signal delivered"
> + }
> + }
> + }
> +
> + # Both threads got their signal. Continuing again should
> + # neither intercept nor deliver any other signal.
> + gdb_test "b end" "Breakpoint .* at .*$srcfile.*"
> + gdb_test "continue" "end .*" "no more signals"
> + }
> +}
> +
> +foreach schedlock {"off" "on"} {
> + test $schedlock
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-delivered-right-thread.c b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.c
> new file mode 100644
> index 0000000..66b68a8
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.c
> @@ -0,0 +1,61 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> + Copyright 2014 Free Software Foundation, Inc.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +#include <pthread.h>
> +#include <signal.h>
> +
> +pthread_barrier_t barrier;
> +
> +void
> +handler (int sig)
> +{
> +}
> +
> +void *
> +thread_function (void *arg)
> +{
> + pthread_barrier_wait (&barrier);
> +
> + while (1)
> + usleep (1);
> +}
> +
> +int
> +main (void)
> +{
> + pthread_t child_thread[2];
> + int i;
> +
> + signal (SIGUSR1, handler);
> +
> + pthread_barrier_init (&barrier, NULL, 3);
> +
> + for (i = 0; i < 2; i++)
> + pthread_create (&child_thread[i], NULL, thread_function, NULL);
> +
> + pthread_barrier_wait (&barrier);
> +
> + pthread_kill (child_thread[0], SIGUSR1);
> +
> + for (i = 0; i < 2; i++)
> + pthread_join (child_thread[i], NULL);
> +
> + return 0;
> +}
> diff --git a/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp
> new file mode 100644
> index 0000000..4243495
> --- /dev/null
> +++ b/gdb/testsuite/gdb.threads/signal-delivered-right-thread.exp
> @@ -0,0 +1,85 @@
> +# Copyright (C) 2014 Free Software Foundation, Inc.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +if [target_info exists gdb,nosignals] {
> + verbose "Skipping ${testfile}.exp because of nosignals."
> + return -1
> +}
> +
> +standard_testfile
> +
> +if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
> + executable { debug }] != "" } {
> + return -1
> +}
> +
> +# Run test proper. COMMAND indicates whether to resume the inferior
> +# with "signal 0" or "continue".
> +
> +proc test { command } {
> + global srcfile binfile
> +
> + with_test_prefix "$command" {
> + clean_restart ${binfile}
> +
> + if ![runto_main] then {
> + fail "Can't run to main"
> + return 0
> + }
> +
> + gdb_test "handle SIGUSR1 stop print pass"
> +
> + gdb_test "continue" "Program received signal SIGUSR1.*" "stop with SIGUSR1"
> +
> + set pattern "\\\* 2\[ \t\]+Thread.*"
> +
> + gdb_test "info threads" $pattern "thread 2 intercepted signal"
> +
> + gdb_test "break handler" "Breakpoint .* at .*$srcfile.*"
> +
> + gdb_test "thread 1" "Switching to thread 1.*"
> +
> + if { $command == "continue" } {
> + gdb_test "continue" "handler .*"
> + } elseif { $command == "signal 0" } {
> + set queried 0
> + set test "signal 0 queries"
> + gdb_test_multiple "signal 0" $test {
> + -re "stopped with.*stopped with.*Continue anyway.*y or n. $" {
> + fail "$test (multiple threads noted)"
> + set queried 1
> + }
> + -re "stopped with signal SIGUSR1.*\r\nContinuing .*still deliver .*Continue anyway.*y or n. $" {
> + pass $test
> + set queried 1
> + }
> + -re "Continue anyway.*y or n. $" {
> + fail "$test (no threads noted)"
> + set queried 1
> + }
> + }
> +
> + if {$queried} {
> + gdb_test "y" "handler .*" "signal is delivered"
> + }
> + }
> +
> + gdb_test "info threads" $pattern "thread 2 got the signal"
> + }
> +}
> +
> +foreach command {"continue" "signal 0"} {
> + test $command
> +}
>