From 3b12939dfc2399200f243851fd55d0e392b64165 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Tue, 21 Jun 2016 01:11:51 +0100 Subject: [PATCH] Replace the sync_execution global with a new enum prompt_state tristate When sync_execution (a boolean) is true, it means we're running a foreground command -- we hide the prompt stop listening to input, give the inferior the terminal, then go to the event loop waiting for the target to stop. With multiple independent UIs, we need to track whether each UI is synchronously blocked waiting for the target. IOW, if you do "continue" in one console, that console stops accepting commands, but you should still be free to type other commands in the others consoles. Just simply making sync_execution be per-UI alone not sufficient, because of this in fetch_inferior_event: /* If the inferior was in sync execution mode, and now isn't, restore the prompt (a synchronous execution command has finished, and we're ready for input). */ if (current_ui->async && was_sync && !sync_execution) observer_notify_sync_execution_done (); We'd have to record at entry the "was_sync" state for each UI, not just of the current UI. This patch instead replaces the sync_execution flag by a per-UI tristate flag indicating the command line prompt state: enum prompt_state { /* The command line is blocked simulating synchronous execution. This is used to implement the foreground execution commands ('run', 'continue', etc.). We won't display the prompt and accept further commands until the execution is actually over. */ PROMPT_BLOCKED, /* The command finished; display the prompt before returning back to the top level. */ PROMPT_NEEDED, /* We've displayed the prompt already, ready for input. */ PROMPTED, ; I think the end result is _much_ clearer than the current code, and, it addresses the original motivation too. gdb/ChangeLog: 2016-06-21 Pedro Alves * annotate.c: Include top.h. (async_background_execution_p): Delete. (print_value_flags): Check the UI's prompt state rather then async_background_execution_p. * event-loop.c (start_event_loop): Set the prompt state to PROMPT_NEEDED. * event-top.c (display_gdb_prompt, async_enable_stdin) (async_disable_stdin): Check the current UI's prompt state instead of the sync_execution global. (command_line_handler): Set the prompt state to PROMPT_NEEDED before running a command, and display the prompt if still needed afterwards. * infcall.c (struct call_thread_fsm) : New field. (new_call_thread_fsm): New parameter 'waiting_ui'. Store it. (call_thread_fsm_should_stop): Set the prompt state to PROMPT_NEEDED. (run_inferior_call): Adjust to temporarily set the prompt state to PROMPT_BLOCKED instead of using the sync_execution global. (call_function_by_hand_dummy): Pass the current UI to new_call_thread_fsm. * infcmd.c: Include top.h. (continue_1): Check the current UI's prompt state instead of the sync_execution global. (continue_command): Validate global execution state before calling prepare_execution_command. (step_1): Call all_uis_check_sync_execution_done. (attach_post_wait): Don't call async_enable_stdin here. Remove reference to sync_execution. * infrun.c (sync_execution): Delete global. (follow_fork_inferior) (reinstall_readline_callback_handler_cleanup): Check the current UI's prompt state instead of the sync_execution global. (check_curr_ui_sync_execution_done) (all_uis_check_sync_execution_done): New functions. (fetch_inferior_event): Call all_uis_check_sync_execution_done instead of trying to determine whether the global sync execution changed. (handle_no_resumed): Check the prompt state of all UIs. (normal_stop): Emit the no unwait-for even to all PROMPT_BLOCKED UIs. Emit the "Switching to" notification to all UIs. Enable stdin in all UIs. * infrun.h (sync_execution): Delete. (all_uis_check_sync_execution_done): Declare. * main.c (captured_command_loop): Don't call interp_pre_command_loop if the prompt is blocked. (catch_command_errors, catch_command_errors_const): Adjust. (captured_main): Set the initial prompt state to PROMPT_NEEDED. * mi/mi-interp.c (display_mi_prompt): Set the prompt state to PROMPTED. (mi_interpreter_resume): Don't clear sync_execution. Remove hack comment. (mi_execute_command_input_handler): Set the prompt state to PROMPT_NEEDED before executing the command, and only display the prompt if the prompt state is PROMPT_NEEDED afterwards. (mi_on_resume_1): Adjust to check the prompt state. * target.c (target_terminal_inferior): Adjust to check the prompt state. * top.c (wait_sync_command_done, maybe_wait_sync_command_done) (execute_command): Check the current UI's prompt state instead of sync_execution. * top.h (enum prompt_state): New. (struct ui) : New field. (ALL_UIS): New macro. --- gdb/ChangeLog | 66 ++++++++++++++++++++++++++ gdb/annotate.c | 15 ++---- gdb/event-loop.c | 1 + gdb/event-top.c | 29 ++++++++---- gdb/infcall.c | 34 ++++++++++---- gdb/infcmd.c | 28 +++++++---- gdb/infrun.c | 114 ++++++++++++++++++++++++++++++++------------- gdb/infrun.h | 10 ++-- gdb/main.c | 11 +++-- gdb/mi/mi-interp.c | 25 +++++----- gdb/target.c | 6 +-- gdb/top.c | 8 ++-- gdb/top.h | 25 ++++++++++ 13 files changed, 270 insertions(+), 102 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index e3293d15ea3..edef298a6d4 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,69 @@ +2016-06-21 Pedro Alves + + * annotate.c: Include top.h. + (async_background_execution_p): Delete. + (print_value_flags): Check the UI's prompt state rather then + async_background_execution_p. + * event-loop.c (start_event_loop): Set the prompt state to + PROMPT_NEEDED. + * event-top.c (display_gdb_prompt, async_enable_stdin) + (async_disable_stdin): Check the current UI's prompt state instead + of the sync_execution global. + (command_line_handler): Set the prompt state to PROMPT_NEEDED + before running a command, and display the prompt if still needed + afterwards. + * infcall.c (struct call_thread_fsm) : New field. + (new_call_thread_fsm): New parameter 'waiting_ui'. Store it. + (call_thread_fsm_should_stop): Set the prompt state to + PROMPT_NEEDED. + (run_inferior_call): Adjust to temporarily set the prompt state to + PROMPT_BLOCKED instead of using the sync_execution global. + (call_function_by_hand_dummy): Pass the current UI to + new_call_thread_fsm. + * infcmd.c: Include top.h. + (continue_1): Check the current UI's prompt state instead of the + sync_execution global. + (continue_command): Validate global execution state before calling + prepare_execution_command. + (step_1): Call all_uis_check_sync_execution_done. + (attach_post_wait): Don't call async_enable_stdin here. Remove + reference to sync_execution. + * infrun.c (sync_execution): Delete global. + (follow_fork_inferior) + (reinstall_readline_callback_handler_cleanup): Check the current + UI's prompt state instead of the sync_execution global. + (check_curr_ui_sync_execution_done) + (all_uis_check_sync_execution_done): New functions. + (fetch_inferior_event): Call all_uis_check_sync_execution_done + instead of trying to determine whether the global sync execution + changed. + (handle_no_resumed): Check the prompt state of all UIs. + (normal_stop): Emit the no unwait-for even to all PROMPT_BLOCKED + UIs. Emit the "Switching to" notification to all UIs. Enable + stdin in all UIs. + * infrun.h (sync_execution): Delete. + (all_uis_check_sync_execution_done): Declare. + * main.c (captured_command_loop): Don't call + interp_pre_command_loop if the prompt is blocked. + (catch_command_errors, catch_command_errors_const): Adjust. + (captured_main): Set the initial prompt state to PROMPT_NEEDED. + * mi/mi-interp.c (display_mi_prompt): Set the prompt state to + PROMPTED. + (mi_interpreter_resume): Don't clear sync_execution. Remove hack + comment. + (mi_execute_command_input_handler): Set the prompt state to + PROMPT_NEEDED before executing the command, and only display the + prompt if the prompt state is PROMPT_NEEDED afterwards. + (mi_on_resume_1): Adjust to check the prompt state. + * target.c (target_terminal_inferior): Adjust to check the prompt + state. + * top.c (wait_sync_command_done, maybe_wait_sync_command_done) + (execute_command): Check the current UI's prompt state instead of + sync_execution. + * top.h (enum prompt_state): New. + (struct ui) : New field. + (ALL_UIS): New macro. + 2016-06-21 Pedro Alves * top.c (gdb_secondary_prompt_depth): Delete. diff --git a/gdb/annotate.c b/gdb/annotate.c index 117f12203b3..64175a442b9 100644 --- a/gdb/annotate.c +++ b/gdb/annotate.c @@ -25,6 +25,7 @@ #include "observer.h" #include "inferior.h" #include "infrun.h" +#include "top.h" /* Prototypes for local functions. */ @@ -46,16 +47,6 @@ void (*deprecated_annotate_signal_hook) (void); static int frames_invalid_emitted; static int breakpoints_invalid_emitted; -/* True if the target can async, and a synchronous execution command - is not in progress. If true, input is accepted, so don't suppress - annotations. */ - -static int -async_background_execution_p (void) -{ - return (target_can_async_p () && !sync_execution); -} - static void print_value_flags (struct type *t) { @@ -70,7 +61,7 @@ annotate_breakpoints_invalid (void) { if (annotation_level == 2 && (!breakpoints_invalid_emitted - || async_background_execution_p ())) + || current_ui->prompt_state != PROMPT_BLOCKED)) { /* If the inferior owns the terminal (e.g., we're resuming), make sure to leave with the inferior still owning it. */ @@ -217,7 +208,7 @@ annotate_frames_invalid (void) { if (annotation_level == 2 && (!frames_invalid_emitted - || async_background_execution_p ())) + || current_ui->prompt_state != PROMPT_BLOCKED)) { /* If the inferior owns the terminal (e.g., we're resuming), make sure to leave with the inferior still owning it. */ diff --git a/gdb/event-loop.c b/gdb/event-loop.c index fe283050507..f94a6fa135f 100644 --- a/gdb/event-loop.c +++ b/gdb/event-loop.c @@ -381,6 +381,7 @@ start_event_loop (void) /* If we long-jumped out of do_one_event, we probably didn't get around to resetting the prompt, which leaves readline in a messed-up state. Reset it here. */ + current_ui->prompt_state = PROMPT_NEEDED; observer_notify_command_error (); /* This call looks bizarre, but it is required. If the user entered a command that caused an error, diff --git a/gdb/event-top.c b/gdb/event-top.c index 5e42c560513..c9e754875df 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -354,7 +354,11 @@ display_gdb_prompt (const char *new_prompt) IE, displayed but not set. */ if (! new_prompt) { - if (sync_execution) + struct ui *ui = current_ui; + + if (ui->prompt_state == PROMPTED) + internal_error (__FILE__, __LINE__, _("double prompt")); + else if (ui->prompt_state == PROMPT_BLOCKED) { /* This is to trick readline into not trying to display the prompt. Even though we display the prompt using this @@ -377,10 +381,11 @@ display_gdb_prompt (const char *new_prompt) do_cleanups (old_chain); return; } - else + else if (ui->prompt_state == PROMPT_NEEDED) { /* Display the top level prompt. */ actual_gdb_prompt = top_level_prompt (); + ui->prompt_state = PROMPTED; } } else @@ -541,14 +546,12 @@ stdin_event_handler (int error, gdb_client_data client_data) void async_enable_stdin (void) { - if (sync_execution) + struct ui *ui = current_ui; + + if (ui->prompt_state == PROMPT_BLOCKED) { - /* See NOTE in async_disable_stdin(). */ - /* FIXME: cagney/1999-09-27: Call this before clearing - sync_execution. Current target_terminal_ours() implementations - check for sync_execution before switching the terminal. */ target_terminal_ours (); - sync_execution = 0; + ui->prompt_state = PROMPT_NEEDED; } } @@ -558,7 +561,9 @@ async_enable_stdin (void) void async_disable_stdin (void) { - sync_execution = 1; + struct ui *ui = current_ui; + + ui->prompt_state = PROMPT_BLOCKED; } @@ -774,8 +779,12 @@ command_line_handler (char *rl) } else { + ui->prompt_state = PROMPT_NEEDED; + command_handler (cmd); - display_gdb_prompt (0); + + if (ui->prompt_state != PROMPTED) + display_gdb_prompt (0); } } diff --git a/gdb/infcall.c b/gdb/infcall.c index 11f5abacb51..d491f9526fa 100644 --- a/gdb/infcall.c +++ b/gdb/infcall.c @@ -464,6 +464,10 @@ struct call_thread_fsm /* The called function's return value. This is extracted from the target before the dummy frame is popped. */ struct value *return_value; + + /* The top level that started the infcall (and is synchronously + waiting for it to end). */ + struct ui *waiting_ui; }; static int call_thread_fsm_should_stop (struct thread_fsm *self); @@ -484,7 +488,8 @@ static struct thread_fsm_ops call_thread_fsm_ops = /* Allocate a new call_thread_fsm object. */ static struct call_thread_fsm * -new_call_thread_fsm (struct gdbarch *gdbarch, struct value *function, +new_call_thread_fsm (struct ui *waiting_ui, + struct gdbarch *gdbarch, struct value *function, struct type *value_type, int struct_return_p, CORE_ADDR struct_addr) { @@ -499,6 +504,8 @@ new_call_thread_fsm (struct gdbarch *gdbarch, struct value *function, sm->return_meta_info.struct_return_p = struct_return_p; sm->return_meta_info.struct_addr = struct_addr; + sm->waiting_ui = waiting_ui; + return sm; } @@ -511,6 +518,8 @@ call_thread_fsm_should_stop (struct thread_fsm *self) if (stop_stack_dummy == STOP_STACK_DUMMY) { + struct cleanup *old_chain; + /* Done. */ thread_fsm_set_finished (self); @@ -520,7 +529,13 @@ call_thread_fsm_should_stop (struct thread_fsm *self) f->return_value = get_call_return_value (&f->return_meta_info); /* Break out of wait_sync_command_done. */ - async_enable_stdin (); + old_chain = make_cleanup (restore_ui_cleanup, current_ui); + current_ui = f->waiting_ui; + target_terminal_ours (); + f->waiting_ui->prompt_state = PROMPT_NEEDED; + + /* This restores the previous UI. */ + do_cleanups (old_chain); } return 1; @@ -558,12 +573,12 @@ run_inferior_call (struct call_thread_fsm *sm, struct gdb_exception caught_error = exception_none; int saved_in_infcall = call_thread->control.in_infcall; ptid_t call_thread_ptid = call_thread->ptid; - int saved_sync_execution = sync_execution; + enum prompt_state saved_prompt_state = current_ui->prompt_state; int was_running = call_thread->state == THREAD_RUNNING; int saved_ui_async = current_ui->async; /* Infcalls run synchronously, in the foreground. */ - sync_execution = 1; + current_ui->prompt_state = PROMPT_BLOCKED; /* So that we don't print the prompt prematurely in fetch_inferior_event. */ current_ui->async = 0; @@ -596,11 +611,11 @@ run_inferior_call (struct call_thread_fsm *sm, } END_CATCH - /* If GDB was previously in sync execution mode, then ensure that it - remains so. normal_stop calls async_enable_stdin, so reset it - again here. In other cases, stdin will be re-enabled by + /* If GDB has the prompt blocked before, then ensure that it remains + so. normal_stop calls async_enable_stdin, so reset the prompt + state again here. In other cases, stdin will be re-enabled by inferior_event_handler, when an exception is thrown. */ - sync_execution = saved_sync_execution; + current_ui->prompt_state = saved_prompt_state; current_ui->async = saved_ui_async; /* At this point the current thread may have changed. Refresh @@ -1120,7 +1135,8 @@ call_function_by_hand_dummy (struct value *function, not report the stop to the user, and captures the return value before the dummy frame is popped. run_inferior_call registers it with the thread ASAP. */ - sm = new_call_thread_fsm (gdbarch, function, + sm = new_call_thread_fsm (current_ui, + gdbarch, function, values_type, struct_return || hidden_first_param_p, struct_addr); diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 1defb13806d..5c3f212bfe5 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -56,6 +56,7 @@ #include "cli/cli-utils.h" #include "infcall.h" #include "thread-fsm.h" +#include "top.h" /* Local functions: */ @@ -730,7 +731,7 @@ continue_1 (int all_threads) iterate_over_threads (proceed_thread_callback, NULL); - if (sync_execution) + if (current_ui->prompt_state == PROMPT_BLOCKED) { /* If all threads in the target were already running, proceed_thread_callback ends up never calling proceed, @@ -775,8 +776,6 @@ continue_command (char *args, int from_tty) args = strip_bg_char (args, &async_exec); args_chain = make_cleanup (xfree, args); - prepare_execution_command (¤t_target, async_exec); - if (args != NULL) { if (startswith (args, "-a")) @@ -840,6 +839,17 @@ continue_command (char *args, int from_tty) /* Done with ARGS. */ do_cleanups (args_chain); + ERROR_NO_INFERIOR; + ensure_not_tfind_mode (); + + if (!non_stop || !all_threads) + { + ensure_valid_thread (); + ensure_not_running (); + } + + prepare_execution_command (¤t_target, async_exec); + if (from_tty) printf_filtered (_("Continuing.\n")); @@ -1014,11 +1024,15 @@ step_1 (int skip_subroutines, int single_inst, char *count_string) proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT); else { + int proceeded; + /* Stepped into an inline frame. Pretend that we've stopped. */ thread_fsm_clean_up (thr->thread_fsm); - normal_stop (); - inferior_event_handler (INF_EXEC_COMPLETE, NULL); + proceeded = normal_stop (); + if (!proceeded) + inferior_event_handler (INF_EXEC_COMPLETE, NULL); + all_uis_check_sync_execution_done (); } } @@ -2710,8 +2724,6 @@ attach_post_wait (char *args, int from_tty, enum attach_post_wait_mode mode) /* The user requested a plain `attach', so be sure to leave the inferior stopped. */ - async_enable_stdin (); - /* At least the current thread is already stopped. */ /* In all-stop, by definition, all threads have to be already @@ -2885,7 +2897,7 @@ attach_command (char *args, int from_tty) STOP_QUIETLY_NO_SIGSTOP is for. */ inferior->control.stop_soon = STOP_QUIETLY_NO_SIGSTOP; - /* sync_execution mode. Wait for stop. */ + /* Wait for stop. */ a = XNEW (struct attach_command_continuation_args); a->args = xstrdup (args); a->from_tty = from_tty; diff --git a/gdb/infrun.c b/gdb/infrun.c index 1e9c28ef4f0..25313b476a9 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -152,10 +152,6 @@ show_step_stop_if_no_debug (struct ui_file *file, int from_tty, fprintf_filtered (file, _("Mode of the step operation is %s.\n"), value); } -/* In asynchronous mode, but simulating synchronous execution. */ - -int sync_execution = 0; - /* proceed and normal_stop use this to notify the user when the inferior stopped in a different thread than it had been running in. */ @@ -442,7 +438,7 @@ follow_fork_inferior (int follow_child, int detach_fork) if (has_vforked && !non_stop /* Non-stop always resumes both branches. */ - && (!target_is_async_p () || sync_execution) + && current_ui->prompt_state == PROMPT_BLOCKED && !(follow_child || detach_fork || sched_multi)) { /* The parent stays blocked inside the vfork syscall until the @@ -3802,7 +3798,9 @@ wait_for_inferior (void) static void reinstall_readline_callback_handler_cleanup (void *arg) { - if (!current_ui->async) + struct ui *ui = current_ui; + + if (!ui->async) { /* We're not going back to the top level event loop yet. Don't install the readline callback, as it'd prep the terminal, @@ -3812,7 +3810,7 @@ reinstall_readline_callback_handler_cleanup (void *arg) return; } - if (current_ui->command_editing && !sync_execution) + if (ui->command_editing && ui->prompt_state != PROMPT_BLOCKED) gdb_rl_callback_handler_reinstall (); } @@ -3845,6 +3843,36 @@ clean_up_just_stopped_threads_fsms (struct execution_control_state *ecs) } } +/* Helper for all_uis_check_sync_execution_done that works on the + current UI. */ + +static void +check_curr_ui_sync_execution_done (void) +{ + struct ui *ui = current_ui; + + if (ui->prompt_state == PROMPT_NEEDED + && ui->async + && !gdb_in_secondary_prompt_p (ui)) + { + target_terminal_ours (); + observer_notify_sync_execution_done (); + } +} + +/* See infrun.h. */ + +void +all_uis_check_sync_execution_done (void) +{ + struct switch_thru_all_uis state; + + SWITCH_THRU_ALL_UIS (state) + { + check_curr_ui_sync_execution_done (); + } +} + /* A cleanup that restores the execution direction to the value saved in *ARG. */ @@ -3872,7 +3900,6 @@ fetch_inferior_event (void *client_data) struct execution_control_state *ecs = &ecss; struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); struct cleanup *ts_old_chain; - int was_sync = sync_execution; enum exec_direction_kind save_exec_dir = execution_direction; int cmd_done = 0; ptid_t waiton_ptid = minus_one_ptid; @@ -3992,14 +4019,12 @@ fetch_inferior_event (void *client_data) /* Revert thread and frame. */ do_cleanups (old_chain); - /* If the inferior was in sync execution mode, and now isn't, - restore the prompt (a synchronous execution command has finished, - and we're ready for input). */ - if (current_ui->async && was_sync && !sync_execution) - observer_notify_sync_execution_done (); + /* If a UI was in sync execution mode, and now isn't, restore its + prompt (a synchronous execution command has finished, and we're + ready for input). */ + all_uis_check_sync_execution_done (); if (cmd_done - && !was_sync && exec_done_display_p && (ptid_equal (inferior_ptid, null_ptid) || !is_running (inferior_ptid))) @@ -4685,17 +4710,32 @@ handle_no_resumed (struct execution_control_state *ecs) struct inferior *inf; struct thread_info *thread; - if (target_can_async_p () && !sync_execution) + if (target_can_async_p ()) { - /* There were no unwaited-for children left in the target, but, - we're not synchronously waiting for events either. Just - ignore. */ + struct ui *ui; + int any_sync = 0; - if (debug_infrun) - fprintf_unfiltered (gdb_stdlog, - "infrun: TARGET_WAITKIND_NO_RESUMED " "(ignoring: bg)\n"); - prepare_to_wait (ecs); - return 1; + ALL_UIS (ui) + { + if (ui->prompt_state == PROMPT_BLOCKED) + { + any_sync = 1; + break; + } + } + if (!any_sync) + { + /* There were no unwaited-for children left in the target, but, + we're not synchronously waiting for events either. Just + ignore. */ + + if (debug_infrun) + fprintf_unfiltered (gdb_stdlog, + "infrun: TARGET_WAITKIND_NO_RESUMED " + "(ignoring: bg)\n"); + prepare_to_wait (ecs); + return 1; + } } /* Otherwise, if we were running a synchronous execution command, we @@ -8194,6 +8234,7 @@ normal_stop (void) ptid_t last_ptid; struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); ptid_t pid_ptid; + struct switch_thru_all_uis state; get_last_target_status (&last_ptid, &last); @@ -8258,19 +8299,24 @@ normal_stop (void) && last.kind != TARGET_WAITKIND_EXITED && last.kind != TARGET_WAITKIND_NO_RESUMED) { - target_terminal_ours_for_output (); - printf_filtered (_("[Switching to %s]\n"), - target_pid_to_str (inferior_ptid)); - annotate_thread_changed (); + SWITCH_THRU_ALL_UIS (state) + { + target_terminal_ours_for_output (); + printf_filtered (_("[Switching to %s]\n"), + target_pid_to_str (inferior_ptid)); + annotate_thread_changed (); + } previous_inferior_ptid = inferior_ptid; } if (last.kind == TARGET_WAITKIND_NO_RESUMED) { - gdb_assert (sync_execution || !target_can_async_p ()); - - target_terminal_ours_for_output (); - printf_filtered (_("No unwaited-for children left.\n")); + SWITCH_THRU_ALL_UIS (state) + if (current_ui->prompt_state == PROMPT_BLOCKED) + { + target_terminal_ours_for_output (); + printf_filtered (_("No unwaited-for children left.\n")); + } } /* Note: this depends on the update_thread_list call above. */ @@ -8282,8 +8328,10 @@ normal_stop (void) if (stopped_by_random_signal) disable_current_display (); - target_terminal_ours (); - async_enable_stdin (); + SWITCH_THRU_ALL_UIS (state) + { + async_enable_stdin (); + } /* Let the user/frontend see the threads as stopped. */ do_cleanups (old_chain); diff --git a/gdb/infrun.h b/gdb/infrun.h index e79bf2d83da..01eff9a1dbd 100644 --- a/gdb/infrun.h +++ b/gdb/infrun.h @@ -35,11 +35,6 @@ extern int debug_displaced; of shared library events by the dynamic linker. */ extern int stop_on_solib_events; -/* Are we simulating synchronous execution? This is used in async gdb - to implement the 'run', 'continue' etc commands, which will not - redisplay the prompt until the execution is actually over. */ -extern int sync_execution; - /* True if execution commands resume all threads of all processes by default; otherwise, resume only threads of the current inferior process. */ @@ -238,4 +233,9 @@ extern struct thread_info *step_over_queue_head; is stopped). On failure, print a message. */ extern void maybe_remove_breakpoints (void); +/* If a UI was in sync execution mode, and now isn't, restore its + prompt (a synchronous execution command has finished, and we're + ready for input). */ +extern void all_uis_check_sync_execution_done (void); + #endif /* INFRUN_H */ diff --git a/gdb/main.c b/gdb/main.c index a1ce327641b..c45bffd81e6 100644 --- a/gdb/main.c +++ b/gdb/main.c @@ -315,8 +315,9 @@ captured_command_loop (void *data) here on. */ current_ui->async = 1; - /* Give the interpreter a chance to print a prompt. */ - interp_pre_command_loop (top_level_interpreter ()); + /* Give the interpreter a chance to print a prompt, if necessary */ + if (ui->prompt_state != PROMPT_BLOCKED) + interp_pre_command_loop (top_level_interpreter ()); /* Now it's time to start the event loop. */ start_event_loop (); @@ -368,7 +369,7 @@ catch_command_errors (catch_command_errors_ftype *command, { TRY { - int was_sync = sync_execution; + int was_sync = current_ui->prompt_state == PROMPT_BLOCKED; command (arg, from_tty); @@ -395,7 +396,7 @@ catch_command_errors_const (catch_command_errors_const_ftype *command, { TRY { - int was_sync = sync_execution; + int was_sync = current_ui->prompt_state == PROMPT_BLOCKED; command (arg, from_tty); @@ -519,6 +520,8 @@ captured_main (void *data) ui->input_fd = fileno (stdin); + ui->prompt_state = PROMPT_NEEDED; + #ifdef __MINGW32__ /* Ensure stderr is unbuffered. A Cygwin pty or pipe is implemented as a Windows pipe, and Windows buffers on pipes. */ diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index 4376ffa06a8..5db61e14f1f 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -93,8 +93,11 @@ static int report_initial_inferior (struct inferior *inf, void *closure); static void display_mi_prompt (struct mi_interp *mi) { + struct ui *ui = current_ui; + fputs_unfiltered ("(gdb) \n", mi->raw_stdout); gdb_flush (mi->raw_stdout); + ui->prompt_state = PROMPTED; } /* Returns the INTERP's data cast as mi_interp if INTERP is an MI, and @@ -169,13 +172,6 @@ mi_interpreter_resume (void *data) ui->call_readline = gdb_readline_no_editing_callback; ui->input_handler = mi_execute_command_input_handler; - /* FIXME: This is a total hack for now. PB's use of the MI - implicitly relies on a bug in the async support which allows - asynchronous commands to leak through the commmand loop. The bug - involves (but is not limited to) the fact that sync_execution was - erroneously initialized to 0. Duplicate by initializing it thus - here... */ - sync_execution = 0; gdb_stdout = mi->out; /* Route error and log output through the MI. */ @@ -315,6 +311,9 @@ static void mi_execute_command_input_handler (char *cmd) { struct mi_interp *mi = as_mi_interp (top_level_interpreter ()); + struct ui *ui = current_ui; + + ui->prompt_state = PROMPT_NEEDED; mi_execute_command_wrapper (cmd); @@ -323,7 +322,7 @@ mi_execute_command_input_handler (char *cmd) to go back to the event loop and will output the prompt in the 'synchronous_command_done' observer when the target next stops. */ - if (!sync_execution) + if (ui->prompt_state == PROMPT_NEEDED) display_mi_prompt (mi); } @@ -1139,12 +1138,10 @@ mi_on_resume_1 (struct mi_interp *mi, ptid_t ptid) if (!running_result_record_printed && mi_proceeded) { running_result_record_printed = 1; - /* This is what gdb used to do historically -- printing prompt even if - it cannot actually accept any input. This will be surely removed - for MI3, and may be removed even earlier. SYNC_EXECUTION is - checked here because we only need to emit a prompt if a - synchronous command was issued when the target is async. */ - if (!target_can_async_p () || sync_execution) + /* This is what gdb used to do historically -- printing prompt + even if it cannot actually accept any input. This will be + surely removed for MI3, and may be removed even earlier. */ + if (current_ui->prompt_state == PROMPT_BLOCKED) fputs_unfiltered ("(gdb) \n", mi->raw_stdout); } gdb_flush (mi->raw_stdout); diff --git a/gdb/target.c b/gdb/target.c index 8a83fbac70c..6f69ac37a1f 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -482,10 +482,8 @@ target_terminal_inferior (void) struct ui *ui = current_ui; /* A background resume (``run&'') should leave GDB in control of the - terminal. Use target_can_async_p, not target_is_async_p, since at - this point the target is not async yet. However, if sync_execution - is not set, we know it will become async prior to resume. */ - if (target_can_async_p () && !sync_execution) + terminal. */ + if (ui->prompt_state != PROMPT_BLOCKED) return; /* Always delete the current UI's input file handler, regardless of diff --git a/gdb/top.c b/gdb/top.c index 79f4293556b..e40835b7e73 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -391,7 +391,7 @@ void wait_sync_command_done (void) { while (gdb_do_one_event () >= 0) - if (!sync_execution) + if (current_ui->prompt_state != PROMPT_BLOCKED) break; } @@ -404,7 +404,9 @@ maybe_wait_sync_command_done (int was_sync) command's list, running command hooks or similars), and we just ran a synchronous command that started the target, wait for that command to end. */ - if (!current_ui->async && !was_sync && sync_execution) + if (!current_ui->async + && !was_sync + && current_ui->prompt_state == PROMPT_BLOCKED) wait_sync_command_done (); } @@ -441,7 +443,7 @@ execute_command (char *p, int from_tty) { const char *cmd = p; char *arg; - int was_sync = sync_execution; + int was_sync = current_ui->prompt_state == PROMPT_BLOCKED; line = p; diff --git a/gdb/top.h b/gdb/top.h index 2aa44080c9b..df96baa1672 100644 --- a/gdb/top.h +++ b/gdb/top.h @@ -25,6 +25,24 @@ struct tl_interp_info; +/* Prompt state. */ + +enum prompt_state +{ + /* The command line is blocked simulating synchronous execution. + This is used to implement the foreground execution commands + ('run', 'continue', etc.). We won't display the prompt and + accept further commands until the execution is actually over. */ + PROMPT_BLOCKED, + + /* The command finished; display the prompt before returning back to + the top level. */ + PROMPT_NEEDED, + + /* We've displayed the prompt already, ready for input. */ + PROMPTED, +}; + /* All about a user interface instance. Each user interface has its own I/O files/streams, readline state, its own top level interpreter (for the main UI, this is the interpreter specified @@ -91,6 +109,9 @@ struct ui it with the event loop. */ int input_fd; + /* See enum prompt_state's description. */ + enum prompt_state prompt_state; + /* The fields below that start with "m_" are "private". They're meant to be accessed through wrapper macros that make them look like globals. */ @@ -145,6 +166,10 @@ extern void switch_thru_all_uis_next (struct switch_thru_all_uis *state); switch_thru_all_uis_cond (&STATE); \ switch_thru_all_uis_next (&STATE)) +/* Traverse over all UIs. */ +#define ALL_UIS(UI) \ + for (UI = ui_list; UI; UI = UI->next) \ + /* Cleanup that restores the current UI. */ extern void restore_ui_cleanup (void *data); -- 2.30.2