The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
//
// (C) Copyright 2011-2012 Sergey A. Babkin.
// This file is a part of Triceps.
// See the file COPYRIGHT for the copyright notice and license information
//
//
// CEP code label.

#include <sched/Rowop.h>
#include <sched/Label.h>
#include <sched/Unit.h>
#include <common/Exception.h>
#include <common/BusyMark.h>

namespace TRICEPS_NS {

////////////////////////////////////// Label /////////////////////////////////

Label::Label(Unit *unit, const_Onceref<RowType> rtype, const string &name) :
	type_(rtype),
	unit_(unit),
	name_(name),
	recursion_(0),
	cleared_(false),
	nonReentrant_(false)
{
	assert(unit);
	assert(!type_.isNull());
	unit->rememberLabel(this);
}

Label::~Label()
{ }

// not inside the function, or it will be initialized in screwed-up order
static string placeholderUnitName = "[label cleared]";
const string &Label::getUnitName() const
{
	return cleared_? placeholderUnitName : unit_->getName();
}

Erref Label::chain(Onceref<Label> lab)
{
	assert(this != NULL);
	assert(!lab.isNull());
	if (!type_->equals(lab->type_)) {
		Erref err = new Errors;
		err->appendMsg(true, "can not chain labels with non-equal row types");
		err->appendMsg(true, "  " + getName() + ":");
		err->appendMsg(true, "    " + type_->print("    "));
		err->appendMsg(true, "  " + lab->getName() + ":");
		err->appendMsg(true, "    " + lab->type_->print("    "));
		return err;
	}
	ChainedVec path;
	if (lab.get() == this || lab->findChained(this, path)) {
		Erref err = new Errors;
		err->appendMsg(true, "labels must not be chained in a loop");
		string dep = "  " + getName() + "->" + lab->getName();
		while (!path.empty()) {
			dep += "->";
			dep += path.back()->getName();
			path.pop_back();
		}
		err->appendMsg(true, dep);
		return err;
	}

	chained_.push_back(lab);
	return NULL;
}

void Label::clearChained()
{
	chained_.clear();
}

void Label::clear()
{
	if (!cleared_) {
		cleared_ = true;
		Erref err;
		try {
			clearSubclass();
		} catch (Exception e) {
			err = e.getErrors();
		}
		clearChained();
		if (!err.isNull())
			throw Exception(err, false);
	}
}

void Label::clearSubclass()
{ }

void Label::call(Unit *unit, Rowop *arg, const Label *chainedFrom) const
{
	if (cleared_) // don't try to execute a cleared label
		return;

	if (unit != unit_) {
		throw Exception(strprintf("Triceps API violation: call() attempt with unit '%s' of label '%s' belonging to unit '%s'.\n", 
			unit->getName().c_str(), getName().c_str(), unit_->getName().c_str()), true);
	}

	if (nonReentrant_ && recursion_ >= 1)
		throw Exception::fTrace("Detected a recursive call of the non-reentrant label '%s'.", getName().c_str());

	{
		int rec = unit->maxRecursionDepth();
		if (rec > 0 && recursion_ >= rec)
			throw Exception::fTrace("Exceeded the unit recursion depth limit %d (attempted %d) on the label '%s'.",
				rec, recursion_ + 1,
				getName().c_str());
	}

	BusyCounter bm(recursion_);

	// XXX this code would be cleaner without exceptions...
	try {
		unit->trace(this, chainedFrom, arg, Unit::TW_BEFORE);
	} catch (Exception e) {
		throw Exception::f(e, "Error when tracing before the label '%s':", getName().c_str());
	}
	try {
		execute(arg);
	} catch (Exception e) {
		Erref err = e.getErrors();
		err->appendMsg(true, strprintf("Called through the label '%s'.", getName().c_str()));
		throw; // the errors buffer got changed in place!
	}
	if (!chained_.empty()) {
		try {
			unit->trace(this, chainedFrom, arg, Unit::TW_BEFORE_CHAINED);
		} catch (Exception e) {
			throw Exception::f(e, "Error when tracing before the chain of the label '%s':", getName().c_str());
		}
		for (ChainedVec::const_iterator it = chained_.begin(); it != chained_.end(); ++it) {
			try {
				(*it)->call(unit, arg, this); // each of them can do their own chaining....
			} catch (Exception e) {
				Erref err = e.getErrors();
				err->appendMsg(true, strprintf("Called chained from the label '%s'.", getName().c_str()));
				throw; // the errors buffer got changed in place!
			}
		}
		try {
			unit->trace(this, chainedFrom, arg, Unit::TW_AFTER_CHAINED);
		} catch (Exception e) {
			throw Exception::f(e, "Error when tracing after the chain of the label '%s':", getName().c_str());
		}
	}
	try {
		unit->trace(this, chainedFrom, arg, Unit::TW_AFTER);
	} catch (Exception e) {
		throw Exception::f(e, "Error when tracing after execution of the label '%s':", getName().c_str());
	}

	// The tracing for TW_BEFORE_DRAIN and TW_AFTER_DRAIN happens in Unit.cpp.
}

bool Label::findChained(const Label *target, ChainedVec &path) const
{
	for (ChainedVec::const_iterator it = chained_.begin(); it != chained_.end(); ++it) {
		if ( it->get() == target || (*it)->findChained(target, path) ) {
			path.push_back(*it);
			return true;
		}
	}
	return false;
}

//////////////////////////////// DummyLabel ///////////////////////////////////////////

void DummyLabel::execute(Rowop *arg) const
{ }


}; // TRICEPS_NS