/* Async events for the GDB event loop. Copyright (C) 1999-2023 Free Software Foundation, Inc. This file is part of GDB. 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 . */ #include "defs.h" #include "async-event.h" #include "ser-event.h" #include "top.h" #include "ui.h" /* PROC is a function to be invoked when the READY flag is set. This happens when there has been a signal and the corresponding signal handler has 'triggered' this async_signal_handler for execution. The actual work to be done in response to a signal will be carried out by PROC at a later time, within process_event. This provides a deferred execution of signal handlers. Async_init_signals takes care of setting up such an async_signal_handler for each interesting signal. */ struct async_signal_handler { /* If ready, call this handler from the main event loop, using invoke_async_handler. */ int ready; /* Pointer to next handler. */ struct async_signal_handler *next_handler; /* Function to call to do the work. */ sig_handler_func *proc; /* Argument to PROC. */ gdb_client_data client_data; /* User-friendly name of this handler. */ const char *name; }; /* PROC is a function to be invoked when the READY flag is set. This happens when the event has been marked with MARK_ASYNC_EVENT_HANDLER. The actual work to be done in response to an event will be carried out by PROC at a later time, within process_event. This provides a deferred execution of event handlers. */ struct async_event_handler { /* If ready, call this handler from the main event loop, using invoke_event_handler. */ int ready; /* Pointer to next handler. */ struct async_event_handler *next_handler; /* Function to call to do the work. */ async_event_handler_func *proc; /* Argument to PROC. */ gdb_client_data client_data; /* User-friendly name of this handler. */ const char *name; }; /* All the async_signal_handlers gdb is interested in are kept onto this list. */ static struct { /* Pointer to first in handler list. */ async_signal_handler *first_handler; /* Pointer to last in handler list. */ async_signal_handler *last_handler; } sighandler_list; /* All the async_event_handlers gdb is interested in are kept onto this list. */ static struct { /* Pointer to first in handler list. */ async_event_handler *first_handler; /* Pointer to last in handler list. */ async_event_handler *last_handler; } async_event_handler_list; /* This event is signalled whenever an asynchronous handler needs to defer an action to the event loop. */ static struct serial_event *async_signal_handlers_serial_event; /* Callback registered with ASYNC_SIGNAL_HANDLERS_SERIAL_EVENT. */ static void async_signals_handler (int error, gdb_client_data client_data) { /* Do nothing. Handlers are run by invoke_async_signal_handlers from instead. */ } void initialize_async_signal_handlers (void) { async_signal_handlers_serial_event = make_serial_event (); add_file_handler (serial_event_fd (async_signal_handlers_serial_event), async_signals_handler, NULL, "async-signals"); } /* Create an asynchronous handler, allocating memory for it. Return a pointer to the newly created handler. This pointer will be used to invoke the handler by invoke_async_signal_handler. PROC is the function to call with CLIENT_DATA argument whenever the handler is invoked. */ async_signal_handler * create_async_signal_handler (sig_handler_func * proc, gdb_client_data client_data, const char *name) { async_signal_handler *async_handler_ptr; async_handler_ptr = XNEW (async_signal_handler); async_handler_ptr->ready = 0; async_handler_ptr->next_handler = NULL; async_handler_ptr->proc = proc; async_handler_ptr->client_data = client_data; async_handler_ptr->name = name; if (sighandler_list.first_handler == NULL) sighandler_list.first_handler = async_handler_ptr; else sighandler_list.last_handler->next_handler = async_handler_ptr; sighandler_list.last_handler = async_handler_ptr; return async_handler_ptr; } /* Mark the handler (ASYNC_HANDLER_PTR) as ready. This information will be used when the handlers are invoked, after we have waited for some event. The caller of this function is the interrupt handler associated with a signal. */ void mark_async_signal_handler (async_signal_handler *async_handler_ptr) { if (debug_event_loop != debug_event_loop_kind::OFF) { /* This is called by signal handlers, so we print it "by hand" using the async-signal-safe methods. */ const char head[] = ("[event-loop] mark_async_signal_handler: marking" "async signal handler `"); gdb_stdlog->write_async_safe (head, strlen (head)); gdb_stdlog->write_async_safe (async_handler_ptr->name, strlen (async_handler_ptr->name)); const char tail[] = "`\n"; gdb_stdlog->write_async_safe (tail, strlen (tail)); } async_handler_ptr->ready = 1; serial_event_set (async_signal_handlers_serial_event); } /* See event-loop.h. */ void clear_async_signal_handler (async_signal_handler *async_handler_ptr) { event_loop_debug_printf ("clearing async signal handler `%s`", async_handler_ptr->name); async_handler_ptr->ready = 0; } /* See event-loop.h. */ int async_signal_handler_is_marked (async_signal_handler *async_handler_ptr) { return async_handler_ptr->ready; } /* Call all the handlers that are ready. Returns true if any was indeed ready. */ int invoke_async_signal_handlers (void) { async_signal_handler *async_handler_ptr; int any_ready = 0; /* We're going to handle all pending signals, so no need to wake up the event loop again the next time around. Note this must be cleared _before_ calling the callbacks, to avoid races. */ serial_event_clear (async_signal_handlers_serial_event); /* Invoke all ready handlers. */ while (1) { for (async_handler_ptr = sighandler_list.first_handler; async_handler_ptr != NULL; async_handler_ptr = async_handler_ptr->next_handler) { if (async_handler_ptr->ready) break; } if (async_handler_ptr == NULL) break; any_ready = 1; async_handler_ptr->ready = 0; /* Async signal handlers have no connection to whichever was the current UI, and thus always run on the main one. */ current_ui = main_ui; event_loop_debug_printf ("invoking async signal handler `%s`", async_handler_ptr->name); (*async_handler_ptr->proc) (async_handler_ptr->client_data); } return any_ready; } /* Delete an asynchronous handler (ASYNC_HANDLER_PTR). Free the space allocated for it. */ void delete_async_signal_handler (async_signal_handler ** async_handler_ptr) { async_signal_handler *prev_ptr; if (sighandler_list.first_handler == (*async_handler_ptr)) { sighandler_list.first_handler = (*async_handler_ptr)->next_handler; if (sighandler_list.first_handler == NULL) sighandler_list.last_handler = NULL; } else { prev_ptr = sighandler_list.first_handler; while (prev_ptr && prev_ptr->next_handler != (*async_handler_ptr)) prev_ptr = prev_ptr->next_handler; gdb_assert (prev_ptr); prev_ptr->next_handler = (*async_handler_ptr)->next_handler; if (sighandler_list.last_handler == (*async_handler_ptr)) sighandler_list.last_handler = prev_ptr; } xfree ((*async_handler_ptr)); (*async_handler_ptr) = NULL; } /* See async-event.h. */ async_event_handler * create_async_event_handler (async_event_handler_func *proc, gdb_client_data client_data, const char *name) { async_event_handler *h; h = XNEW (struct async_event_handler); h->ready = 0; h->next_handler = NULL; h->proc = proc; h->client_data = client_data; h->name = name; if (async_event_handler_list.first_handler == NULL) async_event_handler_list.first_handler = h; else async_event_handler_list.last_handler->next_handler = h; async_event_handler_list.last_handler = h; return h; } /* Mark the handler (ASYNC_HANDLER_PTR) as ready. This information will be used by gdb_do_one_event. The caller will be whoever created the event source, and wants to signal that the event is ready to be handled. */ void mark_async_event_handler (async_event_handler *async_handler_ptr) { event_loop_debug_printf ("marking async event handler `%s` " "(previous state was %d)", async_handler_ptr->name, async_handler_ptr->ready); async_handler_ptr->ready = 1; } /* See event-loop.h. */ void clear_async_event_handler (async_event_handler *async_handler_ptr) { event_loop_debug_printf ("clearing async event handler `%s`", async_handler_ptr->name); async_handler_ptr->ready = 0; } /* See event-loop.h. */ bool async_event_handler_marked (async_event_handler *handler) { return handler->ready; } /* Check if asynchronous event handlers are ready, and call the handler function for one that is. */ int check_async_event_handlers () { async_event_handler *async_handler_ptr; for (async_handler_ptr = async_event_handler_list.first_handler; async_handler_ptr != NULL; async_handler_ptr = async_handler_ptr->next_handler) { if (async_handler_ptr->ready) { event_loop_debug_printf ("invoking async event handler `%s`", async_handler_ptr->name); (*async_handler_ptr->proc) (async_handler_ptr->client_data); return 1; } } return 0; } /* Delete an asynchronous handler (ASYNC_HANDLER_PTR). Free the space allocated for it. */ void delete_async_event_handler (async_event_handler **async_handler_ptr) { async_event_handler *prev_ptr; if (async_event_handler_list.first_handler == *async_handler_ptr) { async_event_handler_list.first_handler = (*async_handler_ptr)->next_handler; if (async_event_handler_list.first_handler == NULL) async_event_handler_list.last_handler = NULL; } else { prev_ptr = async_event_handler_list.first_handler; while (prev_ptr && prev_ptr->next_handler != *async_handler_ptr) prev_ptr = prev_ptr->next_handler; gdb_assert (prev_ptr); prev_ptr->next_handler = (*async_handler_ptr)->next_handler; if (async_event_handler_list.last_handler == (*async_handler_ptr)) async_event_handler_list.last_handler = prev_ptr; } xfree (*async_handler_ptr); *async_handler_ptr = NULL; }