* event-loop.c (struct callback_event): New struct.
authorDoug Evans <dje@google.com>
Mon, 3 May 2010 20:53:21 +0000 (20:53 +0000)
committerDoug Evans <dje@google.com>
Mon, 3 May 2010 20:53:21 +0000 (20:53 +0000)
(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
gdb/gdbserver/event-loop.c
gdb/gdbserver/remote-utils.c
gdb/gdbserver/server.h

index bba30594722fd6b3e187f3f9492d9ea68a095e6b..eb82920b2eb3a80f31be64793541f95f39221682 100644 (file)
@@ -1,3 +1,20 @@
+2010-05-03  Doug Evans  <dje@google.com>
+
+       * 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  <pedro@codesourcery.com>
 
        * proc-service.c (ps_pglobal_lookup): Use
index 6471772e16478d3a07ca661ff4933233481bff09..bc5c1f75a228b04559e3e0939dd93a56202d2673 100644 (file)
@@ -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
index 2bbce67e844f92af150ac89d6c331fb7c40dd536..64106592de75287323bfbf7ec745326d5db5cea2 100644 (file)
@@ -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,
index e7cf1ce40fb679c21c9dfa39964c1f48f1da862f..b580bce4424f03d2360db01f0c21ec0d7be87b73 100644 (file)
@@ -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);