daily update
[external/binutils.git] / sim / common / hw-events.c
index ca6d441..544576b 100644 (file)
@@ -1,25 +1,25 @@
 /* Hardware event manager.
-   Copyright (C) 1998 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2007, 2008, 2009, 2010, 2011
+   Free Software Foundation, Inc.
    Contributed by Cygnus Support.
 
 This file is part of GDB, the GNU debugger.
 
 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 2, or (at your option)
-any later version.
+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, write to the Free Software Foundation, Inc.,
-59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
-#include "sim-main.h"
+#include "hw-main.h"
 #include "hw-base.h"
 
 #include "sim-events.h"
@@ -27,7 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 /* The hw-events object is implemented using sim-events */
 
-struct hw_event {
+struct hw_event
+{
   void *data;
   struct hw *me;
   hw_event_callback *callback;
@@ -35,51 +36,29 @@ struct hw_event {
   struct hw_event_data *entry;
 };
 
-struct hw_event_data {
+struct hw_event_data
+{
   struct hw_event event;
   struct hw_event_data *next;
-  struct hw_event_data **prev;
 };
 
 void
 create_hw_event_data (struct hw *me)
 {
+  if (me->events_of_hw != NULL)
+    hw_abort (me, "stray events");
   /* NOP */
 }
 
 void
 delete_hw_event_data (struct hw *me)
 {
-  if (me->events_of_hw != NULL)
-    hw_abort (me, "stray events");
-}
-
-
-static void
-delete_hw_event (struct hw *me,
-                struct hw_event **event)
-{
-  struct hw_event_data *entry = (*event)->entry;
-  *(entry->prev) = entry->next;
-  entry->next->prev = entry->prev;
-  (*event) = NULL;
-}
-
-
-static void
-create_hw_event (struct hw *me,
-                struct hw_event **event)
-{
-  struct hw_event_data *entry = HW_ZALLOC (me, struct hw_event_data);
-  entry->next = me->events_of_hw;
-  entry->prev = &me->events_of_hw;
-  me->events_of_hw->prev = &entry->next;
-  me->events_of_hw = entry;
-  (*event) = &entry->event;
+  /* Remove the scheduled event.  */
+  while (me->events_of_hw)
+    hw_event_queue_deschedule (me, &me->events_of_hw->event);
 }
 
 
-
 /* Pass the H/W event onto the real callback */
 
 static void
@@ -87,13 +66,16 @@ bounce_hw_event (SIM_DESC sd,
                 void *data)
 {
   /* save the data */
-  struct hw_event *event = (struct hw_event*)data;
-  struct hw *me = event->me;
-  void *event_data = event->data;
-  hw_event_callback *callback = event->callback;
-  hw_free (me, data);
-  event = NULL;
-  callback (me, event_data);
+  struct hw_event_data *entry = (struct hw_event_data *) data;
+  struct hw *me = entry->event.me;
+  void *event_data = entry->event.data;
+  hw_event_callback *callback = entry->event.callback;
+  struct hw_event_data **prev = &me->events_of_hw;
+  while ((*prev) != entry)
+    prev = &(*prev)->next;
+  (*prev) = entry->next;
+  hw_free (me, entry);
+  callback (me, event_data); /* may not return */
 }
 
 
@@ -107,27 +89,76 @@ hw_event_queue_schedule (struct hw *me,
                         void *data)
 {
   struct hw_event *event;
-  create_hw_event (me, &event);
-  /* fill it in */
-  event->data = data;
-  event->callback = callback;
-  event->me = me;
-  event->real = sim_events_schedule (hw_system (me),
-                                    delta_time,
-                                    bounce_hw_event,
-                                    event);
+  va_list dummy;
+  memset (&dummy, 0, sizeof dummy);
+  event = hw_event_queue_schedule_vtracef (me, delta_time, callback, data,
+                                          NULL, dummy);
   return event;
 }
 
+struct hw_event *
+hw_event_queue_schedule_tracef (struct hw *me,
+                               signed64 delta_time,
+                               hw_event_callback *callback,
+                               void *data,
+                               const char *fmt,
+                               ...)
+{
+  struct hw_event *event;
+  va_list ap;
+  va_start (ap, fmt);
+  event = hw_event_queue_schedule_vtracef (me, delta_time, callback, data, fmt, ap);
+  va_end (ap);
+  return event;
+}
+
+struct hw_event *
+hw_event_queue_schedule_vtracef (struct hw *me,
+                                signed64 delta_time,
+                                hw_event_callback *callback,
+                                void *data,
+                                const char *fmt,
+                                va_list ap)
+{
+  struct hw_event_data *entry = HW_ZALLOC (me, struct hw_event_data);
+  entry->next = me->events_of_hw;
+  me->events_of_hw = entry;
+  /* fill it in */
+  entry->event.entry = entry;
+  entry->event.data = data;
+  entry->event.callback = callback;
+  entry->event.me = me;
+  entry->event.real = sim_events_schedule_vtracef (hw_system (me),
+                                                  delta_time,
+                                                  bounce_hw_event,
+                                                  entry,
+                                                  fmt, ap);
+  return &entry->event;
+}
+
 
 void
 hw_event_queue_deschedule (struct hw *me,
                           struct hw_event *event_to_remove)
 {
-  /* remove it from the event queue */
-  sim_events_deschedule (hw_system (me),
-                        event_to_remove->real);
-  delete_hw_event (me, &event_to_remove);
+/* ZAP the event but only if it is still in the event queue.  Note
+   that event_to_remove is only de-referenced after its validity has
+   been confirmed.  */
+  struct hw_event_data **prev;
+  for (prev = &me->events_of_hw;
+       (*prev) != NULL;
+       prev = &(*prev)->next)
+    {
+      struct hw_event_data *entry = (*prev);
+      if (&entry->event == event_to_remove)
+       {
+         sim_events_deschedule (hw_system (me),
+                                entry->event.real);
+         (*prev) = entry->next;
+         hw_free (me, entry);
+         return;
+       }
+    }
 }
 
 
@@ -137,4 +168,109 @@ hw_event_queue_time (struct hw *me)
   return sim_events_time (hw_system (me));
 }
 
+/* Returns the time that remains before the event is raised. */
+signed64
+hw_event_remain_time (struct hw *me, struct hw_event *event)
+{
+  signed64 t;
+
+  t = sim_events_remain_time (hw_system (me), event->real);
+  return t;
+}
 
+/* Only worry about this compling on ANSI systems.
+   Build with `make test-hw-events' in sim/<cpu> directory*/
+
+#if defined (MAIN)
+#include "sim-main.h"
+#include <string.h>
+#include <stdio.h>
+
+static void
+test_handler (struct hw *me,
+             void *data)
+{
+  int *n = data;
+  if (*n != hw_event_queue_time (me))
+    abort ();
+  *n = -(*n);
+}
+
+int
+main (int argc,
+      char **argv)
+{
+  host_callback *cb = ZALLOC (host_callback);
+  struct sim_state *sd = sim_state_alloc (0, cb);
+  struct hw *me = ZALLOC (struct hw);
+  sim_pre_argv_init (sd, "test-hw-events");
+  sim_post_argv_init (sd);
+  me->system_of_hw = sd;
+
+  printf ("Create hw-event-data\n");
+  {
+    create_hw_alloc_data (me);
+    create_hw_event_data (me);
+    delete_hw_event_data (me);
+    delete_hw_alloc_data (me);
+  }
+
+  printf ("Create hw-events\n");
+  {
+    struct hw_event *a;
+    struct hw_event *b;
+    struct hw_event *c;
+    struct hw_event *d;
+    create_hw_alloc_data (me);
+    create_hw_event_data (me);
+    a = hw_event_queue_schedule (me, 0, NULL, NULL);
+    b = hw_event_queue_schedule (me, 1, NULL, NULL);
+    c = hw_event_queue_schedule (me, 2, NULL, NULL);
+    d = hw_event_queue_schedule (me, 1, NULL, NULL);
+    hw_event_queue_deschedule (me, c);
+    hw_event_queue_deschedule (me, b);
+    hw_event_queue_deschedule (me, a);
+    hw_event_queue_deschedule (me, d);
+    c = HW_ZALLOC (me, struct hw_event);
+    hw_event_queue_deschedule (me, b); /* OOPS! */
+    hw_free (me, c);
+    delete_hw_event_data (me);
+    delete_hw_alloc_data (me);
+  }
+
+  printf ("Schedule hw-events\n");
+  {
+    struct hw_event **e;
+    int *n;
+    int i;
+    int nr = 4;
+    e = HW_NZALLOC (me, struct hw_event *, nr);
+    n = HW_NZALLOC (me, int, nr);
+    create_hw_alloc_data (me);
+    create_hw_event_data (me);
+    for (i = 0; i < nr; i++)
+      {
+       n[i] = i;
+       e[i] = hw_event_queue_schedule (me, i, test_handler, &n[i]);
+      }
+    sim_events_preprocess (sd, 1, 1);
+    for (i = 0; i < nr; i++)
+      {
+       if (sim_events_tick (sd))
+         sim_events_process (sd);
+      }
+    for (i = 0; i < nr; i++)
+      {
+       if (n[i] != -i)
+         abort ();
+       hw_event_queue_deschedule (me, e[i]);
+      }
+    hw_free (me, n);
+    hw_free (me, e);
+    delete_hw_event_data (me);
+    delete_hw_alloc_data (me);
+  }
+
+  return 0;
+}
+#endif