/*
- * Copyright (c) 2012, 2015 ARM Limited
+ * Copyright (c) 2012, 2015, 2017 ARM Limited
* All rights reserved
*
* The license below extends only to copyright in the software and shall
* 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: Andreas Sandberg
*/
#include "sim/drain.hh"
-#include "base/misc.hh"
+#include <algorithm>
+
+#include "base/logging.hh"
#include "base/trace.hh"
#include "debug/Drain.hh"
#include "sim/sim_exit.hh"
+#include "sim/sim_object.hh"
DrainManager DrainManager::_instance;
DPRINTF(Drain, "Trying to drain %u objects.\n", drainableCount());
_state = DrainState::Draining;
- for (auto *obj : _allDrainable)
- _count += obj->drain(&_instance);
+ for (auto *obj : _allDrainable) {
+ DrainState status = obj->dmDrain();
+ if (DTRACE(Drain) && status != DrainState::Drained) {
+ SimObject *temp = dynamic_cast<SimObject*>(obj);
+ if (temp)
+ DPRINTF(Drain, "Failed to drain %s\n", temp->name());
+ }
+ _count += status == DrainState::Drained ? 0 : 1;
+ }
if (_count == 0) {
DPRINTF(Drain, "Drain done.\n");
"Resuming a system that isn't fully drained, this is untested and "
"likely to break\n");
+ panic_if(_state == DrainState::Resuming,
+ "Resuming a system that is already trying to resume. This should "
+ "never happen.\n");
+
panic_if(_count != 0,
"Resume called in the middle of a drain cycle. %u objects "
"left to drain.\n", _count);
- DPRINTF(Drain, "Resuming %u objects.\n", drainableCount());
+ // At this point in time the DrainManager and all objects will be
+ // in the the Drained state. New objects (i.e., objects created
+ // while resuming) will inherit the Resuming state from the
+ // DrainManager, which means we have to resume objects until all
+ // objects are in the Running state.
+ _state = DrainState::Resuming;
+
+ do {
+ DPRINTF(Drain, "Resuming %u objects.\n", drainableCount());
+ for (auto *obj : _allDrainable) {
+ if (obj->drainState() != DrainState::Running) {
+ assert(obj->drainState() == DrainState::Drained ||
+ obj->drainState() == DrainState::Resuming);
+ obj->dmDrainResume();
+ }
+ }
+ } while (!allInState(DrainState::Running));
+
_state = DrainState::Running;
- for (auto *obj : _allDrainable)
- obj->drainResume();
}
void
void
DrainManager::signalDrainDone()
{
+ assert(_count > 0);
if (--_count == 0) {
DPRINTF(Drain, "All %u objects drained..\n", drainableCount());
exitSimLoop("Finished drain", 0);
DrainManager::registerDrainable(Drainable *obj)
{
std::lock_guard<std::mutex> lock(globalLock);
- _allDrainable.insert(obj);
+ assert(std::find(_allDrainable.begin(), _allDrainable.end(), obj) ==
+ _allDrainable.end());
+ _allDrainable.push_back(obj);
}
void
DrainManager::unregisterDrainable(Drainable *obj)
{
std::lock_guard<std::mutex> lock(globalLock);
- _allDrainable.erase(obj);
+ auto o = std::find(_allDrainable.begin(), _allDrainable.end(), obj);
+ assert(o != _allDrainable.end());
+ _allDrainable.erase(o);
+}
+
+bool
+DrainManager::allInState(DrainState state) const
+{
+ for (const auto *obj : _allDrainable) {
+ if (obj->drainState() != state)
+ return false;
+ }
+
+ return true;
}
size_t
Drainable::Drainable()
: _drainManager(DrainManager::instance()),
- _drainState(DrainState::Running)
+ _drainState(_drainManager.state())
{
_drainManager.registerDrainable(this);
}
_drainManager.unregisterDrainable(this);
}
+DrainState
+Drainable::dmDrain()
+{
+ _drainState = DrainState::Draining;
+ _drainState = drain();
+ assert(_drainState == DrainState::Draining ||
+ _drainState == DrainState::Drained);
+
+ return _drainState;
+}
+
void
-Drainable::drainResume()
+Drainable::dmDrainResume()
{
+ panic_if(_drainState != DrainState::Drained &&
+ _drainState != DrainState::Resuming,
+ "Trying to resume an object that hasn't been drained\n");
+
_drainState = DrainState::Running;
+ drainResume();
}