From 50d01748162c1853ccb2d4659e9471f1029e9a7d Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Fri, 24 Oct 2008 19:33:39 +0000 Subject: * event-loop.h: Mention async_event_handlers. (async_event_handler): Forward declare. (async_event_handler_func): New typedef. (create_async_event_handler, delete_async_event_handler) (mark_async_event_handler): Declare. * event-loop.c (event_data): New. (event_handler_func): Take an event_data instead of an integer. (struct gdb_event): Replace the integer file descriptor by a generic event_data. (async_event_handler): New. (async_handler_ready): Delete. (async_event_handler_list): New. (create_event): New. (create_file_event): Use it. (process_event): Adjust. (gdb_do_one_event): Poll from the event sources in round-robin fashion across calls. Be sure to consult all sources before blocking. (handle_file_event): Take an event_data instead of an integer. Adjust. (gdb_wait_for_event): Add `block' argument. Handle it. (mark_async_signal_handler): Remove unneeded cast. (invoke_async_signal_handler): Rename to ... (invoke_async_signal_handlres): ... this. Return true if any was handled. (check_async_ready): Delete (create_async_event_handler): New. (mark_async_event_handler): New. (struct async_event_handler_data): New. (invoke_async_event_handler): New. (check_async_event_handlers): New. (delete_async_event_handler): New. (handle_timer_event): Adjust. --- gdb/ChangeLog | 36 ++++++ gdb/event-loop.c | 382 +++++++++++++++++++++++++++++++++++++++++-------------- gdb/event-loop.h | 54 ++++++-- 3 files changed, 367 insertions(+), 105 deletions(-) (limited to 'gdb') diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 183322d..75ea020 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,39 @@ +2008-10-24 Pedro Alves + + * event-loop.h: Mention async_event_handlers. + (async_event_handler): Forward declare. + (async_event_handler_func): New typedef. + (create_async_event_handler, delete_async_event_handler) + (mark_async_event_handler): Declare. + * event-loop.c (event_data): New. + (event_handler_func): Take an event_data instead of an integer. + (struct gdb_event): Replace the integer file descriptor by a + generic event_data. + (async_event_handler): New. + (async_handler_ready): Delete. + (async_event_handler_list): New. + (create_event): New. + (create_file_event): Use it. + (process_event): Adjust. + (gdb_do_one_event): Poll from the event sources in round-robin + fashion across calls. Be sure to consult all sources before + blocking. + (handle_file_event): Take an event_data instead of an integer. + Adjust. + (gdb_wait_for_event): Add `block' argument. Handle it. + (mark_async_signal_handler): Remove unneeded cast. + (invoke_async_signal_handler): Rename to ... + (invoke_async_signal_handlres): ... this. Return true if any was + handled. + (check_async_ready): Delete + (create_async_event_handler): New. + (mark_async_event_handler): New. + (struct async_event_handler_data): New. + (invoke_async_event_handler): New. + (check_async_event_handlers): New. + (delete_async_event_handler): New. + (handle_timer_event): Adjust. + 2008-10-24 Doug Evans * dwarf2read.c (typename_concat): Don't segv if prefix or suffix diff --git a/gdb/event-loop.c b/gdb/event-loop.c index b36fefa..e9f2959 100644 --- a/gdb/event-loop.c +++ b/gdb/event-loop.c @@ -38,25 +38,40 @@ #include "gdb_assert.h" #include "gdb_select.h" +/* Data point to pass to the event handler. */ +typedef union event_data +{ + void *ptr; + int integer; +} event_data; + typedef struct gdb_event gdb_event; -typedef void (event_handler_func) (int); +typedef void (event_handler_func) (event_data); /* Event for the GDB event system. Events are queued by calling async_queue_event and serviced later on by gdb_do_one_event. An event can be, for instance, a file descriptor becoming ready to be - read. Servicing an event simply means that the procedure PROC will + read. Servicing an event simply means that the procedure PROC will be called. We have 2 queues, one for file handlers that we listen to in the event loop, and one for the file handlers+events that are - ready. The procedure PROC associated with each event is always the - same (handle_file_event). Its duty is to invoke the handler - associated with the file descriptor whose state change generated - the event, plus doing other cleanups and such. */ + ready. The procedure PROC associated with each event is dependant + of the event source. In the case of monitored file descriptors, it + is always the same (handle_file_event). Its duty is to invoke the + handler associated with the file descriptor whose state change + generated the event, plus doing other cleanups and such. In the + case of async signal handlers, it is + invoke_async_signal_handler. */ struct gdb_event { - event_handler_func *proc; /* Procedure to call to service this event. */ - int fd; /* File descriptor that is ready. */ - struct gdb_event *next_event; /* Next in list of events or NULL. */ + /* Procedure to call to service this event. */ + event_handler_func *proc; + + /* Data to pass to the event handler. */ + event_data data; + + /* Next in list of events or NULL. */ + struct gdb_event *next_event; }; /* Information about each file descriptor we register with the event @@ -82,7 +97,7 @@ file_handler; 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 - asyn_signal_handler for each interesting signal. */ + async_signal_handler for each interesting signal. */ typedef struct async_signal_handler { int ready; /* If ready, call this handler from the main event loop, @@ -93,6 +108,29 @@ typedef struct async_signal_handler } async_signal_handler; +/* 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. */ +typedef struct async_event_handler + { + /* If ready, call this handler from the main event loop, using + invoke_event_handler. */ + int ready; + + /* Point 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; + } +async_event_handler; + /* Event queue: - the first event in the queue is the head of the queue. @@ -207,21 +245,25 @@ static struct } sighandler_list; -/* Are any of the handlers ready? Check this variable using - check_async_ready. This is used by process_event, to determine - whether or not to invoke the invoke_async_signal_handler - function. */ -static int async_handler_ready = 0; - -static void create_file_handler (int fd, int mask, handler_func * proc, gdb_client_data client_data); -static void invoke_async_signal_handler (void); -static void handle_file_event (int event_file_desc); -static int gdb_wait_for_event (void); -static int check_async_ready (void); -static void async_queue_event (gdb_event * event_ptr, queue_position position); -static gdb_event *create_file_event (int fd); -static int process_event (void); -static void handle_timer_event (int dummy); +/* 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; + +static int invoke_async_signal_handlers (void); +static void create_file_handler (int fd, int mask, handler_func *proc, + gdb_client_data client_data); +static void handle_file_event (event_data data); +static void check_async_event_handlers (void); +static void check_async_signal_handlers (void); +static int gdb_wait_for_event (int); static void poll_timers (void); @@ -260,6 +302,22 @@ async_queue_event (gdb_event * event_ptr, queue_position position) } } +/* Create a generic event, to be enqueued in the event queue for + processing. PROC is the procedure associated to the event. DATA + is passed to PROC upon PROC invocation. */ + +static gdb_event * +create_event (event_handler_func proc, event_data data) +{ + gdb_event *event; + + event = xmalloc (sizeof (*event)); + event->proc = proc; + event->data = data; + + return event; +} + /* Create a file event, to be enqueued in the event queue for processing. The procedure associated to this event is always handle_file_event, which will in turn invoke the one that was @@ -267,12 +325,10 @@ async_queue_event (gdb_event * event_ptr, queue_position position) static gdb_event * create_file_event (int fd) { - gdb_event *file_event_ptr; + event_data data; - file_event_ptr = (gdb_event *) xmalloc (sizeof (gdb_event)); - file_event_ptr->proc = handle_file_event; - file_event_ptr->fd = fd; - return (file_event_ptr); + data.integer = fd; + return create_event (handle_file_event, data); } /* Process one event. @@ -280,7 +336,7 @@ create_file_event (int fd) or an asynchronous event handler can be invoked in response to the reception of a signal. If an event was processed (either way), 1 is returned otherwise - 0 is returned. + 0 is returned. Scan the queue from head to tail, processing therefore the high priority events first, by invoking the associated event handler procedure. */ @@ -289,17 +345,14 @@ process_event (void) { gdb_event *event_ptr, *prev_ptr; event_handler_func *proc; - int fd; + event_data data; /* First let's see if there are any asynchronous event handlers that are ready. These would be the result of invoking any of the signal handlers. */ - if (check_async_ready ()) - { - invoke_async_signal_handler (); - return 1; - } + if (invoke_async_signal_handlers ()) + return 1; /* Look in the event queue to find an event that is ready to be processed. */ @@ -310,7 +363,7 @@ process_event (void) /* Call the handler for the event. */ proc = event_ptr->proc; - fd = event_ptr->fd; + data = event_ptr->data; /* Let's get rid of the event from the event queue. We need to do this now because while processing the event, the proc @@ -338,7 +391,7 @@ process_event (void) xfree (event_ptr); /* Now call the procedure associated with the event. */ - (*proc) (fd); + (*proc) (data); return 1; } @@ -355,33 +408,59 @@ process_event (void) int gdb_do_one_event (void *data) { - /* Any events already waiting in the queue? */ + static int event_source_head = 0; + const int number_of_sources = 3; + int current = 0; + + /* Any events already waiting in the queue? */ if (process_event ()) + return 1; + + /* To level the fairness across event sources, we poll them in a + round-robin fashion. */ + for (current = 0; current < number_of_sources; current++) { - return 1; + switch (event_source_head) + { + case 0: + /* Are any timers that are ready? If so, put an event on the + queue. */ + poll_timers (); + break; + case 1: + /* Are there events already waiting to be collected on the + monitored file descriptors? */ + gdb_wait_for_event (0); + break; + case 2: + /* Are there any asynchronous event handlers ready? */ + check_async_event_handlers (); + break; + } + + event_source_head++; + if (event_source_head == number_of_sources) + event_source_head = 0; } - /* Are any timers that are ready? If so, put an event on the queue. */ - poll_timers (); + /* Handle any new events collected. */ + if (process_event ()) + return 1; - /* Wait for a new event. If gdb_wait_for_event returns -1, - we should get out because this means that there are no - event sources left. This will make the event loop stop, - and the application exit. */ + /* Block waiting for a new event. If gdb_wait_for_event returns -1, + we should get out because this means that there are no event + sources left. This will make the event loop stop, and the + application exit. */ - if (gdb_wait_for_event () < 0) - { - return -1; - } + if (gdb_wait_for_event (1) < 0) + return -1; - /* Handle any new events occurred while waiting. */ + /* Handle any new events occurred while waiting. */ if (process_event ()) - { - return 1; - } + return 1; - /* If gdb_wait_for_event has returned 1, it means that one - event has been handled. We break out of the loop. */ + /* If gdb_wait_for_event has returned 1, it means that one event has + been handled. We break out of the loop. */ return 1; } @@ -659,7 +738,7 @@ delete_file_handler (int fd) through event_ptr->proc. EVENT_FILE_DESC is file descriptor of the event in the front of the event queue. */ static void -handle_file_event (int event_file_desc) +handle_file_event (event_data data) { file_handler *file_ptr; int mask; @@ -667,6 +746,7 @@ handle_file_event (int event_file_desc) int error_mask; int error_mask_returned; #endif + int event_file_desc = data.integer; /* Search the file handler list to find one that matches the fd in the event. */ @@ -735,15 +815,13 @@ handle_file_event (int event_file_desc) } } -/* Called by gdb_do_one_event to wait for new events on the - monitored file descriptors. Queue file events as they are - detected by the poll. - If there are no events, this function will block in the - call to poll. - Return -1 if there are no files descriptors to monitor, - otherwise return 0. */ +/* Called by gdb_do_one_event to wait for new events on the monitored + file descriptors. Queue file events as they are detected by the + poll. If BLOCK and if there are no events, this function will + block in the call to poll. Return -1 if there are no files + descriptors to monitor, otherwise return 0. */ static int -gdb_wait_for_event (void) +gdb_wait_for_event (int block) { file_handler *file_ptr; gdb_event *file_event_ptr; @@ -760,13 +838,18 @@ gdb_wait_for_event (void) if (use_poll) { #ifdef HAVE_POLL - num_found = - poll (gdb_notifier.poll_fds, - (unsigned long) gdb_notifier.num_fds, - gdb_notifier.timeout_valid ? gdb_notifier.poll_timeout : -1); + int timeout; + + if (block) + timeout = gdb_notifier.timeout_valid ? gdb_notifier.poll_timeout : -1; + else + timeout = 0; + + num_found = poll (gdb_notifier.poll_fds, + (unsigned long) gdb_notifier.num_fds, timeout); /* Don't print anything if we get out of poll because of a - signal. */ + signal. */ if (num_found == -1 && errno != EINTR) perror_with_name (("poll")); #else @@ -776,6 +859,18 @@ gdb_wait_for_event (void) } else { + struct timeval select_timeout; + + struct timeval *timeout_p; + if (block) + timeout_p = gdb_notifier.timeout_valid + ? &gdb_notifier.select_timeout : NULL; + else + { + memset (&select_timeout, 0, sizeof (select_timeout)); + timeout_p = &select_timeout; + } + gdb_notifier.ready_masks[0] = gdb_notifier.check_masks[0]; gdb_notifier.ready_masks[1] = gdb_notifier.check_masks[1]; gdb_notifier.ready_masks[2] = gdb_notifier.check_masks[2]; @@ -783,8 +878,7 @@ gdb_wait_for_event (void) &gdb_notifier.ready_masks[0], &gdb_notifier.ready_masks[1], &gdb_notifier.ready_masks[2], - gdb_notifier.timeout_valid - ? &gdb_notifier.select_timeout : NULL); + timeout_p); /* Clear the masks after an error from select. */ if (num_found == -1) @@ -792,7 +886,9 @@ gdb_wait_for_event (void) FD_ZERO (&gdb_notifier.ready_masks[0]); FD_ZERO (&gdb_notifier.ready_masks[1]); FD_ZERO (&gdb_notifier.ready_masks[2]); - /* Dont print anything is we got a signal, let gdb handle it. */ + + /* Dont print anything if we got a signal, let gdb handle + it. */ if (errno != EINTR) perror_with_name (("select")); } @@ -911,21 +1007,18 @@ call_async_signal_handler (struct async_signal_handler *handler) void mark_async_signal_handler (async_signal_handler * async_handler_ptr) { - ((async_signal_handler *) async_handler_ptr)->ready = 1; - async_handler_ready = 1; + async_handler_ptr->ready = 1; } -/* Call all the handlers that are ready. */ -static void -invoke_async_signal_handler (void) +/* Call all the handlers that are ready. Returns true if any was + indeed ready. */ +static int +invoke_async_signal_handlers (void) { async_signal_handler *async_handler_ptr; + int any_ready = 0; - if (async_handler_ready == 0) - return; - async_handler_ready = 0; - - /* Invoke ready handlers. */ + /* Invoke ready handlers. */ while (1) { @@ -938,11 +1031,12 @@ invoke_async_signal_handler (void) } if (async_handler_ptr == NULL) break; + any_ready = 1; async_handler_ptr->ready = 0; (*async_handler_ptr->proc) (async_handler_ptr->client_data); } - return; + return any_ready; } /* Delete an asynchronous handler (ASYNC_HANDLER_PTR). @@ -971,11 +1065,111 @@ delete_async_signal_handler (async_signal_handler ** async_handler_ptr) (*async_handler_ptr) = NULL; } -/* Is it necessary to call invoke_async_signal_handler? */ -static int -check_async_ready (void) +/* Create an asynchronous event handler, allocating memory for it. + Return a pointer to the newly created handler. PROC is the + function to call with CLIENT_DATA argument whenever the handler is + invoked. */ +async_event_handler * +create_async_event_handler (async_event_handler_func *proc, + gdb_client_data client_data) +{ + async_event_handler *h; + + h = xmalloc (sizeof (*h)); + h->ready = 0; + h->next_handler = NULL; + h->proc = proc; + h->client_data = client_data; + 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) +{ + async_handler_ptr->ready = 1; +} + +struct async_event_handler_data +{ + async_event_handler_func* proc; + gdb_client_data client_data; +}; + +static void +invoke_async_event_handler (event_data data) +{ + struct async_event_handler_data *hdata = data.ptr; + async_event_handler_func* proc = hdata->proc; + gdb_client_data client_data = hdata->client_data; + + xfree (hdata); + (*proc) (client_data); +} + +/* Check if any asynchronous event handlers are ready, and queue + events in the ready queue for any that are. */ +static void +check_async_event_handlers (void) +{ + async_event_handler *async_handler_ptr; + struct async_event_handler_data *hdata; + struct gdb_event *event_ptr; + event_data data; + + 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) + { + async_handler_ptr->ready = 0; + + hdata = xmalloc (sizeof (*hdata)); + + hdata->proc = async_handler_ptr->proc; + hdata->client_data = async_handler_ptr->client_data; + + data.ptr = hdata; + + event_ptr = create_event (invoke_async_event_handler, data); + async_queue_event (event_ptr, TAIL); + } + } +} + +/* Delete an asynchronous handler (ASYNC_HANDLER_PTR). + Free the space allocated for it. */ +void +delete_async_event_handler (async_event_handler **async_handler_ptr) { - return async_handler_ready; + 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; + 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; } /* Create a timer that will expire in MILLISECONDS from now. When the @@ -1080,11 +1274,11 @@ delete_timer (int id) } /* When a timer event is put on the event queue, it will be handled by - this function. Just call the assiciated procedure and delete the - timer event from the event queue. Repeat this for each timer that - has expired. */ + this function. Just call the associated procedure and delete the + timer event from the event queue. Repeat this for each timer that + has expired. */ static void -handle_timer_event (int dummy) +handle_timer_event (event_data dummy) { struct timeval time_now; struct gdb_timer *timer_ptr, *saved_timer; @@ -1150,7 +1344,7 @@ poll_timers (void) { event_ptr = (gdb_event *) xmalloc (sizeof (gdb_event)); event_ptr->proc = handle_timer_event; - event_ptr->fd = timer_list.first_timer->timer_id; + event_ptr->data.integer = timer_list.first_timer->timer_id; async_queue_event (event_ptr, TAIL); } diff --git a/gdb/event-loop.h b/gdb/event-loop.h index fc813ca..2a75b81 100644 --- a/gdb/event-loop.h +++ b/gdb/event-loop.h @@ -24,21 +24,33 @@ sources to listen on. External event sources can be plugged into the loop. - There are 3 main components: + There are 4 main components: - a list of file descriptors to be monitored, GDB_NOTIFIER. + - a list of asynchronous event sources to be monitored, + ASYNC_EVENT_HANDLER_LIST. - a list of events that have occurred, EVENT_QUEUE. - a list of signal handling functions, SIGHANDLER_LIST. - GDB_NOTIFIER keeps track of the event sources. Event sources for - gdb are currently the UI and the target. Gdb communicates with the - command line user interface via the readline library and usually - communicates with remote targets via a serial port. Serial ports - are represented in GDB as file descriptors and select/poll calls. - For native targets instead, the communication consists of calls to - ptrace and waits (via signals) or calls to poll/select (via file - descriptors). In the current gdb, the code handling events related - to the target resides in the wait_for_inferior function and in - various target specific files (*-tdep.c). + GDB_NOTIFIER keeps track of the file descriptor based event + sources. ASYNC_EVENT_HANDLER_LIST keeps track of asynchronous + event sources that are signalled by some component of gdb, usually + a target_ops instance. Event sources for gdb are currently the UI + and the target. Gdb communicates with the command line user + interface via the readline library and usually communicates with + remote targets via a serial port. Serial ports are represented in + GDB as file descriptors and select/poll calls. For native targets + instead, the communication varies across operating system debug + APIs, but usually consists of calls to ptrace and waits (via + signals) or calls to poll/select (via file descriptors). In the + current gdb, the code handling events related to the target resides + in wait_for_inferior for synchronous targets; or, for asynchronous + capable targets, by having the target register either a target + controlled file descriptor and/or an asynchronous event source in + the event loop, with the fetch_inferior_event function as the event + callback. In both the synchronous and asynchronous cases, usually + the target event is collected through the target_wait interface. + The target is free to install other event sources in the event loop + if it so requires. EVENT_QUEUE keeps track of the events that have happened during the last iteration of the event loop, and need to be processed. An @@ -57,8 +69,10 @@ typedef void *gdb_client_data; struct async_signal_handler; +struct async_event_handler; typedef void (handler_func) (int, gdb_client_data); typedef void (sig_handler_func) (gdb_client_data); +typedef void (async_event_handler_func) (gdb_client_data); typedef void (timer_handler_func) (gdb_client_data); /* Where to add an event onto the event queue, by queue_event. */ @@ -113,3 +127,21 @@ void mark_async_signal_handler (struct async_signal_handler *handler); void gdb_call_async_signal_handler (struct async_signal_handler *handler, int immediate_p); + +/* Create and register an asynchronous event source in the event loop, + and set PROC as its callback. CLIENT_DATA is passed as argument to + PROC upon its invocation. Returns a pointer to an opaque structure + used to mark as ready and to later delete this event source from + the event loop. */ +extern struct async_event_handler * + create_async_event_handler (async_event_handler_func *proc, + gdb_client_data client_data); + +/* Remove the event source pointed by HANDLER_PTR created by + CREATE_ASYNC_EVENT_HANDLER from the event loop, and release it. */ +extern void + delete_async_event_handler (struct async_event_handler **handler_ptr); + +/* Call the handler from HANDLER the next time through the event + loop. */ +extern void mark_async_event_handler (struct async_event_handler *handler); -- cgit v1.1