From 24b066ba2b90ef215c6c414ae1d15f79a8addefc Mon Sep 17 00:00:00 2001 From: Doug Evans Date: Mon, 3 May 2010 20:53:21 +0000 Subject: [PATCH] * event-loop.c (struct callback_event): New struct. (callback_list): New global. (append_callback_event, delete_callback_event): New functions. (process_callback): New function. (start_event_loop): Call it. * remote-utils.c (NOT_SCHEDULED): Define. (readchar_buf, readchar_bufcnt, readchar_bufp): New static globals, moved out of readchar. (readchar): Rewrite. Call reschedule before returning. (reset_readchar): New function. (remote_close): Call it. (process_remaining, reschedule): New functions. * server.h (callback_handler_func): New typedef. (append_callback_event, delete_callback_event): Declare. --- gdb/gdbserver/ChangeLog | 17 ++++++ gdb/gdbserver/event-loop.c | 115 +++++++++++++++++++++++++++++++++++ gdb/gdbserver/remote-utils.c | 96 ++++++++++++++++++++++++----- gdb/gdbserver/server.h | 4 ++ 4 files changed, 216 insertions(+), 16 deletions(-) diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index bba3059472..eb82920b2e 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,20 @@ +2010-05-03 Doug Evans + + * event-loop.c (struct callback_event): New struct. + (callback_list): New global. + (append_callback_event, delete_callback_event): New functions. + (process_callback): New function. + (start_event_loop): Call it. + * remote-utils.c (NOT_SCHEDULED): Define. + (readchar_buf, readchar_bufcnt, readchar_bufp): New static globals, + moved out of readchar. + (readchar): Rewrite. Call reschedule before returning. + (reset_readchar): New function. + (remote_close): Call it. + (process_remaining, reschedule): New functions. + * server.h (callback_handler_func): New typedef. + (append_callback_event, delete_callback_event): Declare. + 2010-05-03 Pedro Alves * proc-service.c (ps_pglobal_lookup): Use diff --git a/gdb/gdbserver/event-loop.c b/gdb/gdbserver/event-loop.c index 6471772e16..bc5c1f75a2 100644 --- a/gdb/gdbserver/event-loop.c +++ b/gdb/gdbserver/event-loop.c @@ -141,6 +141,36 @@ static struct } gdb_notifier; +/* Callbacks are just routines that are executed before waiting for the + next event. In GDB this is struct gdb_timer. We don't need timers + so rather than copy all that complexity in gdbserver, we provide what + we need, but we do so in a way that if/when the day comes that we need + that complexity, it'll be easier to add - replace callbacks with timers + and use a delta of zero (which is all gdb currently uses timers for anyway). + + PROC will be executed before gdbserver goes to sleep to wait for the + next event. */ + +struct callback_event + { + int id; + callback_handler_func *proc; + gdb_client_data *data; + struct callback_event *next; + }; + +/* Table of registered callbacks. */ + +static struct + { + struct callback_event *first; + struct callback_event *last; + + /* Id of the last callback created. */ + int num_callbacks; + } +callback_list; + /* Insert an event object into the gdb event queue. EVENT_PTR points to the event to be inserted into the queue. The @@ -220,6 +250,81 @@ process_event (void) return 0; } +/* Append PROC to the callback list. + The result is the "id" of the callback that can be passed back to + delete_callback_event. */ + +int +append_callback_event (callback_handler_func *proc, gdb_client_data data) +{ + struct callback_event *event_ptr; + + event_ptr = xmalloc (sizeof (*event_ptr)); + event_ptr->id = callback_list.num_callbacks++; + event_ptr->proc = proc; + event_ptr->data = data; + event_ptr->next = NULL; + if (callback_list.first == NULL) + callback_list.first = event_ptr; + if (callback_list.last != NULL) + callback_list.last->next = event_ptr; + callback_list.last = event_ptr; + return event_ptr->id; +} + +/* Delete callback ID. + It is not an error callback ID doesn't exist. */ + +void +delete_callback_event (int id) +{ + struct callback_event **p; + + for (p = &callback_list.first; *p != NULL; p = &(*p)->next) + { + struct callback_event *event_ptr = *p; + + if (event_ptr->id == id) + { + *p = event_ptr->next; + if (event_ptr == callback_list.last) + callback_list.last = NULL; + free (event_ptr); + break; + } + } +} + +/* Run the next callback. + The result is 1 if a callback was called and event processing + should continue, -1 if the callback wants the event loop to exit, + and 0 if there are no more callbacks. */ + +static int +process_callback (void) +{ + struct callback_event *event_ptr; + + event_ptr = callback_list.first; + if (event_ptr != NULL) + { + callback_handler_func *proc = event_ptr->proc; + gdb_client_data *data = event_ptr->data; + + /* Remove the event before calling PROC, + more events may get added by PROC. */ + callback_list.first = event_ptr->next; + if (callback_list.first == NULL) + callback_list.last = NULL; + free (event_ptr); + if ((*proc) (data)) + return -1; + return 1; + } + + return 0; +} + /* Add a file handler/descriptor to the list of descriptors we are interested in. FD is the file descriptor for the file/stream to be listened to. MASK is a combination of READABLE, WRITABLE, @@ -507,6 +612,16 @@ start_event_loop (void) if (res) continue; + /* Process any queued callbacks before we go to sleep. */ + res = process_callback (); + + /* Did the callback want the event loop to stop? */ + if (res == -1) + return; + + if (res) + continue; + /* Wait for a new event. If 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 diff --git a/gdb/gdbserver/remote-utils.c b/gdb/gdbserver/remote-utils.c index 2bbce67e84..64106592de 100644 --- a/gdb/gdbserver/remote-utils.c +++ b/gdb/gdbserver/remote-utils.c @@ -80,7 +80,19 @@ typedef int socklen_t; # define INVALID_DESCRIPTOR -1 #endif +/* Extra value for readchar_callback. */ +enum { + /* The callback is currently not scheduled. */ + NOT_SCHEDULED = -1 +}; + +/* Status of the readchar callback. + Either NOT_SCHEDULED or the callback id. */ +static int readchar_callback = NOT_SCHEDULED; + static int readchar (void); +static void reset_readchar (void); +static void reschedule (void); /* A cache entry for a successfully looked-up symbol. */ struct sym_cache @@ -341,6 +353,8 @@ remote_close (void) close (remote_desc); #endif remote_desc = INVALID_DESCRIPTOR; + + reset_readchar (); } /* Convert hex digit A to a number. */ @@ -926,33 +940,83 @@ initialize_async_io (void) unblock_async_io (); } +/* Internal buffer used by readchar. + These are global to readchar because reschedule_remote needs to be + able to tell whether the buffer is empty. */ + +static unsigned char readchar_buf[BUFSIZ]; +static int readchar_bufcnt = 0; +static unsigned char *readchar_bufp; + /* Returns next char from remote GDB. -1 if error. */ static int readchar (void) { - static unsigned char buf[BUFSIZ]; - static int bufcnt = 0; - static unsigned char *bufp; + int ch; - if (bufcnt-- > 0) - return *bufp++; + if (readchar_bufcnt == 0) + { + readchar_bufcnt = read (remote_desc, readchar_buf, sizeof (readchar_buf)); - bufcnt = read (remote_desc, buf, sizeof (buf)); + if (readchar_bufcnt <= 0) + { + if (readchar_bufcnt == 0) + fprintf (stderr, "readchar: Got EOF\n"); + else + perror ("readchar"); - if (bufcnt <= 0) - { - if (bufcnt == 0) - fprintf (stderr, "readchar: Got EOF\n"); - else - perror ("readchar"); + return -1; + } - return -1; + readchar_bufp = readchar_buf; } - bufp = buf; - bufcnt--; - return *bufp++; + readchar_bufcnt--; + ch = *readchar_bufp++; + reschedule (); + return ch; +} + +/* Reset the readchar state machine. */ + +static void +reset_readchar (void) +{ + readchar_bufcnt = 0; + if (readchar_callback != NOT_SCHEDULED) + { + delete_callback_event (readchar_callback); + readchar_callback = NOT_SCHEDULED; + } +} + +/* Process remaining data in readchar_buf. */ + +static int +process_remaining (void *context) +{ + int res; + + /* This is a one-shot event. */ + readchar_callback = NOT_SCHEDULED; + + if (readchar_bufcnt > 0) + res = handle_serial_event (0, NULL); + else + res = 0; + + return res; +} + +/* If there is still data in the buffer, queue another event to process it, + we can't sleep in select yet. */ + +static void +reschedule (void) +{ + if (readchar_bufcnt > 0 && readchar_callback == NOT_SCHEDULED) + readchar_callback = append_callback_event (process_remaining, NULL); } /* Read a packet from the remote machine, with error checking, diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index e7cf1ce40f..b580bce442 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -333,10 +333,14 @@ extern int non_stop; /* Functions from event-loop.c. */ typedef void *gdb_client_data; typedef int (handler_func) (int, gdb_client_data); +typedef int (callback_handler_func) (gdb_client_data); extern void delete_file_handler (int fd); extern void add_file_handler (int fd, handler_func *proc, gdb_client_data client_data); +extern int append_callback_event (callback_handler_func *proc, + gdb_client_data client_data); +extern void delete_callback_event (int id); extern void start_event_loop (void); -- 2.34.1