/* Event loop machinery for GDB, the GNU debugger.
- Copyright (C) 1999, 2000, 2001, 2002, 2005, 2006, 2007, 2008, 2009, 2010,
- 2011 Free Software Foundation, Inc.
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
Written by Elena Zannoni <ezannoni@cygnus.com> of Cygnus Solutions.
This file is part of GDB.
#include "defs.h"
#include "event-loop.h"
#include "event-top.h"
+#include "queue.h"
#ifdef HAVE_POLL
#if defined (HAVE_POLL_H)
#endif
#include <sys/types.h>
-#include "gdb_string.h"
-#include <errno.h>
#include <sys/time.h>
#include "exceptions.h"
-#include "gdb_assert.h"
#include "gdb_select.h"
+#include "observer.h"
/* Tell create_file_handler what events we are interested in.
This is used by the select version of the event loop. */
case of async signal handlers, it is
invoke_async_signal_handler. */
-struct gdb_event
+typedef struct gdb_event
{
/* 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;
- };
+ } *gdb_event_p;
/* Information about each file descriptor we register with the event
loop. */
}
async_event_handler;
-
-/* Event queue:
- - the first event in the queue is the head of the queue.
- It will be the next to be serviced.
- - the last event in the queue
-
- Events can be inserted at the front of the queue or at the end of
- the queue. Events will be extracted from the queue for processing
- starting from the head. Therefore, events inserted at the head of
- the queue will be processed in a last in first out fashion, while
- those inserted at the tail of the queue will be processed in a first
- in first out manner. All the fields are NULL if the queue is
- empty. */
-
-static struct
- {
- gdb_event *first_event; /* First pending event. */
- gdb_event *last_event; /* Last pending event. */
- }
-event_queue;
+DECLARE_QUEUE_P(gdb_event_p);
+DEFINE_QUEUE_P(gdb_event_p);
+static QUEUE(gdb_event_p) *event_queue = NULL;
/* Gdb_notifier is just a list of file descriptors gdb is interested in.
These are the input file descriptor, and the target file
static void poll_timers (void);
\f
-/* Insert an event object into the gdb event queue at
- the specified position.
- POSITION can be head or tail, with values TAIL, HEAD.
- EVENT_PTR points to the event to be inserted into the queue.
- The caller must allocate memory for the event. It is freed
- after the event has ben handled.
- Events in the queue will be processed head to tail, therefore,
- events inserted at the head of the queue will be processed
- as last in first out. Event appended at the tail of the queue
- will be processed first in first out. */
-static void
-async_queue_event (gdb_event * event_ptr, queue_position position)
-{
- if (position == TAIL)
- {
- /* The event will become the new last_event. */
-
- event_ptr->next_event = NULL;
- if (event_queue.first_event == NULL)
- event_queue.first_event = event_ptr;
- else
- event_queue.last_event->next_event = event_ptr;
- event_queue.last_event = event_ptr;
- }
- else if (position == HEAD)
- {
- /* The event becomes the new first_event. */
-
- event_ptr->next_event = event_queue.first_event;
- if (event_queue.first_event == NULL)
- event_queue.last_event = event_ptr;
- event_queue.first_event = event_ptr;
- }
-}
-
/* 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. */
return create_event (handle_file_event, data);
}
+
+/* Free EVENT. */
+
+static void
+gdb_event_xfree (struct gdb_event *event)
+{
+ xfree (event);
+}
+
+/* Initialize the event queue. */
+
+void
+initialize_event_loop (void)
+{
+ event_queue = QUEUE_alloc (gdb_event_p, gdb_event_xfree);
+}
+
/* Process one event.
The event can be the next one to be serviced in the event queue,
or an asynchronous event handler can be invoked in response to
static int
process_event (void)
{
- gdb_event *event_ptr, *prev_ptr;
- event_handler_func *proc;
- 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. */
/* Look in the event queue to find an event that is ready
to be processed. */
- for (event_ptr = event_queue.first_event; event_ptr != NULL;
- event_ptr = event_ptr->next_event)
+ if (!QUEUE_is_empty (gdb_event_p, event_queue))
{
- /* Call the handler for the event. */
-
- proc = event_ptr->proc;
- 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
- function could end up calling 'error' and therefore jump out
- to the caller of this function, gdb_do_one_event. In that
- case, we would have on the event queue an event wich has been
- processed, but not deleted. */
-
- if (event_queue.first_event == event_ptr)
- {
- event_queue.first_event = event_ptr->next_event;
- if (event_ptr->next_event == NULL)
- event_queue.last_event = NULL;
- }
- else
- {
- prev_ptr = event_queue.first_event;
- while (prev_ptr->next_event != event_ptr)
- prev_ptr = prev_ptr->next_event;
+ do this now because while processing the event, the proc
+ function could end up calling 'error' and therefore jump out
+ to the caller of this function, gdb_do_one_event. In that
+ case, we would have on the event queue an event wich has been
+ processed, but not deleted. */
+ gdb_event *event_ptr = QUEUE_deque (gdb_event_p, event_queue);
+ /* Call the handler for the event. */
+ event_handler_func *proc = event_ptr->proc;
+ event_data data = event_ptr->data;
- prev_ptr->next_event = event_ptr->next_event;
- if (event_ptr->next_event == NULL)
- event_queue.last_event = prev_ptr;
- }
- xfree (event_ptr);
+ gdb_event_xfree (event_ptr);
/* Now call the procedure associated with the event. */
(*proc) (data);
/* Process one high level event. If nothing is ready at this time,
wait for something to happen (via gdb_wait_for_event), then process
it. Returns >0 if something was done otherwise returns <0 (this
- can happen if there are no event sources to wait for). If an error
- occurs catch_errors() which calls this function returns zero. */
+ can happen if there are no event sources to wait for). */
int
-gdb_do_one_event (void *data)
+gdb_do_one_event (void)
{
static int event_source_head = 0;
const int number_of_sources = 3;
void
start_event_loop (void)
{
- /* Loop until there is nothing to do. This is the entry point to the
- event loop engine. gdb_do_one_event, called via catch_errors()
- will process one event for each invocation. It blocks waits for
- an event and then processes it. >0 when an event is processed, 0
- when catch_errors() caught an error and <0 when there are no
- longer any event sources registered. */
+ /* Loop until there is nothing to do. This is the entry point to
+ the event loop engine. gdb_do_one_event will process one event
+ for each invocation. It blocks waiting for an event and then
+ processes it. */
while (1)
{
- int gdb_result;
-
- gdb_result = catch_errors (gdb_do_one_event, 0, "", RETURN_MASK_ALL);
- if (gdb_result < 0)
- break;
-
- /* 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. */
+ volatile struct gdb_exception ex;
+ int result = 0;
- if (gdb_result == 0)
+ TRY_CATCH (ex, RETURN_MASK_ALL)
+ {
+ result = gdb_do_one_event ();
+ }
+ if (ex.reason < 0)
{
+ exception_print (gdb_stderr, ex);
+
/* If any exception escaped to here, we better enable
stdin. Otherwise, any command that calls async_disable_stdin,
and then throws, will leave stdin inoperable. */
async_enable_stdin ();
- /* FIXME: this should really be a call to a hook that is
- interface specific, because interfaces can display the
- prompt in their own way. */
- display_gdb_prompt (0);
+ /* 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. */
+ observer_notify_command_error ();
/* This call looks bizarre, but it is required. If the user
entered a command that caused an error,
after_char_processing_hook won't be called from
/* Maybe better to set a flag to be checked somewhere as to
whether display the prompt or not. */
}
+ if (result < 0)
+ break;
}
/* We are done with the event loop. There are no more event sources
{
(new_poll_fds + j)->fd = (gdb_notifier.poll_fds + i)->fd;
(new_poll_fds + j)->events = (gdb_notifier.poll_fds + i)->events;
- (new_poll_fds + j)->revents = (gdb_notifier.poll_fds + i)->revents;
+ (new_poll_fds + j)->revents
+ = (gdb_notifier.poll_fds + i)->revents;
j++;
}
}
int mask;
#ifdef HAVE_POLL
int error_mask;
- int error_mask_returned;
#endif
int event_file_desc = data.integer;
if (use_poll)
{
#ifdef HAVE_POLL
+ /* POLLHUP means EOF, but can be combined with POLLIN to
+ signal more data to read. */
error_mask = POLLHUP | POLLERR | POLLNVAL;
- mask = (file_ptr->ready_mask & file_ptr->mask) |
- (file_ptr->ready_mask & error_mask);
- error_mask_returned = mask & error_mask;
+ mask = file_ptr->ready_mask & (file_ptr->mask | error_mask);
- if (error_mask_returned != 0)
+ if ((mask & (POLLERR | POLLNVAL)) != 0)
{
/* Work in progress. We may need to tell somebody
what kind of error we had. */
- if (error_mask_returned & POLLHUP)
- printf_unfiltered (_("Hangup detected on fd %d\n"), file_ptr->fd);
- if (error_mask_returned & POLLERR)
- printf_unfiltered (_("Error detected on fd %d\n"), file_ptr->fd);
- if (error_mask_returned & POLLNVAL)
- printf_unfiltered (_("Invalid or non-`poll'able fd %d\n"), file_ptr->fd);
+ if (mask & POLLERR)
+ printf_unfiltered (_("Error detected on fd %d\n"),
+ file_ptr->fd);
+ if (mask & POLLNVAL)
+ printf_unfiltered (_("Invalid or non-`poll'able fd %d\n"),
+ file_ptr->fd);
file_ptr->error = 1;
}
else
{
if (file_ptr->ready_mask & GDB_EXCEPTION)
{
- printf_unfiltered (_("Exception condition detected on fd %d\n"), file_ptr->fd);
+ printf_unfiltered (_("Exception condition detected "
+ "on fd %d\n"), file_ptr->fd);
file_ptr->error = 1;
}
else
if (file_ptr->ready_mask == 0)
{
file_event_ptr = create_file_event (file_ptr->fd);
- async_queue_event (file_event_ptr, TAIL);
+ QUEUE_enque (gdb_event_p, event_queue, file_event_ptr);
}
file_ptr->ready_mask = (gdb_notifier.poll_fds + i)->revents;
}
if (file_ptr->ready_mask == 0)
{
file_event_ptr = create_file_event (file_ptr->fd);
- async_queue_event (file_event_ptr, TAIL);
+ QUEUE_enque (gdb_event_p, event_queue, file_event_ptr);
}
file_ptr->ready_mask = mask;
}
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)
+create_async_signal_handler (sig_handler_func * proc,
+ gdb_client_data client_data)
{
async_signal_handler *async_handler_ptr;
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;
data.ptr = hdata;
event_ptr = create_event (invoke_async_event_handler, data);
- async_queue_event (event_ptr, TAIL);
+ QUEUE_enque (gdb_event_p, event_queue, event_ptr);
}
}
}
if (async_event_handler_list.first_handler == *async_handler_ptr)
{
- async_event_handler_list.first_handler = (*async_handler_ptr)->next_handler;
+ 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;
}
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;
event_ptr = (gdb_event *) xmalloc (sizeof (gdb_event));
event_ptr->proc = handle_timer_event;
event_ptr->data.integer = timer_list.first_timer->timer_id;
- async_queue_event (event_ptr, TAIL);
+ QUEUE_enque (gdb_event_p, event_queue, event_ptr);
}
/* Now we need to update the timeout for select/ poll, because