X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fsim%2Feventq.hh;h=6f393fc338751ba2b2c20fe1427cb5267b2a7587;hb=7c904d9d3faff80d724c8ab3e6f1d3815545e2f5;hp=5fc73bb53d5b43d81e5c5c359d4610b620eabb92;hpb=4a5b51b516853c9fcaabc44caacdd7e8e93dc0ef;p=gem5.git diff --git a/src/sim/eventq.hh b/src/sim/eventq.hh index 5fc73bb53..6f393fc33 100644 --- a/src/sim/eventq.hh +++ b/src/sim/eventq.hh @@ -1,5 +1,7 @@ /* * Copyright (c) 2000-2005 The Regents of The University of Michigan + * Copyright (c) 2013 Advanced Micro Devices, Inc. + * Copyright (c) 2013 Mark D. Hill and David A. Wood * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,6 +26,9 @@ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Authors: Steve Reinhardt + * Nathan Binkert */ /* @file @@ -33,163 +38,282 @@ #ifndef __SIM_EVENTQ_HH__ #define __SIM_EVENTQ_HH__ -#include - #include -#include +#include +#include +#include +#include +#include #include -#include -#include "sim/host.hh" // for Tick - -#include "base/fast_alloc.hh" -#include "base/trace.hh" +#include "base/flags.hh" +#include "base/misc.hh" +#include "base/types.hh" +#include "debug/Event.hh" #include "sim/serialize.hh" -class EventQueue; // forward declaration +class EventQueue; // forward declaration +class BaseGlobalEvent; + +//! Simulation Quantum for multiple eventq simulation. +//! The quantum value is the period length after which the queues +//! synchronize themselves with each other. This means that any +//! event to scheduled on Queue A which is generated by an event on +//! Queue B should be at least simQuantum ticks away in future. +extern Tick simQuantum; + +//! Current number of allocated main event queues. +extern uint32_t numMainEventQueues; + +//! Array for main event queues. +extern std::vector mainEventQueue; + +#ifndef SWIG +//! The current event queue for the running thread. Access to this queue +//! does not require any locking from the thread. + +extern __thread EventQueue *_curEventQueue; + +#endif + +//! Current mode of execution: parallel / serial +extern bool inParallelMode; + +//! Function for returning eventq queue for the provided +//! index. The function allocates a new queue in case one +//! does not exist for the index, provided that the index +//! is with in bounds. +EventQueue *getEventQueue(uint32_t index); + +inline EventQueue *curEventQueue() { return _curEventQueue; } +inline void curEventQueue(EventQueue *q) { _curEventQueue = q; } + +/** + * Common base class for Event and GlobalEvent, so they can share flag + * and priority definitions and accessor functions. This class should + * not be used directly. + */ +class EventBase +{ + protected: + typedef unsigned short FlagsType; + typedef ::Flags Flags; + + static const FlagsType PublicRead = 0x003f; // public readable flags + static const FlagsType PublicWrite = 0x001d; // public writable flags + static const FlagsType Squashed = 0x0001; // has been squashed + static const FlagsType Scheduled = 0x0002; // has been scheduled + static const FlagsType AutoDelete = 0x0004; // delete after dispatch + static const FlagsType AutoSerialize = 0x0008; // must be serialized + static const FlagsType IsExitEvent = 0x0010; // special exit event + static const FlagsType IsMainQueue = 0x0020; // on main event queue + static const FlagsType Initialized = 0x7a40; // somewhat random bits + static const FlagsType InitMask = 0xffc0; // mask for init bits + + public: + typedef int8_t Priority; + + /// 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. + + /// Minimum priority + static const Priority Minimum_Pri = SCHAR_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). + static const Priority Debug_Enable_Pri = -101; + + /// Breakpoints should happen before anything else (except + /// enabling trace output), so we don't miss any action when + /// debugging. + static const Priority 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. + static const Priority CPU_Switch_Pri = -31; + + /// For some reason "delayed" inter-cluster writebacks are + /// scheduled before regular writebacks (which have default + /// priority). Steve? + static const Priority Delayed_Writeback_Pri = -1; + + /// Default is zero for historical reasons. + static const Priority Default_Pri = 0; + + /// DVFS update event leads to stats dump therefore given a lower priority + /// to ensure all relevant states have been updated + static const Priority DVFS_Update_Pri = 31; -////////////////////// -// -// Main Event Queue -// -// Events on this queue are processed at the *beginning* of each -// cycle, before the pipeline simulation is performed. -// -// defined in eventq.cc -// -////////////////////// -extern EventQueue mainEventQueue; + /// Serailization needs to occur before tick events also, so + /// that a serialize/unserialize is identical to an on-line + /// CPU switch. + static const Priority Serialize_Pri = 32; + /// CPU ticks must come after other associated CPU events + /// (such as writebacks). + static const Priority CPU_Tick_Pri = 50; + + /// Statistics events (dump, reset, etc.) come after + /// everything else, but before exit. + static const Priority Stat_Event_Pri = 90; + + /// Progress events come at the end. + static const Priority Progress_Event_Pri = 95; + + /// If we want to exit on this cycle, it's the very last thing + /// we do. + static const Priority Sim_Exit_Pri = 100; + + /// Maximum priority + static const Priority Maximum_Pri = SCHAR_MAX; +}; /* * 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 +class Event : public EventBase, public Serializable { 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); + + Tick _when; //!< timestamp when event should be processed + Priority _priority; //!< event priority + Flags flags; + +#ifndef NDEBUG + /// Global counter to generate unique IDs for Event instances + static Counter instanceCounter; + + /// 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; + /// queue to which this event belongs (though it may or may not be /// scheduled on this queue yet) EventQueue *queue; +#endif - Event *next; +#ifdef EVENTQ_DEBUG + Tick whenCreated; //!< time created + Tick whenScheduled; //!< time scheduled +#endif - Tick _when; //!< timestamp when event should be processed - int _priority; //!< event priority - char _flags; + void + setWhen(Tick when, EventQueue *q) + { + _when = when; +#ifndef NDEBUG + queue = q; +#endif +#ifdef EVENTQ_DEBUG + whenScheduled = curTick(); +#endif + } + + bool + initialized() const + { + return (flags & InitMask) == Initialized; + } protected: - enum Flags { - None = 0x0, - Squashed = 0x1, - Scheduled = 0x2, - AutoDelete = 0x4, - AutoSerialize = 0x8 - }; + /// Accessor for flags. + Flags + getFlags() const + { + return flags & PublicRead; + } - bool getFlags(Flags f) const { return (_flags & f) == f; } - void setFlags(Flags f) { _flags |= f; } - void clearFlags(Flags f) { _flags &= ~f; } + bool + isFlagSet(Flags _flags) const + { + assert(_flags.noneSet(~PublicRead)); + return flags.isSet(_flags); + } - protected: - EventQueue *theQueue() const { return queue; } + /// Accessor for flags. + void + setFlags(Flags _flags) + { + assert(_flags.noneSet(~PublicWrite)); + flags.set(_flags); + } -#if TRACING_ON - Tick when_created; //!< Keep track of creation time For debugging - Tick when_scheduled; //!< Keep track of creation time For debugging + void + clearFlags(Flags _flags) + { + assert(_flags.noneSet(~PublicWrite)); + flags.clear(_flags); + } - virtual void trace(const char *action); //!< trace event activity -#else - void trace(const char *) {} -#endif + void + clearFlags() + { + flags.clear(PublicWrite); + } - 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 { - /// Breakpoints should happen before anything else, so we - /// don't miss any action when debugging. - Debug_Break_Pri = -100, - - /// For some reason "delayed" inter-cluster writebacks are - /// scheduled before regular writebacks (which have default - /// priority). Steve? - Delayed_Writeback_Pri = -1, - - /// Default is zero for historical reasons. - Default_Pri = 0, - - /// 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, - - /// Serailization needs to occur before tick events also, so - /// that a serialize/unserialize is identical to an on-line - /// CPU switch. - Serialize_Pri = 32, - - /// CPU ticks must come after other associated CPU events - /// (such as writebacks). - CPU_Tick_Pri = 50, - - /// Statistics events (dump, reset, etc.) come after - /// everything else, but before exit. - Stat_Event_Pri = 90, - - /// If we want to exit on this cycle, it's the very last thing - /// we do. - Sim_Exit_Pri = 100 - }; - /* * Event constructor * @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) + Event(Priority p = Default_Pri, Flags f = 0) + : nextBin(nullptr), nextInBin(nullptr), _when(0), _priority(p), + flags(Initialized | f) { + assert(f.noneSet(~PublicWrite)); +#ifndef NDEBUG + instance = ++instanceCounter; + queue = NULL; +#endif +#ifdef EVENTQ_DEBUG + whenCreated = curTick(); + whenScheduled = 0; +#endif } - ~Event() {} - - virtual const std::string name() const { - return csprintf("Event_%x", (uintptr_t)this); - } - - /// 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(); + virtual ~Event(); + virtual const std::string name() const; /// 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 @@ -202,105 +326,257 @@ 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 flags.isSet(Scheduled); } /// Squash the current event - void squash() { setFlags(Squashed); } + void squash() { flags.set(Squashed); } /// Check whether the event is squashed - bool squashed() { return getFlags(Squashed); } + bool squashed() const { return flags.isSet(Squashed); } + + /// See if this is a SimExitEvent (without resorting to RTTI) + bool isExitEvent() const { return flags.isSet(IsExitEvent); } + + /// Check whether this event will auto-delete + bool isAutoDelete() const { return flags.isSet(AutoDelete); } /// Get the time that the event is scheduled Tick when() const { return _when; } /// Get the event priority - int priority() const { return _priority; } + Priority priority() const { return _priority; } - struct priority_compare : - public std::binary_function - { - bool operator()(const Event *l, const Event *r) const { - return l->when() >= r->when() || l->priority() >= r->priority(); - } - }; + //! If this is part of a GlobalEvent, return the pointer to the + //! Global Event. By default, there is no GlobalEvent, so return + //! NULL. (Overridden in GlobalEvent::BarrierEvent.) + virtual BaseGlobalEvent *globalEvent() { return NULL; } - virtual void serialize(std::ostream &os); - virtual void unserialize(Checkpoint *cp, const std::string §ion); +#ifndef SWIG + void serialize(CheckpointOut &cp) const M5_ATTR_OVERRIDE; + void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE; +#endif }; -template -void -DelayFunction(Tick when, T *object) +#ifndef SWIG +inline bool +operator<(const Event &l, const Event &r) { - class DelayEvent : public Event - { - private: - T *object; + return l.when() < r.when() || + (l.when() == r.when() && l.priority() < r.priority()); +} - public: - DelayEvent(Tick when, T *o) - : Event(&mainEventQueue), object(o) - { setFlags(this->AutoDestroy); schedule(when); } - void process() { (object->*F)(); } - const char *description() { return "delay"; } - }; +inline bool +operator>(const Event &l, const Event &r) +{ + return l.when() > r.when() || + (l.when() == r.when() && l.priority() > r.priority()); +} - new DelayEvent(when, object); +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()); } -template -class EventWrapper : public Event +inline bool +operator==(const Event &l, const Event &r) { - private: - T *object; + return l.when() == r.when() && l.priority() == r.priority(); +} - public: - EventWrapper(T *obj, bool del = false, EventQueue *q = &mainEventQueue, - Priority p = Default_Pri) - : Event(q, p), object(obj) - { - if (del) - setFlags(AutoDelete); - } - void process() { (object->*F)(); } -}; +inline bool +operator!=(const Event &l, const Event &r) +{ + return l.when() != r.when() || l.priority() != r.priority(); +} +#endif -/* +/** * Queue of events sorted in time order + * + * Events are scheduled (inserted into the event queue) using the + * schedule() method. This method either inserts a synchronous + * or asynchronous event. + * + * Synchronous events are scheduled using schedule() method with the + * argument 'global' set to false (default). This should only be done + * from a thread holding the event queue lock + * (EventQueue::service_mutex). The lock is always held when an event + * handler is called, it can therefore always insert events into its + * own event queue unless it voluntarily releases the lock. + * + * Events can be scheduled across thread (and event queue borders) by + * either scheduling asynchronous events or taking the target event + * queue's lock. However, the lock should never be taken + * directly since this is likely to cause deadlocks. Instead, code + * that needs to schedule events in other event queues should + * temporarily release its own queue and lock the new queue. This + * prevents deadlocks since a single thread never owns more than one + * event queue lock. This functionality is provided by the + * ScopedMigration helper class. Note that temporarily migrating + * between event queues can make the simulation non-deterministic, it + * should therefore be limited to cases where that can be tolerated + * (e.g., handling asynchronous IO or fast-forwarding in KVM). + * + * Asynchronous events can also be scheduled using the normal + * schedule() method with the 'global' parameter set to true. Unlike + * the previous queue migration strategy, this strategy is fully + * deterministic. This causes the event to be inserted in a separate + * queue of asynchronous events (async_queue), which is merged main + * event queue at the end of each simulation quantum (by calling the + * handleAsyncInsertions() method). Note that this implies that such + * events must happen at least one simulation quantum into the future, + * otherwise they risk being scheduled in the past by + * handleAsyncInsertions(). */ class EventQueue : public Serializable { - protected: - std::string objName; - private: + std::string objName; Event *head; + Tick _curTick; + + //! Mutex to protect async queue. + std::mutex async_queue_mutex; + + //! List of events added by other threads to this event queue. + std::list async_queue; + /** + * Lock protecting event handling. + * + * This lock is always taken when servicing events. It is assumed + * that the thread scheduling new events (not asynchronous events + * though) have taken this lock. This is normally done by + * serviceOne() since new events are typically scheduled as a + * response to an earlier event. + * + * This lock is intended to be used to temporarily steal an event + * queue to support inter-thread communication when some + * deterministic timing can be sacrificed for speed. For example, + * the KVM CPU can use this support to access devices running in a + * different thread. + * + * @see EventQueue::ScopedMigration. + * @see EventQueue::ScopedRelease + * @see EventQueue::lock() + * @see EventQueue::unlock() + */ + std::mutex service_mutex; + + //! Insert / remove event from the queue. Should only be called + //! by thread operating this queue. void insert(Event *event); void remove(Event *event); + //! Function for adding events to the async queue. The added events + //! are added to main event queue later. Threads, other than the + //! owning thread, should call this function instead of insert(). + void asyncInsert(Event *event); + + EventQueue(const EventQueue &); + public: +#ifndef SWIG + /** + * Temporarily migrate execution to a different event queue. + * + * An instance of this class temporarily migrates execution to a + * different event queue by releasing the current queue, locking + * the new queue, and updating curEventQueue(). This can, for + * example, be useful when performing IO across thread event + * queues when timing is not crucial (e.g., during fast + * forwarding). + */ + class ScopedMigration + { + public: + ScopedMigration(EventQueue *_new_eq) + : new_eq(*_new_eq), old_eq(*curEventQueue()) + { + old_eq.unlock(); + new_eq.lock(); + curEventQueue(&new_eq); + } + + ~ScopedMigration() + { + new_eq.unlock(); + old_eq.lock(); + curEventQueue(&old_eq); + } + + private: + EventQueue &new_eq; + EventQueue &old_eq; + }; + + /** + * Temporarily release the event queue service lock. + * + * There are cases where it is desirable to temporarily release + * the event queue lock to prevent deadlocks. For example, when + * waiting on the global barrier, we need to release the lock to + * prevent deadlocks from happening when another thread tries to + * temporarily take over the event queue waiting on the barrier. + */ + class ScopedRelease + { + public: + ScopedRelease(EventQueue *_eq) + : eq(*_eq) + { + eq.unlock(); + } + + ~ScopedRelease() + { + eq.lock(); + } + + private: + EventQueue &eq; + }; +#endif - // constructor - EventQueue(const std::string &n) - : objName(n), head(NULL) - {} + EventQueue(const std::string &n); virtual const std::string name() const { return objName; } + void name(const std::string &st) { objName = st; } + + //! Schedule the given event on this queue. Safe to call from any + //! thread. + void schedule(Event *event, Tick when, bool global = false); - // schedule the given event on this queue - void schedule(Event *ev); - void deschedule(Event *ev); - void reschedule(Event *ev); + //! Deschedule the specified event. Should be called only from the + //! owning thread. + void deschedule(Event *event); - Tick nextTick() { return head->when(); } - void serviceOne(); + //! Reschedule the specified event. Should be called only from + //! the owning thread. + void reschedule(Event *event, Tick when, bool always = false); + + Tick nextTick() const { return head->when(); } + void setCurTick(Tick newVal) { _curTick = newVal; } + Tick getCurTick() { return _curTick; } + Event *getHead() const { return head; } + + 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; @@ -312,96 +588,197 @@ class EventQueue : public Serializable //assert(head->when() >= when && "event scheduled in the past"); serviceOne(); } - } - // default: process all events up to 'now' (curTick) - void serviceEvents() { serviceEvents(curTick); } + setCurTick(when); + } // return true if no events are queued - bool empty() { return head == NULL; } - - void dump(); + bool empty() const { return head == NULL; } - Tick nextEventTime() { return empty() ? curTick : head->when(); } + void dump() const; - virtual void serialize(std::ostream &os); - virtual void unserialize(Checkpoint *cp, const std::string §ion); -}; + bool debugVerify() const; + //! Function for moving events from the async_queue to the main queue. + void handleAsyncInsertions(); -////////////////////// -// -// inline functions -// -// can't put these inside declaration due to circular dependence -// between Event and EventQueue classes. -// -////////////////////// + /** + * Function to signal that the event loop should be woken up because + * an event has been scheduled by an agent outside the gem5 event + * loop(s) whose event insertion may not have been noticed by gem5. + * This function isn't needed by the usual gem5 event loop but may + * be necessary in derived EventQueues which host gem5 onto other + * schedulers. + * + * @param when Time of a delayed wakeup (if known). This parameter + * can be used by an implementation to schedule a wakeup in the + * future if it is sure it will remain active until then. + * Or it can be ignored and the event queue can be woken up now. + */ + virtual void wakeup(Tick when = (Tick)-1) { } + + /** + * function for replacing the head of the event queue, so that a + * different set of events can run without disturbing events that have + * already been scheduled. Already scheduled events can be processed + * by replacing the original head back. + * USING THIS FUNCTION CAN BE DANGEROUS TO THE HEALTH OF THE SIMULATOR. + * NOT RECOMMENDED FOR USE. + */ + Event* replaceHead(Event* s); -// schedule at specified time (place on event queue specified via -// constructor) -inline void -Event::schedule(Tick t) -{ - assert(!scheduled()); - assert(t >= curTick); + /**@{*/ + /** + * Provide an interface for locking/unlocking the event queue. + * + * @warn Do NOT use these methods directly unless you really know + * what you are doing. Incorrect use can easily lead to simulator + * deadlocks. + * + * @see EventQueue::ScopedMigration. + * @see EventQueue::ScopedRelease + * @see EventQueue + */ + void lock() { service_mutex.lock(); } + void unlock() { service_mutex.unlock(); } + /**@}*/ - setFlags(Scheduled); -#if TRACING_ON - when_scheduled = curTick; +#ifndef SWIG + void serialize(CheckpointOut &cp) const M5_ATTR_OVERRIDE; + void unserialize(CheckpointIn &cp) M5_ATTR_OVERRIDE; #endif - _when = t; - queue->schedule(this); -} -inline void -Event::deschedule() -{ - assert(scheduled()); + /** + * Reschedule an event after a checkpoint. + * + * Since events don't know which event queue they belong to, + * parent objects need to reschedule events themselves. This + * method conditionally schedules an event that has the Scheduled + * flag set. It should be called by parent objects after + * unserializing an object. + * + * @warn Only use this method after unserializing an Event. + */ + void checkpointReschedule(Event *event); - clearFlags(Squashed); - clearFlags(Scheduled); - queue->deschedule(this); -} + virtual ~EventQueue() { } +}; -inline void -Event::reschedule(Tick t) +void dumpMainQueue(); + +#ifndef SWIG +class EventManager { - assert(scheduled()); - clearFlags(Squashed); + protected: + /** A pointer to this object's event queue */ + EventQueue *eventq; -#if TRACING_ON - when_scheduled = curTick; -#endif - _when = t; - queue->reschedule(this); -} + public: + EventManager(EventManager &em) : eventq(em.eventq) {} + EventManager(EventManager *em) : eventq(em->eventq) {} + EventManager(EventQueue *eq) : eventq(eq) {} -inline void -EventQueue::schedule(Event *event) -{ - insert(event); - if (DTRACE(Event)) - event->trace("scheduled"); -} + EventQueue * + eventQueue() const + { + return eventq; + } + + void + schedule(Event &event, Tick when) + { + eventq->schedule(&event, when); + } + + void + deschedule(Event &event) + { + eventq->deschedule(&event); + } + + void + reschedule(Event &event, Tick when, bool always = false) + { + eventq->reschedule(&event, when, always); + } + + void + schedule(Event *event, Tick when) + { + eventq->schedule(event, when); + } + + void + deschedule(Event *event) + { + eventq->deschedule(event); + } -inline void -EventQueue::deschedule(Event *event) + void + reschedule(Event *event, Tick when, bool always = false) + { + eventq->reschedule(event, when, always); + } + + void wakeupEventQueue(Tick when = (Tick)-1) + { + eventq->wakeup(when); + } + + void setCurTick(Tick newVal) { eventq->setCurTick(newVal); } +}; + +template +void +DelayFunction(EventQueue *eventq, Tick when, T *object) { - remove(event); - if (DTRACE(Event)) - event->trace("descheduled"); + class DelayEvent : public Event + { + private: + T *object; + + public: + DelayEvent(T *o) + : Event(Default_Pri, AutoDelete), object(o) + { } + void process() { (object->*F)(); } + const char *description() const { return "delay"; } + }; + + eventq->schedule(new DelayEvent(object), when); } -inline void -EventQueue::reschedule(Event *event) +template +class EventWrapper : public Event { - remove(event); - insert(event); - if (DTRACE(Event)) - event->trace("rescheduled"); -} + private: + T *object; + + public: + EventWrapper(T *obj, bool del = false, Priority p = Default_Pri) + : Event(p), object(obj) + { + if (del) + setFlags(AutoDelete); + } + EventWrapper(T &obj, bool del = false, Priority p = Default_Pri) + : Event(p), object(&obj) + { + if (del) + setFlags(AutoDelete); + } + void process() { (object->*F)(); } + + const std::string + name() const + { + return object->name() + ".wrapped_event"; + } + + const char *description() const { return "EventWrapped"; } +}; +#endif #endif // __SIM_EVENTQ_HH__