blob: 1aab4a23332ff57508311ba434393ae79fdceb4c [file] [log] [blame]
/*
* Copyright (c) 2011-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
* modification, are permitted provided that the following conditions are
* met: redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer;
* redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution;
* neither the name of the copyright holders nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* 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
*/
#ifndef __SIM_GLOBAL_EVENT_HH__
#define __SIM_GLOBAL_EVENT_HH__
#include <mutex>
#include <vector>
#include "base/barrier.hh"
#include "sim/eventq_impl.hh"
/**
* @file sim/global_event.hh
* Global events and related declarations.
*
* A global event is an event that occurs across all threads, i.e.,
* globally. It consists of a set of "local" (regular) Events, one
* per thread/event queue, a barrier object, and common state. The
* local events are scheduled for the same tick. The local event
* process() method enters the barrier to wait for other threads; once
* all threads reach that tick (and enter the associated barrier), the
* global event is triggered and its associated activity is performed.
*
* There are two basic global event patterns, GlobalEvent and
* GlobalSyncEvent. GlobalEvent is the base class for typical global
* events, while GlobalSyncEvent is optimized for global
* synchronization operations.
*/
/**
* Common base class for GlobalEvent and GlobalSyncEvent.
*/
class BaseGlobalEvent : public EventBase
{
private:
//! Mutex variable for providing exculsive right to schedule global
//! events. This is necessary so that a total order can be maintained
//! amongst the global events. Without ensuring the total order, it is
//! possible that threads execute global events in different orders,
//! which can result in a deadlock.
static std::mutex globalQMutex;
protected:
/// The base class for the local events that will synchronize
/// threads to perform the global event. This class is abstract,
/// since it derives from the abstract Event class but still does
/// not define the required process() method.
class BarrierEvent : public Event
{
protected:
BaseGlobalEvent *_globalEvent;
BarrierEvent(BaseGlobalEvent *global_event, Priority p, Flags f)
: Event(p, f), _globalEvent(global_event)
{
}
~BarrierEvent();
friend class BaseGlobalEvent;
bool globalBarrier()
{
// This method will be called from the process() method in
// the local barrier events
// (GlobalSyncEvent::BarrierEvent). The local event
// queues are always locked when servicing events (calling
// the process() method), which means that it will be
// locked when entering this method. We need to unlock it
// while waiting on the barrier to prevent deadlocks if
// another thread wants to lock the event queue.
EventQueue::ScopedRelease release(curEventQueue());
return _globalEvent->barrier.wait();
}
public:
virtual BaseGlobalEvent *globalEvent() { return _globalEvent; }
};
//! The barrier that all threads wait on before performing the
//! global event.
Barrier barrier;
//! The individual local event instances (one per thread/event queue).
std::vector<BarrierEvent *> barrierEvent;
public:
BaseGlobalEvent(Priority p, Flags f);
virtual ~BaseGlobalEvent();
virtual void process() = 0;
virtual const char *description() const = 0;
void schedule(Tick when);
bool scheduled() const
{
bool sched = false;
for (uint32_t i = 0; i < numMainEventQueues; ++i) {
sched = sched || barrierEvent[i]->scheduled();
}
return sched;
}
Tick when() const
{
assert(numMainEventQueues > 0);
return barrierEvent[0]->when();
}
void deschedule();
void reschedule(Tick when);
};
/**
* Funky intermediate class to support CRTP so that we can have a
* common constructor to create the local events, even though the
* types of the local events are defined in the derived classes.
*/
template <class Derived>
class BaseGlobalEventTemplate : public BaseGlobalEvent
{
protected:
BaseGlobalEventTemplate(Priority p, Flags f)
: BaseGlobalEvent(p, f)
{
for (int i = 0; i < numMainEventQueues; ++i)
barrierEvent[i] = new typename Derived::BarrierEvent(this, p, f);
}
};
/**
* The main global event class. Ordinary global events should derive
* from this class, and define process() to specify the action to be
* taken when the event is reached. All threads will synchronize at a
* barrier, exactly one of the threads will execute the process()
* method, then the threads will synchronize again so that none of
* them continue until process() is complete.
*/
class GlobalEvent : public BaseGlobalEventTemplate<GlobalEvent>
{
public:
typedef BaseGlobalEventTemplate<GlobalEvent> Base;
class BarrierEvent : public Base::BarrierEvent
{
public:
void process();
BarrierEvent(Base *global_event, Priority p, Flags f)
: Base::BarrierEvent(global_event, p, f)
{ }
};
GlobalEvent(Priority p, Flags f)
: Base(p, f)
{ }
GlobalEvent(Tick when, Priority p, Flags f)
: Base(p, f)
{
schedule(when);
}
virtual void process() = 0;
};
/**
* A special global event that synchronizes all threads and forces
* them to process asynchronously enqueued events. Useful for
* separating quanta in a quantum-based parallel simulation.
*/
class GlobalSyncEvent : public BaseGlobalEventTemplate<GlobalSyncEvent>
{
public:
typedef BaseGlobalEventTemplate<GlobalSyncEvent> Base;
class BarrierEvent : public Base::BarrierEvent
{
public:
void process();
BarrierEvent(Base *global_event, Priority p, Flags f)
: Base::BarrierEvent(global_event, p, f)
{ }
};
GlobalSyncEvent(Priority p, Flags f)
: Base(p, f), repeat(0)
{ }
GlobalSyncEvent(Tick when, Tick _repeat, Priority p, Flags f)
: Base(p, f), repeat(_repeat)
{
schedule(when);
}
void process();
const char *description() const;
Tick repeat;
};
#endif // __SIM_GLOBAL_EVENT_HH__