eventq: Don't use inline friend function when a static function will do.
[gem5.git] / src / sim / eventq.hh
index fa65b08afd9e5c978f8741c3f4dd8e9af8b7a806..e0194a742e6f2b1b6b428ff9e3f7dfdbfec03034 100644 (file)
 #ifndef __SIM_EVENTQ_HH__
 #define __SIM_EVENTQ_HH__
 
-#include <assert.h>
-
 #include <algorithm>
+#include <cassert>
+#include <climits>
 #include <map>
 #include <string>
 #include <vector>
 
-#include "sim/host.hh" // for Tick
-
 #include "base/fast_alloc.hh"
 #include "base/misc.hh"
 #include "base/trace.hh"
 #include "sim/serialize.hh"
+#include "sim/host.hh"
 
-class EventQueue;      // forward declaration
+class EventQueue;       // forward declaration
 
 //////////////////////
 //
@@ -64,26 +63,66 @@ class EventQueue;   // forward declaration
 //////////////////////
 extern EventQueue mainEventQueue;
 
-
 /*
  * An item on an event queue.  The action caused by a given
  * event is specified by deriving a subclass and overriding the
  * process() member function.
+ *
+ * Caution, the order of members is chosen to maximize data packing.
  */
 class Event : public Serializable, public FastAlloc
 {
     friend class EventQueue;
 
   private:
+    // The event queue is now a linked list of linked lists.  The
+    // 'nextBin' pointer is to find the bin, where a bin is defined as
+    // when+priority.  All events in the same bin will be stored in a
+    // second linked list (a stack) maintained by the 'nextInBin'
+    // pointer.  The list will be accessed in LIFO order.  The end
+    // result is that the insert/removal in 'nextBin' is
+    // linear/constant, and the lookup/removal in 'nextInBin' is
+    // constant/constant.  Hopefully this is a significant improvement
+    // over the current fully linear insertion.
+    Event *nextBin;
+    Event *nextInBin;
+
+    static Event *insertBefore(Event *event, Event *curr);
+    static Event *removeItem(Event *event, Event *last);
+
     /// queue to which this event belongs (though it may or may not be
     /// scheduled on this queue yet)
-    EventQueue *queue;
+    EventQueue *_queue;
+
+    Tick _when;         //!< timestamp when event should be processed
+    short _priority;    //!< event priority
+    short _flags;
 
-    Event *next;
+#ifndef NDEBUG
+    /// Global counter to generate unique IDs for Event instances
+    static Counter instanceCounter;
 
-    Tick _when;        //!< timestamp when event should be processed
-    int _priority;     //!< event priority
-    char _flags;
+    /// This event's unique ID.  We can also use pointer values for
+    /// this but they're not consistent across runs making debugging
+    /// more difficult.  Thus we use a global counter value when
+    /// debugging.
+    Counter instance;
+#endif
+
+#ifdef EVENTQ_DEBUG
+    Tick whenCreated;   //!< time created
+    Tick whenScheduled; //!< time scheduled
+#endif
+
+  protected:
+    void
+    setWhen(Tick when)
+    {
+        _when = when;
+#ifdef EVENTQ_DEBUG
+        whenScheduled = curTick;
+#endif
+    }
 
   protected:
     enum Flags {
@@ -100,26 +139,20 @@ class Event : public Serializable, public FastAlloc
     void clearFlags(Flags f) { _flags &= ~f; }
 
   protected:
-    EventQueue *theQueue() const { return queue; }
-
-#if TRACING_ON
-    Tick when_created; //!< Keep track of creation time For debugging
-    Tick when_scheduled;       //!< Keep track of creation time For debugging
-
-    virtual void trace(const char *action);    //!< trace event activity
-#else
-    void trace(const char *) {}
-#endif
+    EventQueue *queue() const { return _queue; }
 
-    unsigned annotated_value;
+    // This function isn't really useful if TRACING_ON is not defined
+    virtual void trace(const char *action);     //!< trace event activity
 
   public:
-
     /// Event priorities, to provide tie-breakers for events scheduled
     /// at the same cycle.  Most events are scheduled at the default
     /// priority; these values are used to control events that need to
     /// be ordered within a cycle.
     enum Priority {
+        /// Minimum priority
+        Minimum_Pri             = SHRT_MIN,
+
         /// If we enable tracing on a particular cycle, do that as the
         /// very first thing so we don't miss any of the events on
         /// that cycle (even if we enter the debugger).
@@ -128,38 +161,44 @@ class Event : public Serializable, public FastAlloc
         /// Breakpoints should happen before anything else (except
         /// enabling trace output), so we don't miss any action when
         /// debugging.
-        Debug_Break_Pri                = -100,
+        Debug_Break_Pri         = -100,
 
         /// CPU switches schedule the new CPU's tick event for the
         /// same cycle (after unscheduling the old CPU's tick event).
         /// The switch needs to come before any tick events to make
         /// sure we don't tick both CPUs in the same cycle.
-        CPU_Switch_Pri         =   -31,
+        CPU_Switch_Pri          =   -31,
 
         /// For some reason "delayed" inter-cluster writebacks are
         /// scheduled before regular writebacks (which have default
         /// priority).  Steve?
-        Delayed_Writeback_Pri  =   -1,
+        Delayed_Writeback_Pri   =   -1,
 
         /// Default is zero for historical reasons.
-        Default_Pri            =    0,
+        Default_Pri             =    0,
 
         /// Serailization needs to occur before tick events also, so
         /// that a serialize/unserialize is identical to an on-line
         /// CPU switch.
-        Serialize_Pri          =   32,
+        Serialize_Pri           =   32,
 
         /// CPU ticks must come after other associated CPU events
         /// (such as writebacks).
-        CPU_Tick_Pri           =   50,
+        CPU_Tick_Pri            =   50,
 
         /// Statistics events (dump, reset, etc.) come after
         /// everything else, but before exit.
-        Stat_Event_Pri         =   90,
+        Stat_Event_Pri          =   90,
+
+        /// Progress events come at the end.
+        Progress_Event_Pri      =   95,
 
         /// If we want to exit on this cycle, it's the very last thing
         /// we do.
-        Sim_Exit_Pri           =  100
+        Sim_Exit_Pri            =  100,
+
+        /// Maximum priority
+        Maximum_Pri             = SHRT_MAX
     };
 
     /*
@@ -167,40 +206,41 @@ class Event : public Serializable, public FastAlloc
      * @param queue that the event gets scheduled on
      */
     Event(EventQueue *q, Priority p = Default_Pri)
-        : queue(q), next(NULL), _priority(p), _flags(None),
-#if TRACING_ON
-          when_created(curTick), when_scheduled(0),
-#endif
-          annotated_value(0)
+        : nextBin(NULL), nextInBin(NULL), _queue(q), _priority(p), _flags(None)
     {
+#ifndef NDEBUG
+        instance = ++instanceCounter;
+#endif
+#ifdef EVENTQ_DEBUG
+        whenCreated = curTick;
+        whenScheduled = 0;
+#endif
     }
 
-    ~Event() {}
+    virtual
+    ~Event()
+    {
+    }
 
-    virtual const std::string name() const {
+    virtual const std::string
+    name() const
+    {
+#ifndef NDEBUG
+        return csprintf("Event_%d", instance);
+#else
         return csprintf("Event_%x", (uintptr_t)this);
+#endif
     }
 
-    /// Determine if the current event is scheduled
-    bool scheduled() const { return getFlags(Scheduled); }
-
-    /// Schedule the event with the current priority or default priority
-    void schedule(Tick t);
-
-    /// Reschedule the event with the current priority
-    void reschedule(Tick t);
-
-    /// Remove the event from the current schedule
-    void deschedule();
-
     /// Return a C string describing the event.  This string should
     /// *not* be dynamically allocated; just a const char array
     /// describing the event class.
-    virtual const char *description();
+    virtual const char *description() const;
 
     /// Dump the current event data
-    void dump();
+    void dump() const;
 
+  public:
     /*
      * This member function is invoked when the event is processed
      * (occurs).  There is no default implementation; each subclass
@@ -213,17 +253,27 @@ class Event : public Serializable, public FastAlloc
      */
     virtual void process() = 0;
 
-    void annotate(unsigned value) { annotated_value = value; };
-    unsigned annotation() { return annotated_value; }
+    /// Determine if the current event is scheduled
+    bool scheduled() const { return getFlags(Scheduled); }
+
+    /// Schedule the event with the current priority or default priority
+    void schedule(Tick t);
+
+    /// Reschedule the event with the current priority
+    // always parameter means to schedule if not already scheduled
+    void reschedule(Tick t, bool always = false);
+
+    /// Remove the event from the current schedule
+    void deschedule();
 
     /// Squash the current event
     void squash() { setFlags(Squashed); }
 
     /// Check whether the event is squashed
-    bool squashed() { return getFlags(Squashed); }
+    bool squashed() const { return getFlags(Squashed); }
 
     /// See if this is a SimExitEvent (without resorting to RTTI)
-    bool isExitEvent() { return getFlags(IsExitEvent); }
+    bool isExitEvent() const { return getFlags(IsExitEvent); }
 
     /// Get the time that the event is scheduled
     Tick when() const { return _when; }
@@ -231,10 +281,12 @@ class Event : public Serializable, public FastAlloc
     /// Get the event priority
     int priority() const { return _priority; }
 
-    struct priority_compare :
-    public std::binary_function<Event *, Event *, bool>
+    struct priority_compare
+        : public std::binary_function<Event *, Event *, bool>
     {
-        bool operator()(const Event *l, const Event *r) const {
+        bool
+        operator()(const Event *l, const Event *r) const
+        {
             return l->when() >= r->when() || l->priority() >= r->priority();
         }
     };
@@ -257,7 +309,7 @@ DelayFunction(Tick when, T *object)
             : Event(&mainEventQueue), object(o)
             { setFlags(this->AutoDestroy); schedule(when); }
         void process() { (object->*F)(); }
-        const char *description() { return "delay"; }
+        const char *description() const { return "delay"; }
     };
 
     new DelayEvent(when, object);
@@ -270,13 +322,25 @@ class EventWrapper : public Event
     T *object;
 
   public:
-    EventWrapper(T *obj, bool del = false, EventQueue *q = &mainEventQueue,
+    EventWrapper(T *obj, bool del = false,
+                 EventQueue *q = &mainEventQueue,
+                 Priority p = Default_Pri)
+        : Event(q, p), object(obj)
+    {
+        if (del)
+            setFlags(AutoDelete);
+    }
+
+    EventWrapper(T *obj, Tick t, bool del = false,
+                 EventQueue *q = &mainEventQueue,
                  Priority p = Default_Pri)
         : Event(q, p), object(obj)
     {
         if (del)
             setFlags(AutoDelete);
+        schedule(t);
     }
+
     void process() { (object->*F)(); }
 };
 
@@ -304,17 +368,19 @@ class EventQueue : public Serializable
     virtual const std::string name() const { return objName; }
 
     // schedule the given event on this queue
-    void schedule(Event *ev);
+    void schedule(Event *ev, Tick when);
     void deschedule(Event *ev);
-    void reschedule(Event *ev);
+    void reschedule(Event *ev, Tick when);
 
-    Tick nextTick() { return head->when(); }
+    Tick nextTick() const { return head->when(); }
     Event *serviceOne();
 
     // process all events up to the given timestamp.  we inline a
     // quick test to see if there are any events to process; if so,
     // call the internal out-of-line version to process them all.
-    void serviceEvents(Tick when) {
+    void
+    serviceEvents(Tick when)
+    {
         while (!empty()) {
             if (nextTick() > when)
                 break;
@@ -332,12 +398,14 @@ class EventQueue : public Serializable
     void serviceEvents() { serviceEvents(curTick); }
 
     // return true if no events are queued
-    bool empty() { return head == NULL; }
+    bool empty() const { return head == NULL; }
 
-    void dump();
+    void dump() const;
 
     Tick nextEventTime() { return empty() ? curTick : head->when(); }
 
+    bool debugVerify() const;
+
     virtual void serialize(std::ostream &os);
     virtual void unserialize(Checkpoint *cp, const std::string &section);
 };
@@ -355,47 +423,77 @@ class EventQueue : public Serializable
 // schedule at specified time (place on event queue specified via
 // constructor)
 inline void
-Event::schedule(Tick t)
+Event::schedule(Tick when)
 {
-    assert(!scheduled());
-//    if (t < curTick)
-//        warn("t is less than curTick, ensure you don't want cycles");
-
-    setFlags(Scheduled);
-#if TRACING_ON
-    when_scheduled = curTick;
-#endif
-    _when = t;
-    queue->schedule(this);
+    _queue->schedule(this, when);
 }
 
 inline void
 Event::deschedule()
 {
-    assert(scheduled());
-
-    clearFlags(Squashed);
-    clearFlags(Scheduled);
-    queue->deschedule(this);
+    _queue->deschedule(this);
 }
 
 inline void
-Event::reschedule(Tick t)
+Event::reschedule(Tick when, bool always)
 {
-    assert(scheduled());
-    clearFlags(Squashed);
+    if (scheduled()) {
+        _queue->reschedule(this, when);
+    } else {
+        assert(always);
+        _queue->schedule(this, when);
+    }
+}
 
-#if TRACING_ON
-    when_scheduled = curTick;
-#endif
-    _when = t;
-    queue->reschedule(this);
+inline bool
+operator<(const Event &l, const Event &r)
+{
+    return l.when() < r.when() ||
+        (l.when() == r.when() && l.priority() < r.priority());
+}
+
+inline bool
+operator>(const Event &l, const Event &r)
+{
+    return l.when() > r.when() ||
+        (l.when() == r.when() && l.priority() > r.priority());
+}
+
+inline bool
+operator<=(const Event &l, const Event &r)
+{
+    return l.when() < r.when() ||
+        (l.when() == r.when() && l.priority() <= r.priority());
+}
+inline bool
+operator>=(const Event &l, const Event &r)
+{
+    return l.when() > r.when() ||
+        (l.when() == r.when() && l.priority() >= r.priority());
+}
+
+inline bool
+operator==(const Event &l, const Event &r)
+{
+    return l.when() == r.when() && l.priority() == r.priority();
+}
+
+inline bool
+operator!=(const Event &l, const Event &r)
+{
+    return l.when() != r.when() || l.priority() != r.priority();
 }
 
 inline void
-EventQueue::schedule(Event *event)
+EventQueue::schedule(Event *event, Tick when)
 {
+    assert(when >= curTick);
+    assert(!event->scheduled());
+
+    event->setWhen(when);
     insert(event);
+    event->setFlags(Event::Scheduled);
+
     if (DTRACE(Event))
         event->trace("scheduled");
 }
@@ -403,20 +501,33 @@ EventQueue::schedule(Event *event)
 inline void
 EventQueue::deschedule(Event *event)
 {
+    assert(event->scheduled());
+
     remove(event);
+
+    event->clearFlags(Event::Squashed);
+    event->clearFlags(Event::Scheduled);
+
+    if (event->getFlags(Event::AutoDelete))
+        delete event;
+
     if (DTRACE(Event))
         event->trace("descheduled");
 }
 
 inline void
-EventQueue::reschedule(Event *event)
+EventQueue::reschedule(Event *event, Tick when)
 {
+    assert(when >= curTick);
+    assert(event->scheduled());
+
     remove(event);
+    event->setWhen(when);
     insert(event);
+    event->clearFlags(Event::Squashed);
+
     if (DTRACE(Event))
         event->trace("rescheduled");
 }
 
-
-
 #endif // __SIM_EVENTQ_HH__