Mercurial > ecos
diff host/libcdl/transact.cxx @ 76:435cced73e2f ecos-v1_3_1-release
eCos v1.3.1 merged from eCos master repository on 2000-03-27-23:22:51-BST
| author | jlarmour |
|---|---|
| date | Tue, 28 Mar 2000 14:10:45 +0000 |
| parents | |
| children | 70190fa0fe10 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/host/libcdl/transact.cxx @@ -0,0 +1,2817 @@ +//{{{ Banner + +//============================================================================ +// +// transaction.cxx +// +// Implementation of the CdlTransaction class +// +//============================================================================ +//####COPYRIGHTBEGIN#### +// +// ---------------------------------------------------------------------------- +// Copyright (C) 1999, 2000 Red Hat, Inc. +// +// This file is part of the eCos host tools. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or (at your option) +// any later version. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +// more details. +// +// You should have received a copy of the GNU General Public License along with +// this program; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// ---------------------------------------------------------------------------- +// +//####COPYRIGHTEND#### +//============================================================================ +//#####DESCRIPTIONBEGIN#### +// +// Author(s): bartv +// Contact(s): bartv +// Date: 1999/07/16 +// Version: 0.01 +// +//####DESCRIPTIONEND#### +//============================================================================ + +//}}} +//{{{ #include's + +// ---------------------------------------------------------------------------- +#include "cdlconfig.h" + +// Get the infrastructure types, assertions, tracing and similar +// facilities. +#include <cyg/infra/cyg_ass.h> +#include <cyg/infra/cyg_trac.h> + +// <cdlcore.hxx> defines everything implemented in this module. +// It implicitly supplies <string>, <vector> and <map> because +// the class definitions rely on these headers. +#include <cdlcore.hxx> + +//}}} + +//{{{ CdlTransactionCallback class + +// ---------------------------------------------------------------------------- +// The callback class is very straightforward. The hard work is done in +// the transaction class. + +CdlTransactionCallback::CdlTransactionCallback() +{ + CYG_REPORT_FUNCNAME("CdlTransactionCallback:: constructor"); + CYG_REPORT_FUNCARG1XV(this); + + // The vectors etc. will take care of themselves. + cdltransactioncallback_cookie = CdlTransactionCallback_Magic; + + CYG_POSTCONDITION_THISC(); + CYG_REPORT_RETURN(); +} + +CdlTransactionCallback::~CdlTransactionCallback() +{ + CYG_REPORT_FUNCNAME("CdlTransactionCallback:: destructor"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + cdltransactioncallback_cookie = CdlTransactionCallback_Invalid; + value_changes.clear(); + active_changes.clear(); + legal_values_changes.clear(); + value_source_changes.clear(); + new_conflicts.clear(); + new_structural_conflicts.clear(); + nodes_with_resolved_conflicts.clear(); + nodes_with_resolved_structural_conflicts.clear(); + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionCallback::set_callback_fn(void (*fn)(const CdlTransactionCallback&)) +{ + CYG_REPORT_FUNCNAME("CdlTransactionCallback::set_callback_fn"); + CYG_REPORT_FUNCARG1XV(fn); + + CdlTransactionBody::set_callback_fn(fn); + + CYG_REPORT_RETURN(); +} + +void (*CdlTransactionCallback::get_callback_fn())(const CdlTransactionCallback&) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransactionCallback::get_callback_fn", "result %p"); + + void (*result)(const CdlTransactionCallback&) = CdlTransactionBody::get_callback_fn(); + + CYG_REPORT_RETVAL(result); + return result; +} + +bool +CdlTransactionCallback::check_this(cyg_assert_class_zeal zeal) const +{ + if (CdlTransactionCallback_Magic != cdltransactioncallback_cookie) { + return false; + } + return true; +} + +//}}} +//{{{ CdlTransaction statics + +// ---------------------------------------------------------------------------- +void (*CdlTransactionBody::callback_fn)(const CdlTransactionCallback&) = 0; +CdlInferenceCallback CdlTransactionBody::inference_callback = 0; +bool CdlTransactionBody::inference_enabled = true; +int CdlTransactionBody::inference_recursion_limit = 3; +CdlValueSource CdlTransactionBody::inference_override = CdlValueSource_Inferred; +CYGDBG_DEFINE_MEMLEAK_COUNTER(CdlTransactionBody); + +//}}} +//{{{ Transaction creation and destruction + +// ---------------------------------------------------------------------------- +CdlTransaction +CdlTransactionBody::make(CdlToplevel toplevel) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::make", "result %p"); + CYG_REPORT_FUNCARG1XV(toplevel); + CYG_PRECONDITION_CLASSC(toplevel); + CYG_PRECONDITIONC(0 == toplevel->transaction); + + CdlTransaction result = new CdlTransactionBody(toplevel, 0, 0); + toplevel->transaction = result; + + CYG_REPORT_RETVAL(result); + return result; +} + +CdlTransaction +CdlTransactionBody::make(CdlConflict conflict) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::make (sub-transaction)", "result %p"); + CYG_REPORT_FUNCARG2XV(this, conflict); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_ZERO_OR_CLASSC(conflict); + + CdlTransaction result = new CdlTransactionBody(0, this, conflict); + + CYG_REPORT_RETVAL(result); + return result; +} + +CdlTransactionBody::CdlTransactionBody(CdlToplevel toplevel_arg, CdlTransaction parent_arg, CdlConflict conflict_arg) +{ + CYG_REPORT_FUNCNAME("CdlTransaction:: constructor"); + CYG_REPORT_FUNCARG4XV(this, toplevel_arg, parent_arg, conflict_arg); + CYG_PRECONDITION_ZERO_OR_CLASSC(toplevel_arg); + CYG_PRECONDITION_ZERO_OR_CLASSC(parent_arg); + CYG_PRECONDITION_ZERO_OR_CLASSC(conflict_arg); + CYG_PRECONDITIONC( ((0 == toplevel_arg) && (0 != parent_arg)) || ((0 == parent_arg) && (0 != toplevel_arg))); + + // The containers will take care of themselves, as will all_changes + toplevel = toplevel_arg; + parent = parent_arg; + conflict = conflict_arg; + dirty = false; + cdltransactionbody_cookie = CdlTransactionBody_Magic; + CYGDBG_MEMLEAK_CONSTRUCTOR(); + + CYG_POSTCONDITION_THISC(); + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- +CdlTransactionBody::~CdlTransactionBody() +{ + CYG_REPORT_FUNCNAME("CdlTransaction:: destructor"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + // The transaction must have been either committed or cancelled. + // This means that various of the STL containers should be empty + CYG_ASSERTC(0 == commit_cancel_ops.size()); + CYG_ASSERTC(0 == changes.size()); + CYG_ASSERTC(0 == deleted_conflicts.size()); + CYG_ASSERTC(0 == deleted_structural_conflicts.size()); + CYG_ASSERTC(0 == new_conflicts.size()); + CYG_ASSERTC(0 == new_structural_conflicts.size()); + CYG_ASSERTC(0 == resolved_conflicts.size()); + CYG_ASSERTC(0 == global_conflicts_with_solutions.size()); + CYG_ASSERTC(0 == activated.size()); + CYG_ASSERTC(0 == deactivated.size()); + CYG_ASSERTC(0 == legal_values_changes.size()); + CYG_ASSERTC(0 == value_changes.size()); + CYG_ASSERTC(0 == active_changes.size()); + + // If this was a toplevel transaction, the toplevel knows + // about the transaction. + if (0 != toplevel) { + CYG_ASSERTC(toplevel->transaction == this); + toplevel->transaction = 0; + } + cdltransactionbody_cookie = CdlTransactionBody_Invalid; + toplevel = 0; + parent = 0; + conflict = 0; + dirty = false; + + CYGDBG_MEMLEAK_DESTRUCTOR(); + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ check_this() + +// ---------------------------------------------------------------------------- + +bool +CdlTransactionBody::check_this(cyg_assert_class_zeal zeal) const +{ + if (CdlTransactionBody_Magic != cdltransactionbody_cookie) { + return false; + } + CYGDBG_MEMLEAK_CHECKTHIS(); + + //zeal = cyg_extreme; + switch(zeal) { + case cyg_system_test: + case cyg_extreme : + { + std::map<CdlValuable,CdlValue>::const_iterator map_i; + for (map_i = changes.begin(); map_i != changes.end(); map_i++) { + if (!map_i->first->check_this(cyg_quick) || !map_i->second.check_this(cyg_quick)) { + return false; + } + } + + std::list<CdlConflict>::const_iterator conf_i; + for (conf_i = new_conflicts.begin(); conf_i != new_conflicts.end(); conf_i++) { + if (!(*conf_i)->check_this(cyg_quick)) { + return false; + } + } + for (conf_i = new_structural_conflicts.begin(); conf_i != new_structural_conflicts.end(); conf_i++) { + if (!(*conf_i)->check_this(cyg_quick)) { + return false; + } + } + + std::vector<CdlConflict>::const_iterator conf_i2; + for (conf_i2 = deleted_conflicts.begin(); conf_i2 != deleted_conflicts.end(); conf_i2++) { + if (!(*conf_i2)->check_this(cyg_quick)) { + return false; + } + } + for (conf_i2 = resolved_conflicts.begin(); conf_i2 != resolved_conflicts.end(); conf_i2++) { + if (!(*conf_i2)->check_this(cyg_quick)) { + return false; + } + } + for (conf_i2 = deleted_structural_conflicts.begin(); conf_i2 != deleted_structural_conflicts.end(); conf_i2++) { + if (!(*conf_i2)->check_this(cyg_quick)) { + return false; + } + } + for (conf_i = global_conflicts_with_solutions.begin(); conf_i != global_conflicts_with_solutions.end(); conf_i++) { + if (!(*conf_i)->check_this(cyg_quick)) { + return false; + } + if (0 != (*conf_i)->transaction) { + return false; + } + } + + // Nodes cannot have been both activated and deactivated in one transaction + std::set<CdlNode>::const_iterator node_i; + for (node_i = activated.begin(); node_i != activated.end(); node_i++) { + if (!(*node_i)->check_this(cyg_quick)) { + return false; + } + if (deactivated.end() != deactivated.find(*node_i)) { + return false; + } + } + for (node_i = deactivated.begin(); node_i != deactivated.end(); node_i++) { + if (!(*node_i)->check_this(cyg_quick)) { + return false; + } + if (activated.end() != activated.find(*node_i)) { + return false; + } + } + std::set<CdlValuable>::const_iterator val_i; + for (val_i = legal_values_changes.begin(); val_i != legal_values_changes.end(); val_i++) { + if (!(*val_i)->check_this(cyg_quick)) { + return false; + } + } + + std::deque<CdlValuable>::const_iterator val_i2; + for (val_i2 = value_changes.begin(); val_i2 != value_changes.end(); val_i2++) { + if (!(*val_i2)->check_this(cyg_quick)) { + return false; + } + } + + std::deque<CdlNode>::const_iterator active_i; + for (active_i = active_changes.begin(); active_i != active_changes.end(); active_i++) { + if (!(*active_i)->check_this(cyg_quick)) { + return false; + } + } + } + case cyg_thorough: + if ((0 != toplevel) && !toplevel->check_this(cyg_quick)) { + return false; + } + if ((0 != parent) && !parent->check_this(cyg_quick)) { + return false; + } + if ((0 != conflict) && !conflict->check_this(cyg_quick)) { + return false; + } + + case cyg_quick: + case cyg_trivial : + if ((0 == toplevel) && (0 == parent)) { + return false; + } + if (this == parent) { + return false; + } + + case cyg_none : + break; + } + + return true; +} + +//}}} +//{{{ Misc + +// ---------------------------------------------------------------------------- +CdlToplevel +CdlTransactionBody::get_toplevel() const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_toplevel", "result %p"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CdlToplevel result = toplevel; + + CYG_REPORT_RETVAL(result); + return result; +} + +CdlTransaction +CdlTransactionBody::get_parent() const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_parent", "result %p"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CdlTransaction result = parent; + + CYG_REPORT_RETVAL(result); + return result; +} + +CdlConflict +CdlTransactionBody::get_conflict() const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_conflict", "result %p"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CdlConflict result = conflict; + + CYG_REPORT_RETVAL(result); + return result; +} + + +// ---------------------------------------------------------------------------- +void (*CdlTransactionBody::get_callback_fn())(const CdlTransactionCallback&) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_callback_fn", "result %p"); + + void (*result)(const CdlTransactionCallback&) = callback_fn; + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::set_callback_fn(void (*fn)(const CdlTransactionCallback&)) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::set_callback_fn"); + CYG_REPORT_FUNCARG1XV(fn); + + callback_fn = fn; + + CYG_REPORT_RETURN(); +} + +CdlInferenceCallback +CdlTransactionBody::get_inference_callback_fn() +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_inference_callback_fn", "result %p"); + + CdlInferenceCallback result = inference_callback; + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::set_inference_callback_fn(CdlInferenceCallback fn) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::set_inference_callback"); + CYG_REPORT_FUNCARG1XV(fn); + + inference_callback = fn; + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionBody::enable_automatic_inference() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::enable_automatic_inference"); + + inference_enabled = true; + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionBody::disable_automatic_inference() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::disable_automatic_inference"); + + inference_enabled = false; + + CYG_REPORT_RETURN(); +} + +bool +CdlTransactionBody::is_automatic_inference_enabled() +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::is_automatic_inference_enabled", "result %d"); + + bool result = inference_enabled; + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::set_inference_recursion_limit(int limit) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::set_inference_recursion_limit"); + CYG_REPORT_FUNCARG1XV(limit); + CYG_PRECONDITIONC(0 < limit); + CYG_PRECONDITIONC(limit < 16); // arbitrary number + + inference_recursion_limit = limit; + + CYG_REPORT_RETURN(); +} + +int +CdlTransactionBody::get_inference_recursion_limit() +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_inference_recursion_limit", "result %d"); + + int result = inference_recursion_limit; + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::set_inference_override(CdlValueSource source) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::set_inference_override"); + CYG_REPORT_FUNCARG1XV(source); + CYG_PRECONDITIONC((CdlValueSource_Invalid == source) || Cdl::is_valid_value_source(source)); + + inference_override = source; + + CYG_REPORT_RETURN(); +} + +CdlValueSource +CdlTransactionBody::get_inference_override() +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_inference_override", "result %d"); + + CdlValueSource result = inference_override; + + CYG_REPORT_RETVAL((int) result); + return result; +} + +//}}} +//{{{ Value access and updates + +// ---------------------------------------------------------------------------- +const CdlValue& +CdlTransactionBody::get_whole_value(CdlConstValuable valuable_arg) const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_whole_value", "result %p"); + CYG_REPORT_FUNCARG2XV(this, valuable_arg); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(valuable_arg); + + // Unfortunately I need a valuable rather than a const-valuable when + // accessing the STL containers. + CdlValuable valuable = const_cast<CdlValuable>(valuable_arg); + + // If we are trying to find a solution, keep track of all valuables + // that were accessed. + const CdlValue* result = 0; + if (0 != conflict) { + conflict->solution_references.insert(valuable); + } + + std::map<CdlValuable,CdlValue>::const_iterator val_i; + val_i = changes.find(valuable); + if (val_i != changes.end()) { + result = &(val_i->second); + } else if (0 != parent) { + result = &(parent->get_whole_value(valuable)); + } else { + result = &(valuable->get_whole_value()); + } + + CYG_REPORT_RETVAL(result); + return *result; +} + +void +CdlTransactionBody::set_whole_value(CdlValuable valuable, const CdlValue& old_value, const CdlValue& new_value) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::set_whole_value"); + CYG_REPORT_FUNCARG3XV(this, valuable, &new_value); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(valuable); + CYG_PRECONDITION_CLASSOC(old_value); + CYG_PRECONDITION_CLASSOC(new_value); + CYG_PRECONDITIONC(&old_value != &new_value); + + CdlValueFlavor flavor = old_value.get_flavor(); + CYG_ASSERTC(flavor == new_value.get_flavor()); + CYG_ASSERTC(CdlValueFlavor_None != flavor); + + bool value_changed = false; + bool bool_changed = false; + + if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) { + if (old_value.is_enabled() != new_value.is_enabled()) { + value_changed = true; + bool_changed = true; + } + } + if (!value_changed && ((CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor))) { + if (old_value.get_simple_value() != new_value.get_simple_value()) { + value_changed = true; + } + } + if (value_changed) { + std::deque<CdlValuable>::const_iterator change_i; + change_i = std::find(value_changes.begin(), value_changes.end(), valuable); + if (change_i == value_changes.end()) { + value_changes.push_back(valuable); + } + } + + // Actually do the update. This may modify old_value, so has to be + // left to the end. + changes[valuable] = new_value; + + // If there was a change to the boolean part of the value and the valuable + // implements an interface, the interface value needs to be recalculated. + if (bool_changed && valuable->has_property(CdlPropertyId_Implements)) { + std::vector<CdlInterface> interfaces; + std::vector<CdlInterface>::const_iterator interface_i; + valuable->get_implemented_interfaces(interfaces); + for (interface_i = interfaces.begin(); interface_i != interfaces.end(); interface_i++) { + (*interface_i)->recalculate(this); + } + } + + CYG_REPORT_RETURN(); +} + +const std::map<CdlValuable, CdlValue>& +CdlTransactionBody::get_changes() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_changes"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return changes; +} + +//}}} +//{{{ Active access and updates + +// ---------------------------------------------------------------------------- +// Nodes can become active or inactive during transactions, and this affects +// propagation and expression evaluation + +bool +CdlTransactionBody::is_active(CdlNode node) const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::is_active", "result %d"); + CYG_REPORT_FUNCARG2XV(this, node); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + bool result = false; + if (activated.end() != activated.find(node)) { + result = true; + } else if (deactivated.end() != deactivated.find(node)) { + result = false; + } else if (0 != parent) { + result = parent->is_active(node); + } else { + result = node->is_active(); + } + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::set_active(CdlNode node, bool state) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::set_active"); + CYG_REPORT_FUNCARG3XV(this, node, state); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + if (state) { + activated.insert(node); + std::set<CdlNode>::iterator node_i = deactivated.find(node); + if (deactivated.end() != node_i) { + deactivated.erase(node_i); + } + } else { + deactivated.insert(node); + std::set<CdlNode>::iterator node_i = activated.find(node); + if (activated.end() != node_i) { + activated.erase(node_i); + } + } + active_changes.push_back(node); + + CdlValuable valuable = dynamic_cast<CdlValuable>(node); + if ((0 != valuable) && valuable->has_property(CdlPropertyId_Implements)) { + std::vector<CdlInterface> interfaces; + std::vector<CdlInterface>::const_iterator interface_i; + valuable->get_implemented_interfaces(interfaces); + for (interface_i = interfaces.begin(); interface_i != interfaces.end(); interface_i++) { + (*interface_i)->recalculate(this); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ Conflict access and updates + +//{{{ get_conflict() etc. + +// ---------------------------------------------------------------------------- +bool +CdlTransactionBody::has_conflict(CdlNode node, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::has_conflict", "result %d"); + CYG_REPORT_FUNCARG3XV(this, node, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + // Because it is necessary to check whether or not any given + // conflict has been cleared in the current transaction or any + // parent transaction, recursion into the parent is not + // appropriate. + bool result = false; + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_conflicts.begin(); + conf_i != current_transaction->new_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = true; + break; + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (!result && (0 != current_transaction)); + + if (!result) { + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->conflicts.begin(); conf_i != toplevel->conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = true; + break; + } + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +CdlConflict +CdlTransactionBody::get_conflict(CdlNode node, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_conflict", "result %p"); + CYG_REPORT_FUNCARG3XV(this, node, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + CdlConflict result = 0; + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_conflicts.begin(); + conf_i != current_transaction->new_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = *conf_i; + break; + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while ((0 == result) && (0 != current_transaction)); + + if (0 == result) { + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->conflicts.begin(); conf_i != toplevel->conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = *conf_i; + break; + } + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::get_conflicts(CdlNode node, bool (*pFn)(CdlConflict), std::vector<CdlConflict>& result) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_conflicts"); + CYG_REPORT_FUNCARG3XV(this, node, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_conflicts.begin(); + conf_i != current_transaction->new_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result.push_back(*conf_i); + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (0 != current_transaction); + + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->conflicts.begin(); conf_i != toplevel->conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result.push_back(*conf_i); + } + } + + CYG_REPORT_RETURN(); +} + +bool +CdlTransactionBody::has_conflict(CdlNode node, CdlProperty prop, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::has_conflict", "result %d"); + CYG_REPORT_FUNCARG4XV(this, node, prop, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + CYG_PRECONDITION_CLASSC(prop); + + bool result = false; + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_conflicts.begin(); + conf_i != current_transaction->new_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + + result = true; + break; + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (!result && (0 != current_transaction)); + + if (!result) { + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->conflicts.begin(); conf_i != toplevel->conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + + result = true; + break; + } + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +CdlConflict +CdlTransactionBody::get_conflict(CdlNode node, CdlProperty prop, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_conflict", "result %p"); + CYG_REPORT_FUNCARG4XV(this, node, prop, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + CYG_PRECONDITION_CLASSC(prop); + + CdlConflict result = 0; + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_conflicts.begin(); + conf_i != current_transaction->new_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = *conf_i; + break; + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while ((0 == result) && (0 != current_transaction)); + + if (0 == result) { + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->conflicts.begin(); conf_i != toplevel->conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + + result = *conf_i; + break; + } + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::get_conflicts(CdlNode node, CdlProperty prop, bool (*pFn)(CdlConflict), std::vector<CdlConflict>& result) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_conflict"); + CYG_REPORT_FUNCARG4XV(this, node, prop, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + CYG_PRECONDITION_CLASSC(prop); + + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_conflicts.begin(); + conf_i != current_transaction->new_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result.push_back(*conf_i); + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (0 != current_transaction); + + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->conflicts.begin(); conf_i != toplevel->conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + + result.push_back(*conf_i); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ get_structural_conflict() etc + +// ---------------------------------------------------------------------------- +bool +CdlTransactionBody::has_structural_conflict(CdlNode node, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::has_structural_conflict", "result %d"); + CYG_REPORT_FUNCARG3XV(this, node, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + // Because it is necessary to check whether or not any given + // conflict has been cleared in the current transaction or any + // parent transaction, recursion into the parent is not + // appropriate. + bool result = false; + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_structural_conflicts.begin(); + conf_i != current_transaction->new_structural_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = true; + break; + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (!result && (0 != current_transaction)); + + if (!result) { + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->structural_conflicts.begin(); conf_i != toplevel->structural_conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = true; + break; + } + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +CdlConflict +CdlTransactionBody::get_structural_conflict(CdlNode node, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_structural_conflict", "result %p"); + CYG_REPORT_FUNCARG3XV(this, node, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + CdlConflict result = 0; + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_structural_conflicts.begin(); + conf_i != current_transaction->new_structural_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = *conf_i; + break; + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while ((0 == result) && (0 != current_transaction)); + + if (0 == result) { + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->structural_conflicts.begin(); conf_i != toplevel->structural_conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = *conf_i; + break; + } + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::get_structural_conflicts(CdlNode node, bool (*pFn)(CdlConflict), std::vector<CdlConflict>& result) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_structural_conflicts"); + CYG_REPORT_FUNCARG3XV(this, node, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_structural_conflicts.begin(); + conf_i != current_transaction->new_structural_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result.push_back(*conf_i); + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (0 != current_transaction); + + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->structural_conflicts.begin(); conf_i != toplevel->structural_conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result.push_back(*conf_i); + } + } + + CYG_REPORT_RETURN(); +} + +bool +CdlTransactionBody::has_structural_conflict(CdlNode node, CdlProperty prop, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::has_structural_conflict", "result %d"); + CYG_REPORT_FUNCARG4XV(this, node, prop, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + CYG_PRECONDITION_CLASSC(prop); + + bool result = false; + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_structural_conflicts.begin(); + conf_i != current_transaction->new_structural_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + + result = true; + break; + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (!result && (0 != current_transaction)); + + if (!result) { + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->structural_conflicts.begin(); conf_i != toplevel->structural_conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + + result = true; + break; + } + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +CdlConflict +CdlTransactionBody::get_structural_conflict(CdlNode node, CdlProperty prop, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_structural_conflict", "result %p"); + CYG_REPORT_FUNCARG4XV(this, node, prop, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + CYG_PRECONDITION_CLASSC(prop); + + CdlConflict result = 0; + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_structural_conflicts.begin(); + conf_i != current_transaction->new_structural_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result = *conf_i; + break; + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while ((0 == result) && (0 != current_transaction)); + + if (0 == result) { + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->structural_conflicts.begin(); conf_i != toplevel->structural_conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + + result = *conf_i; + break; + } + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +void +CdlTransactionBody::get_structural_conflicts(CdlNode node, CdlProperty prop, bool (*pFn)(CdlConflict), + std::vector<CdlConflict>& result) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_structural_conflict"); + CYG_REPORT_FUNCARG4XV(this, node, prop, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + CYG_PRECONDITION_CLASSC(prop); + + std::list<CdlConflict>::const_iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_structural_conflicts.begin(); + conf_i != current_transaction->new_structural_conflicts.end(); + conf_i++) { + + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + result.push_back(*conf_i); + } + } + + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (0 != current_transaction); + + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->structural_conflicts.begin(); conf_i != toplevel->structural_conflicts.end(); conf_i++) { + if ((node == (*conf_i)->get_node()) && (prop == (*conf_i)->get_property()) && + !(this->has_conflict_been_cleared(*conf_i)) && (*pFn)(*conf_i)) { + + result.push_back(*conf_i); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ clear_conflicts() + +// ---------------------------------------------------------------------------- +// Clearing a conflict. This can only happen in the context of a +// transaction. +void +CdlTransactionBody::clear_conflicts(CdlNode node, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::clear_conflicts"); + CYG_REPORT_FUNCARG3XV(this, node, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + // Recursing into the parent is the wrong thing to do here, it + // would result in the conflict being cleared in the parent rather + // than in the current transaction. + std::list<CdlConflict>::iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_conflicts.begin(); conf_i != current_transaction->new_conflicts.end(); ) { + CdlConflict conflict = *conf_i++; + if ((node == conflict->get_node()) && !(this->has_conflict_been_cleared(conflict)) && (*pFn)(conflict)) { + this->clear_conflict(conflict); + } + } + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (0 != current_transaction); + + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->conflicts.begin(); conf_i != toplevel->conflicts.end(); ) { + CdlConflict conflict = *conf_i++; + if ((node == conflict->get_node()) && !(this->has_conflict_been_cleared(conflict)) && (*pFn)(conflict)) { + this->clear_conflict(conflict); + } + } + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionBody::clear_conflicts(CdlNode node, CdlProperty prop, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::clear_conflicts"); + CYG_REPORT_FUNCARG4XV(this, node, prop, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + std::list<CdlConflict>::iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_conflicts.begin(); conf_i != current_transaction->new_conflicts.end(); ) { + CdlConflict conflict = *conf_i++; + if ((node == conflict->get_node()) && (prop == conflict->get_property()) && + !(this->has_conflict_been_cleared(conflict)) && (*pFn)(conflict)) { + this->clear_conflict(conflict); + } + } + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (0 != current_transaction); + + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->conflicts.begin(); conf_i != toplevel->conflicts.end(); ) { + CdlConflict conflict = *conf_i++; + if ((node == conflict->get_node()) && (prop == conflict->get_property()) && + !(this->has_conflict_been_cleared(conflict)) && (*pFn)(conflict)) { + this->clear_conflict(conflict); + } + } + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionBody::clear_structural_conflicts(CdlNode node, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::clear_structural_conflicts"); + CYG_REPORT_FUNCARG3XV(this, node, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + std::list<CdlConflict>::iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_structural_conflicts.begin(); + conf_i != current_transaction->new_structural_conflicts.end(); + ) { + + CdlConflict conflict = *conf_i++; + if ((node == conflict->get_node()) && !(this->has_conflict_been_cleared(conflict)) && (*pFn)(conflict)) { + this->clear_conflict(conflict); + } + } + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (0 != current_transaction); + + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->structural_conflicts.begin(); conf_i != toplevel->structural_conflicts.end(); ) { + CdlConflict conflict = *conf_i++; + if ((node == conflict->get_node()) && !(this->has_conflict_been_cleared(conflict)) && (*pFn)(conflict)) { + this->clear_conflict(conflict); + } + } + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionBody::clear_structural_conflicts(CdlNode node, CdlProperty prop, bool (*pFn)(CdlConflict)) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::clear_structural_conflicts"); + CYG_REPORT_FUNCARG4XV(this, node, prop, pFn); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(node); + + std::list<CdlConflict>::iterator conf_i; + CdlTransaction current_transaction = this; + CdlToplevel toplevel = this->toplevel; + do { + CYG_LOOP_INVARIANT_CLASSC(current_transaction); + + for (conf_i = current_transaction->new_structural_conflicts.begin(); + conf_i != current_transaction->new_structural_conflicts.end(); + ) { + + CdlConflict conflict = *conf_i++; + if ((node == conflict->get_node()) && (prop == conflict->get_property()) && + !(this->has_conflict_been_cleared(conflict)) && (*pFn)(conflict)) { + + this->clear_conflict(conflict); + } + } + toplevel = current_transaction->toplevel; + current_transaction = current_transaction->parent; + } while (0 != current_transaction); + + CYG_ASSERT_CLASSC(toplevel); + for (conf_i = toplevel->structural_conflicts.begin(); conf_i != toplevel->structural_conflicts.end(); ) { + CdlConflict conflict = *conf_i++; + if ((node == conflict->get_node()) && (prop == conflict->get_property()) && (*pFn)(conflict)) { + this->clear_conflict(conflict); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ clear_conflict() + +// ---------------------------------------------------------------------------- +void +CdlTransactionBody::clear_conflict(CdlConflict conflict) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::clear_conflict"); + CYG_REPORT_FUNCARG2XV(this, conflict); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(conflict); + + // If this conflict was created during the transaction, it should + // be on the new_conflicts or new_structural_conflicts container + if (this == conflict->transaction) { + // The conflict should be on one of the two new_conflicts deques. + if (conflict->structural) { + std::list<CdlConflict>::iterator conf_i = std::find(new_structural_conflicts.begin(), + new_structural_conflicts.end(), conflict); + CYG_ASSERTC(conf_i != new_structural_conflicts.end()); + new_structural_conflicts.erase(conf_i); + } else { + std::list<CdlConflict>::iterator conf_i = std::find(new_conflicts.begin(), new_conflicts.end(), conflict); + CYG_ASSERTC(conf_i != new_conflicts.end()); + new_conflicts.erase(conf_i); + } + delete conflict; + + } else { + if (conflict->structural) { + deleted_structural_conflicts.push_back(conflict); + } else { + deleted_conflicts.push_back(conflict); + } + } + + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- +bool +CdlTransactionBody::has_conflict_been_cleared(CdlConflict conf) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::has_conflict_been_cleared", "result %d"); + CYG_REPORT_FUNCARG2XV(this, conf); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(conf); + + bool result = false; + CdlTransaction current_transaction = this; + do { + if (conf->structural) { + if (std::find(current_transaction->deleted_structural_conflicts.begin(), + current_transaction->deleted_structural_conflicts.end(), conf) != + current_transaction->deleted_structural_conflicts.end()) { + + result = true; + } + } else { + if (std::find(current_transaction->deleted_conflicts.begin(), current_transaction->deleted_conflicts.end(), + conf) != current_transaction->deleted_conflicts.end()) { + result = true; + } + } + + current_transaction = current_transaction->parent; + } while (!result && (0 != current_transaction)); + + CYG_REPORT_RETVAL(result); + return result; +} + +//}}} +//{{{ per-transaction data + +// ---------------------------------------------------------------------------- +// Accessing the per-transaction conflict data. + +const std::list<CdlConflict>& +CdlTransactionBody::get_new_conflicts() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_new_conflicts"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return new_conflicts; +} + +const std::list<CdlConflict>& +CdlTransactionBody::get_new_structural_conflicts() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_new_structural_conflicts"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return new_structural_conflicts; +} + +const std::vector<CdlConflict>& +CdlTransactionBody::get_deleted_conflicts() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_deleted_conflicts"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return deleted_conflicts; +} + +const std::vector<CdlConflict>& +CdlTransactionBody::get_deleted_structural_conflicts() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_deleted_structural_conflicts"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return deleted_structural_conflicts; +} + +const std::vector<CdlConflict>& +CdlTransactionBody::get_resolved_conflicts() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_resolved_conflicts"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return resolved_conflicts; +} + +const std::list<CdlConflict>& +CdlTransactionBody::get_global_conflicts_with_solutions() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_global_conflicts_with_solutions"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return global_conflicts_with_solutions; +} + +//}}} + +//}}} +//{{{ Commit/cancel operations + +// ---------------------------------------------------------------------------- +void +CdlTransactionBody::add_commit_cancel_op(CdlTransactionCommitCancelOp* op) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::add_commit_cancel_op"); + CYG_REPORT_FUNCARG2XV(this, op); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITIONC(0 != op); + + commit_cancel_ops.push_back(op); + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionBody::cancel_last_commit_cancel_op() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::cancel_last_commit_cancel_op"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CdlTransactionCommitCancelOp* op = *(commit_cancel_ops.rbegin()); + commit_cancel_ops.pop_back(); + op->cancel(this); + delete op; + + CYG_REPORT_RETURN(); +} + +CdlTransactionCommitCancelOp* +CdlTransactionBody::get_last_commit_cancel_op() const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::get_last_commit_cancel_op", "result %p"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CdlTransactionCommitCancelOp* op = *(commit_cancel_ops.rbegin()); + + CYG_REPORT_RETVAL(op); + return op; +} + +const std::vector<CdlTransactionCommitCancelOp*>& +CdlTransactionBody::get_commit_cancel_ops() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_commit_cancel_ops"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return commit_cancel_ops; +} + +//}}} +//{{{ Propagation + +// ---------------------------------------------------------------------------- +// Propagation should happen whenever one or more changes have been applied, +// so that the impact of these changes on other parts of the configuration +// can be fully assessed. The transaction keeps track of all the changes +// to date and invokes appropriate node and property update handlers. + +void +CdlTransactionBody::propagate() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::propagate"); + CYG_REPORT_FUNCARG1XV(this); + CYG_INVARIANT_THISC(CdlTransactionBody); + + // Now it is time to worry about value and active changes. + // Propagation may result in new entries, so only the + // front item of one of the vectors is modified. + while ((0 < value_changes.size()) || (0 < active_changes.size())) { + + if (0 != value_changes.size()) { + + CdlValuable valuable = value_changes.front(); + value_changes.pop_front(); + + // A value change may invalidate one or more solutions. + // This happens during propagation rather than at the time + // that the value is actually changed, so that multiple + // solutions can be applied in one go. + std::list<CdlConflict>::iterator conf_i, conf_j; + for (conf_i = new_conflicts.begin(); conf_i != new_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + (*conf_i)->update_solution_validity(valuable); + } + for (conf_i = global_conflicts_with_solutions.begin(); + conf_i != global_conflicts_with_solutions.end(); + ) { + + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + conf_j = conf_i++; + + (*conf_j)->update_solution_validity(valuable); + if (!(*conf_j)->has_known_solution()) { + global_conflicts_with_solutions.erase(conf_j); + } + } + + // If the valuable is no longer loaded then there is + // no need to worry about propagation + if (0 != valuable->get_toplevel()) { + + // Inform the valuable itself about the update, so that + // e.g. the value can be checked against legal_values + valuable->update(this, CdlUpdate_ValueChange); + + std::vector<CdlReferrer>& referrers = valuable->referrers; + std::vector<CdlReferrer>::iterator ref_i; + for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) { + ref_i->update(this, valuable, CdlUpdate_ValueChange); + } + } + + } else { + + CdlNode node = active_changes.front(); + active_changes.pop_front(); + + if (0 != node->get_toplevel()) { + node->update(this, CdlUpdate_ActiveChange); + + std::vector<CdlReferrer>& referrers = node->referrers; + std::vector<CdlReferrer>::iterator ref_i; + for (ref_i = referrers.begin(); ref_i != referrers.end(); ref_i++) { + ref_i->update(this, node, CdlUpdate_ActiveChange); + } + } + } + } + + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- +bool +CdlTransactionBody::is_propagation_required() const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::is_propagation_required", "result %d"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + bool result = false; + + if ((0 != value_changes.size()) || (0 != active_changes.size())) { + result = true; + } + + CYG_REPORT_RETVAL(result); + return result; +} + + +// ---------------------------------------------------------------------------- +void +CdlTransactionBody::add_legal_values_change(CdlValuable valuable) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::add_legal_values_change"); + CYG_REPORT_FUNCARG2XV(this, valuable); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(valuable); + + legal_values_changes.insert(valuable); + + CYG_REPORT_RETURN(); +} + +const std::set<CdlValuable>& +CdlTransactionBody::get_legal_values_changes() const +{ + CYG_REPORT_FUNCNAME("CdlTransaction::get_legal_values_changes"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + CYG_REPORT_RETURN(); + return legal_values_changes; +} + +//}}} +//{{{ Cancel + +// ---------------------------------------------------------------------------- +// Cancellation is straightforward, essentially it just involves clearing +// out all of the STL containers. The transaction object can then be-used, +// so fields like parent and toplevel must not change. +void +CdlTransactionBody::cancel() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::cancel"); + CYG_REPORT_FUNCARG1XV(this); + CYG_INVARIANT_THISC(CdlTransactionBody); + + // First take care of the cancel ops, if any, in case a cancel op + // depends on some of the other transaction state. + std::vector<CdlTransactionCommitCancelOp*>::reverse_iterator cancel_i; + for (cancel_i = commit_cancel_ops.rbegin(); cancel_i != commit_cancel_ops.rend(); cancel_i++) { + (*cancel_i)->cancel(this); + delete *cancel_i; + *cancel_i = 0; + } + commit_cancel_ops.clear(); + + this->changes.clear(); + std::list<CdlConflict>::iterator conf_i; + for (conf_i = this->new_conflicts.begin(); conf_i != this->new_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + delete *conf_i; + } + this->new_conflicts.clear(); + for (conf_i = this->new_structural_conflicts.begin(); conf_i != this->new_structural_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + delete *conf_i; + } + this->new_structural_conflicts.clear(); + + this->deleted_structural_conflicts.clear(); + this->deleted_conflicts.clear(); + + // Any conflicts created and resolved during this transaction will + // still be present in resolved_conflicts. Some global conflicts + // may be there as well. + std::vector<CdlConflict>::iterator conf_i2; + for (conf_i2 = this->resolved_conflicts.begin(); conf_i2 != this->resolved_conflicts.end(); conf_i2++) { + if (this == (*conf_i2)->transaction) { + delete (*conf_i2); + } + } + this->resolved_conflicts.clear(); + + // Any global conflicts which have been updated with a solution need + // to have that solution cleared. Currently no attempt is made to + // keep solutions valid for global conflicts. + for (conf_i = this->global_conflicts_with_solutions.begin(); + conf_i != this->global_conflicts_with_solutions.end(); + conf_i++) { + + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + (*conf_i)->clear_solution(); + } + this->global_conflicts_with_solutions.clear(); + + this->activated.clear(); + this->deactivated.clear(); + this->legal_values_changes.clear(); + this->value_changes.clear(); + this->active_changes.clear(); + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ Commit + +// ---------------------------------------------------------------------------- +// The commit operation. There are two main branches for this code. The +// first branch deals with sub-transactions, and basically involves +// transferring changes from the sub-transaction to the parent. It is +// assumed that the sub-transaction has been fully propagated, so +// data can just be transferred from the child to the parent. +// +// The second branch involves committing changes from a transaction to +// the toplevel, invoking the transaction callback if necessary. + +void +CdlTransactionBody::commit() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::commit"); + CYG_REPORT_FUNCARG1XV(this); + CYG_INVARIANT_THISC(CdlTransactionBody); + + std::map<CdlValuable, CdlValue>::iterator map_i; + std::list<CdlConflict>::iterator conf_i, conf_j; + std::vector<CdlConflict>::const_iterator conf_i2, conf_j2; + std::set<CdlNode>::iterator set_i, set_j, set_k; + std::set<CdlValuable>::iterator set_i2, set_j2; + + if (0 != parent) { + // Any conflicts that were solved by the inference engine further + // down are still resolved. + // Great care has to be taken with conflict ownership. The following + // cases have to be considered. + // 1) the resolved conflict is global, its transaction is zero, this + // conflict must only be destroyed if the toplevel transaction + // is committed - at which time time the conflict should appear + // on the deleted_conflicts lists. + // 2) the conflict belongs to a higher level transaction, we have + // recursed a certain amount trying to resolve it e.g. to explore + // OR branches of the tree. Again the resolved conflict can only + // be destroyed when the appropriate higher-level commit happens, + // and should appear on the deleted conflicts list. + // 3) the conflict was created and resolved further down the tree. + // We are keeping it around for informational purposes only. + // Associating it with this transaction allows the code to + // distinguish this case from (1) and (2). + for (conf_i2 = resolved_conflicts.begin(); conf_i2 != resolved_conflicts.end(); conf_i2++) { + CdlConflict conf = *conf_i2; + CYG_LOOP_INVARIANT_CLASSC(conf); + CYG_LOOP_INVARIANTC(parent->resolved_conflicts.end() == \ + std::find(parent->resolved_conflicts.begin(), \ + parent->resolved_conflicts.end(), conf)); + parent->resolved_conflicts.push_back(conf); + parent->dirty = true; + if (this == conf->transaction) { + conf->transaction = parent; + } + } + resolved_conflicts.clear(); + + // Any global conflicts for which solutions were found in the + // sub-transaction still have solutions in the parent. + for (conf_i = global_conflicts_with_solutions.begin(); conf_i != global_conflicts_with_solutions.end(); conf_i++) { + + CdlConflict conf = *conf_i; + CYG_LOOP_INVARIANT_CLASSC(conf); + + // It is not clear that this search is actually useful, especially + // given that the solution is currently stored with the conflict + // rather than with the transaction. + conf_j = std::find(parent->global_conflicts_with_solutions.begin(), + parent->global_conflicts_with_solutions.end(), + conf); + if (conf_j == parent->global_conflicts_with_solutions.end()) { + parent->global_conflicts_with_solutions.push_back(conf); + parent->dirty = true; + } + } + global_conflicts_with_solutions.clear(); + + // Now take care of deleted conflicts. + for (conf_i2 = deleted_conflicts.begin(); conf_i2 != deleted_conflicts.end(); conf_i2++) { + CdlConflict conf = *conf_i2; + CYG_LOOP_INVARIANT_CLASSC(conf); + // Possibilities to consider: + // 1) the conflict may have been local to the parent transaction, + // in which case it can be deleted. + // 2) the conflict may have been created in a higher-level + // transaction, in which case it has to be moved to the + // parent's deleted_conflicts vector. + // 3) the conflict may also have been global, again it needs + // to be propagated into the parent's deleted_conflicts vector. + // + // But that is not the whole story. If this sub-transaction was + // created specifically to resolve the conflict then the latter + // should appear on the resolved vector and not be destroyed + // immediately. This is true for both global and per-transaction + // conflicts. + // + // For global conflicts it is also necessary to worry about + // the global_conflicts_with_solutions list, that has to be + // kept in synch with the rest of the world. + conf_i = std::find(parent->new_conflicts.begin(), parent->new_conflicts.end(), conf); + bool can_delete = false; + if (conf_i != parent->new_conflicts.end()) { + parent->new_conflicts.erase(conf_i); + can_delete = true; + } else { + parent->deleted_conflicts.push_back(conf); + } + if (0 == conf->transaction) { + conf_j = std::find(parent->global_conflicts_with_solutions.begin(), + parent->global_conflicts_with_solutions.end(), + conf); + if (conf_j != parent->global_conflicts_with_solutions.end()) { + parent->global_conflicts_with_solutions.erase(conf_j); + } + } + if (conf == this->conflict) { + // The conflict may have been fortuitously resolved lower down, + // in which case it will have appeared in this->resolved_conflicts() + // and copied already. + conf_j2 = std::find(parent->resolved_conflicts.begin(), parent->resolved_conflicts.end(), conf); + if (conf_j2 == parent->resolved_conflicts.end()) { + parent->resolved_conflicts.push_back(conf); + parent->dirty = true; + } + } else if (can_delete) { + delete conf; + } + } + // Unnecessary, but let's keep things clean. + deleted_conflicts.clear(); + + // Deleted structural conflicts. For now the inference engine can do nothing with + // these so they are a bit simpler. + for (conf_i2 = deleted_structural_conflicts.begin(); conf_i2 != deleted_structural_conflicts.end(); conf_i2++) { + + CdlConflict conf = *conf_i2; + CYG_LOOP_INVARIANT_CLASSC(conf); + conf_i = std::find(parent->new_structural_conflicts.begin(), parent->new_structural_conflicts.end(), conf); + if (conf_i != parent->new_structural_conflicts.end()) { + parent->new_structural_conflicts.erase(conf_i); + delete conf; + } else { + parent->deleted_structural_conflicts.push_back(conf); + } + } + deleted_structural_conflicts.clear(); + + // All value changes need to be propagated from the child to the parent. + // Also, these value changes may invalidate existing solutions. + for (map_i = changes.begin(); map_i != changes.end(); map_i++) { + CYG_LOOP_INVARIANT_CLASSC(map_i->first); + CYG_LOOP_INVARIANT_CLASSOC(map_i->second); + parent->changes[map_i->first] = map_i->second; + + for (conf_i = parent->new_conflicts.begin(); conf_i != parent->new_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + (*conf_i)->update_solution_validity(map_i->first); + } + for (conf_i = parent->global_conflicts_with_solutions.begin(); + conf_i != parent->global_conflicts_with_solutions.end(); + ) { + + conf_j = conf_i++; + CYG_LOOP_INVARIANT_CLASSC(*conf_j); + + (*conf_j)->update_solution_validity(map_i->first); + if (!(*conf_j)->has_known_solution()) { + parent->global_conflicts_with_solutions.erase(conf_j); + } + } + } + changes.clear(); + + // Continue propagating the conflicts.New conflicts can just + // be added. + for (conf_i = new_conflicts.begin(); conf_i != new_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + parent->new_conflicts.push_back(*conf_i); + parent->dirty = true; + (*conf_i)->transaction = parent; + } + for (conf_i = new_structural_conflicts.begin(); conf_i != new_structural_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + parent->new_structural_conflicts.push_back(*conf_i); + parent->dirty = true; + (*conf_i)->transaction = parent; + } + // The cancel operation at the end will delete new conflicts, so the + // containers had better be cleared here. + new_conflicts.clear(); + new_structural_conflicts.clear(); + + + // Also keep track of nodes that have become active or inactive. + set_j = parent->activated.begin(); + for (set_i = activated.begin(); set_i != activated.end(); set_i++) { + set_j = parent->activated.insert(set_j, *set_i); + set_k = parent->deactivated.find(*set_i); + if (set_k != parent->deactivated.end()) { + parent->deactivated.erase(set_k); + } + } + + set_j = parent->deactivated.begin(); + for (set_i = deactivated.begin(); set_i != deactivated.end(); set_i++) { + set_j = parent->deactivated.insert(set_j, *set_i); + set_k = parent->activated.find(*set_i); + if (set_k != parent->activated.end()) { + parent->activated.erase(set_k); + } + } + activated.clear(); + deactivated.clear(); + + // Keep track of other property changes. + set_j2 = parent->legal_values_changes.begin(); + for (set_i2 = legal_values_changes.begin(); set_i2 != legal_values_changes.end(); set_i2++) { + set_j2 = parent->legal_values_changes.insert(set_j2, *set_i2); + } + legal_values_changes.clear(); + + // Any pending commit/cancel ops needs to be transferred to the parent + parent->commit_cancel_ops.insert(parent->commit_cancel_ops.end(), + this->commit_cancel_ops.begin(), this->commit_cancel_ops.end()); + this->commit_cancel_ops.clear(); + + } else { + + CYG_ASSERT_CLASSC(toplevel); + + // If there is a registered callback function, it is necessary to fill + // in the remaining fields of the all_changes callback structure. This + // should happen before any conflicts get deleted. The actual callback + // is invoked at the end, once all the changes have been moved to + // the toplevel. + CdlTransactionCallback all_changes; + if (0 != callback_fn) { + + for (map_i = changes.begin(); map_i != changes.end(); map_i++) { + + if (0 == map_i->first->get_toplevel()) { + continue; + } + CdlValueFlavor flavor = map_i->second.get_flavor(); + const CdlValue& old_value = map_i->first->get_whole_value(); + CYG_LOOP_INVARIANTC(flavor == old_value.get_flavor()); + bool value_changed = false; + + if (old_value.get_source() != map_i->second.get_source()) { + all_changes.value_source_changes.push_back(map_i->first); + } + if ((CdlValueFlavor_Bool == flavor) || (CdlValueFlavor_BoolData == flavor)) { + if (old_value.is_enabled() != map_i->second.is_enabled()) { + value_changed = true; + } + } + if (!value_changed && ((CdlValueFlavor_BoolData == flavor) || (CdlValueFlavor_Data == flavor))) { + if (old_value.get_simple_value() != map_i->second.get_simple_value()) { + value_changed = true; + } + } + if (value_changed) { + all_changes.value_changes.push_back(map_i->first); + } + } + for (conf_i = new_conflicts.begin(); conf_i != new_conflicts.end(); conf_i++) { + all_changes.new_conflicts.push_back(*conf_i); + } + for (conf_i = new_structural_conflicts.begin(); conf_i != new_structural_conflicts.end(); conf_i++) { + all_changes.new_structural_conflicts.push_back(*conf_i); + } + for (conf_i2 = deleted_conflicts.begin(); conf_i2 != deleted_conflicts.end(); conf_i2++) { + CdlNode node = (*conf_i2)->get_node(); + CYG_LOOP_INVARIANT_CLASSC(node); + + all_changes.nodes_with_resolved_conflicts.push_back(node); + } + for (conf_i2 = deleted_structural_conflicts.begin(); conf_i2 != deleted_structural_conflicts.end(); conf_i2++) { + CdlNode node = (*conf_i2)->get_node(); + CYG_LOOP_INVARIANT_CLASSC(node); + + all_changes.nodes_with_resolved_structural_conflicts.push_back(node); + } + for (set_i = activated.begin(); set_i != activated.end(); set_i++) { + if (0 != (*set_i)->get_toplevel()) { + all_changes.active_changes.push_back(*set_i); + } + } + for (set_i = deactivated.begin(); set_i != deactivated.end(); set_i++) { + if (0 != (*set_i)->get_toplevel()) { + all_changes.active_changes.push_back(*set_i); + } + } + for (set_i2 = legal_values_changes.begin(); set_i2 != legal_values_changes.end(); set_i2++) { + if (0 != (*set_i)->get_toplevel()) { + all_changes.legal_values_changes.push_back(*set_i2); + } + } + legal_values_changes.clear(); + } + + // All new values need to be installed in the appropriate valuable. + for (map_i = changes.begin(); map_i != changes.end(); map_i++) { + CYG_LOOP_INVARIANT_CLASSC(map_i->first); + CYG_LOOP_INVARIANT_CLASSOC(map_i->second); + map_i->first->value = map_i->second; + } + changes.clear(); + + // Sort out the conflicts. New conflicts can just be added, although + // care has to be taken to clear state - currently no attempt is + // made to check solution validity for global conflicts. + for (conf_i = new_conflicts.begin(); conf_i != new_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + toplevel->conflicts.push_back(*conf_i); + (*conf_i)->transaction = 0; + (*conf_i)->clear_solution(); + } + new_conflicts.clear(); + for (conf_i = new_structural_conflicts.begin(); conf_i != new_structural_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + toplevel->structural_conflicts.push_back(*conf_i); + (*conf_i)->transaction = 0; + (*conf_i)->clear_solution(); + } + new_structural_conflicts.clear(); + + // Resolved conflicts can be either global or per-transaction + // ones. If the former then the conflict will also be present + // in deleted_conflicts and will get deleted shortly. + for (conf_i2 = resolved_conflicts.begin(); conf_i2 != resolved_conflicts.end(); conf_i2++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i2); + if (0 != (*conf_i2)->transaction) { + delete *conf_i2; + } + } + resolved_conflicts.clear(); + + // Now process conflicts that have gone away. These must actually + // be deleted. + for (conf_i2 = deleted_conflicts.begin(); conf_i2 != deleted_conflicts.end(); conf_i2++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i2); + std::list<CdlConflict>::iterator tmp; + tmp = std::find(toplevel->conflicts.begin(), toplevel->conflicts.end(), *conf_i2); + CYG_LOOP_INVARIANTC(tmp != toplevel->conflicts.end()); + toplevel->conflicts.erase(tmp); + delete *conf_i2; + } + deleted_conflicts.clear(); + for (conf_i2 = deleted_structural_conflicts.begin(); conf_i2 != deleted_structural_conflicts.end(); conf_i2++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i2); + std::list<CdlConflict>::iterator tmp; + tmp = std::find(toplevel->structural_conflicts.begin(), toplevel->structural_conflicts.end(), *conf_i2); + CYG_LOOP_INVARIANTC(tmp != toplevel->structural_conflicts.end()); + toplevel->structural_conflicts.erase(tmp); + delete *conf_i2; + } + deleted_structural_conflicts.clear(); + + // Any global conflicts with solutions need to have their solutions cleared, + // since currently no attempt is made to preserve their accuracy. + for (conf_i = global_conflicts_with_solutions.begin(); conf_i != global_conflicts_with_solutions.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + (*conf_i)->clear_solution(); + } + global_conflicts_with_solutions.clear(); + + for (set_i = activated.begin(); set_i != activated.end(); set_i++) { + (*set_i)->active = true; + } + for (set_i = deactivated.begin(); set_i != deactivated.end(); set_i++) { + (*set_i)->active = false; + } + activated.clear(); + deactivated.clear(); + + // Invoke all pending commit operations + std::vector<CdlTransactionCommitCancelOp*>::iterator commit_i; + for (commit_i = commit_cancel_ops.begin(); commit_i != commit_cancel_ops.end(); commit_i++) { + (*commit_i)->commit(this); + delete *commit_i; + *commit_i = 0; + } + commit_cancel_ops.clear(); + + // Finally take care of the callback. + if (0 != callback_fn) { + (*callback_fn)(all_changes); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ Solution support + +// ---------------------------------------------------------------------------- +// Saving a solution basically involves remembering what changes took place +// in the corresponding sub-transaction. There is no need to worry about +// other data in the sub-transaction such as conflicts, because there +// are no actual value changes. + +void +CdlTransactionBody::save_solution() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::save_solution"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + // Solutions should only be applicable to sub-transactions immediately + // below the main transaction, since that is the only level at which + // inference callbacks occur + CYG_PRECONDITIONC((0 != parent) && (0 == parent->parent)); + + CYG_PRECONDITION_CLASSC(conflict); + CYG_PRECONDITIONC(0 == conflict->solution.size()); + + std::map<CdlValuable, CdlValue>::const_iterator map_i; + for (map_i = changes.begin(); map_i != changes.end(); map_i++) { + // If the valuable was calculated or is otherwise non-modifiable, + // there is no point in storing it with the solution since the + // information is unlikely to be of interest to the user. + CdlValuable valuable = map_i->first; + if (valuable->is_modifiable()) { + conflict->solution.push_back(*map_i); + } + } + + // save_solution() should operate like commit() or cancel(), i.e. + // it leaves an empty sub-transaction. This sub-transaction cannot + // actually be re-used at present because it still references a + // conflict for which a solution is now already in place, but that + // may get cleaned up in future. + this->cancel(); + CYG_REPORT_RETURN(); +} + +// ---------------------------------------------------------------------------- +// Can a solution be applied without e.g. overwriting a user value with +// an inferred value. There is a setting inference_override which controls +// this. +bool +CdlTransactionBody::user_confirmation_required() const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::user_confirmation_required", "result %d"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(this->parent); + + bool result = false; + + std::map<CdlValuable, CdlValue>::const_iterator val_i; + for (val_i = changes.begin(); val_i != changes.end(); val_i++) { + const CdlValue& old_value = parent->get_whole_value(val_i->first); + if (old_value.get_source() > CdlTransactionBody::inference_override) { + result = true; + break; + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// The inference engine is considering modifying a particular valuable. If +// the user has explicitly changed this valuable during the transaction then +// it would be inappropriate to suggest changing it again. +bool +CdlTransactionBody::changed_by_user(CdlValuable valuable) const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::changed_by_user", "result %d"); + CYG_REPORT_FUNCARG2XV(this, valuable); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(valuable); + + bool result = false; + std::map<CdlValuable, CdlValue>::const_iterator change_i = changes.find(valuable); + if (change_i != changes.end()) { + CdlValueSource source = change_i->second.get_source(); + if (CdlValueSource_User == source) { + result = true; + } + } + if (!result && (0 != parent)) { + result = parent->changed_by_user(valuable); + } + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// A variant which takes into account the hierarchy: disabling a container +// when a sub-node has just been changed by the user is also a no-no. +bool +CdlTransactionBody::subnode_changed_by_user(CdlContainer container) const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::subnode_changed_by_user", "result %d"); + CYG_REPORT_FUNCARG2XV(this, container); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(container); + + bool result = false; + + const std::vector<CdlNode>& contents = container->get_contents(); + std::vector<CdlNode>::const_iterator node_i; + for (node_i = contents.begin(); node_i != contents.end(); node_i++) { + CdlValuable valuable = dynamic_cast<CdlValuable>(*node_i); + if ((0 != valuable) && this->changed_by_user(valuable)) { + result = true; + break; + } + CdlContainer container = dynamic_cast<CdlContainer>(*node_i); + if ((0 != container) && this->subnode_changed_by_user(container)) { + result = true; + break; + } + } + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// Is one solution preferable to another? This code assumes that +// user_confirmation_required() and changed_by_user() have already +// been considered, so the only issue at stake here is the changes +// themselves. +// +// For now a simple metric of the number of changes is used. A more +// intelligent approach would take into account how much of the +// hierarchy is affected, e.g. how many other items would end +// up being disabled. Arguably the calling code should be able to +// supply an additional weighting. + +bool +CdlTransactionBody::is_preferable_to(CdlTransaction other) const +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransactionBody::preferable_to", "result %d"); + CYG_REPORT_FUNCARG2XV(this, other); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(other); + CYG_PRECONDITIONC(this != other); + + bool result = false; + if (this->changes.size() < other->changes.size()) { + result = true; + } + + CYG_REPORT_RETVAL(result); + return result; +} + +// ---------------------------------------------------------------------------- +// Applying solutions. Multiple solutions can be applied in one go. If there +// is any overlap, tough. Propagation needs to happen after solutions are +// applied. + +void +CdlTransactionBody::apply_solution(CdlConflict conflict) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::apply_solution"); + CYG_REPORT_FUNCARG2XV(this, conflict); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(conflict); + + // The solution can be for either a per-transaction conflict + // or a global one. There are two lists to search. + std::list<CdlConflict>::const_iterator conf_i; + conf_i = std::find(this->new_conflicts.begin(), this->new_conflicts.end(), conflict); + if (conf_i == this->new_conflicts.end()) { + conf_i = std::find(this->global_conflicts_with_solutions.begin(), + this->global_conflicts_with_solutions.end(), + conflict); + CYG_ASSERTC(conf_i != this->global_conflicts_with_solutions.end()); + } + + std::vector<std::pair<CdlValuable, CdlValue> >::const_iterator val_i; + for (val_i = conflict->solution.begin(); val_i != conflict->solution.end(); val_i++) { + CdlValuable valuable = val_i->first; + CYG_LOOP_INVARIANT_CLASSC(valuable); + + const CdlValue& old_value = this->get_whole_value(valuable); + this->set_whole_value(valuable, old_value, val_i->second); + } + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionBody::apply_solutions(const std::vector<CdlConflict>& solutions) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::apply_solutions"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + std::vector<CdlConflict>::const_iterator conf_i; + for (conf_i = solutions.begin(); conf_i != solutions.end(); conf_i++) { + + std::list<CdlConflict>::const_iterator conf_j; + conf_j = std::find(this->new_conflicts.begin(), this->new_conflicts.end(), conflict); + if (conf_j == this->new_conflicts.end()) { + conf_j = std::find(this->global_conflicts_with_solutions.begin(), + this->global_conflicts_with_solutions.end(), + conflict); + CYG_ASSERTC(conf_j != this->global_conflicts_with_solutions.end()); + } + + std::vector<std::pair<CdlValuable, CdlValue> >::const_iterator val_i; + for (val_i = conflict->solution.begin(); val_i != conflict->solution.end(); val_i++) { + CdlValuable valuable = val_i->first; + CYG_LOOP_INVARIANT_CLASSC(valuable); + const CdlValue& old_value = this->get_whole_value(valuable); + this->set_whole_value(valuable, old_value, val_i->second); + } + } + + CYG_REPORT_RETURN(); +} + +void +CdlTransactionBody::apply_all_solutions() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::apply_all_solutions"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + std::list<CdlConflict>::const_iterator conf_i; + for (conf_i = this->new_conflicts.begin(); conf_i != this->new_conflicts.end(); conf_i++) { + if ((*conf_i)->has_known_solution()) { + + std::vector<std::pair<CdlValuable, CdlValue> >::const_iterator val_i; + for (val_i = conflict->solution.begin(); val_i != conflict->solution.end(); val_i++) { + CdlValuable valuable = val_i->first; + CYG_LOOP_INVARIANT_CLASSC(valuable); + const CdlValue& old_value = this->get_whole_value(valuable); + this->set_whole_value(valuable, old_value, val_i->second); + } + } + } + for (conf_i = this->global_conflicts_with_solutions.begin(); + conf_i != this->global_conflicts_with_solutions.end(); + conf_i++) { + + CYG_ASSERTC((*conf_i)->has_known_solution()); + + std::vector<std::pair<CdlValuable, CdlValue> >::const_iterator val_i; + for (val_i = conflict->solution.begin(); val_i != conflict->solution.end(); val_i++) { + CdlValuable valuable = val_i->first; + CYG_LOOP_INVARIANT_CLASSC(valuable); + const CdlValue& old_value = this->get_whole_value(valuable); + this->set_whole_value(valuable, old_value, val_i->second); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ Inference + +// ---------------------------------------------------------------------------- +//{{{ resolve() - all per-transaction conflicts + +void +CdlTransactionBody::resolve(int level) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::resolve"); + CYG_REPORT_FUNCARG2XV(this, level); + CYG_PRECONDITION_THISC(); + + while(1) { + // Resolving one conflict may affect others, so iterating down the list + // is not safe. Instead we need to loop as long as there are conflicts + // to be considered. + std::list<CdlConflict>::iterator conf_i; + for (conf_i = new_conflicts.begin(); conf_i != new_conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + + // Is there any point in attempt to resolve this conflict? + if ((*conf_i)->has_known_solution() || + (*conf_i)->has_no_solution() || + !(*conf_i)->resolution_implemented()) { + continue; + } + this->resolve(*conf_i, level); + break; + } + if (conf_i == new_conflicts.end()) { + break; + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ resolve() - vector + +// ---------------------------------------------------------------------------- +void +CdlTransactionBody::resolve(const std::vector<CdlConflict>& conflicts, int level) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::resolve"); + CYG_REPORT_FUNCARG2XV(this, level); + CYG_PRECONDITION_THISC(); + + std::vector<CdlConflict>::const_iterator conf_i; + for (conf_i = conflicts.begin(); conf_i != conflicts.end(); conf_i++) { + CYG_LOOP_INVARIANT_CLASSC(*conf_i); + + // Is there any point in attempt to resolve this conflict? + if (!(*conf_i)->has_known_solution() && + !(*conf_i)->has_no_solution() && + (*conf_i)->resolution_implemented()) { + this->resolve(*conf_i, level); + } + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ resolve() - single conflict + +// ---------------------------------------------------------------------------- +// There is a conflict that may have a solution. The resolution +// attempt needs to happen in the context of a sub-transaction +// +// The conflict may have been created during this transaction, +// or it may be a global conflict left over from a previous +// transaction. This can be detected using the conflict's +// transaction field. The commit() code, amongst others, needs +// to handle global and per-transaction conflicts differently. + +void +CdlTransactionBody::resolve(CdlConflict conflict, int level) +{ + CYG_REPORT_FUNCNAME("CdlTransaction::resolve"); + CYG_REPORT_FUNCARG3XV(this, conflict, level); + CYG_PRECONDITION_THISC(); + CYG_PRECONDITION_CLASSC(conflict); + CYG_PRECONDITIONC(0 == conflict->solution.size()); + CYG_PRECONDITIONC((0 <= level) && (level <= inference_recursion_limit)); + + CdlTransaction sub_transaction = this->make(conflict); + CYG_PRECONDITION_CLASSC(sub_transaction); + if (!conflict->inner_resolve(sub_transaction, level)) { + CYG_ASSERTC(0 == sub_transaction->changes.size()); + sub_transaction->cancel(); + delete sub_transaction; + conflict->no_solution = true; + CYG_REPORT_RETURN(); + return; + } + // Is the inference engine lying? The conflict should be resolved + // in the sub-transaction. + if (conflict->is_structural()) { + if (std::find(sub_transaction->deleted_structural_conflicts.begin(), + sub_transaction->deleted_structural_conflicts.end(), + conflict) == sub_transaction->deleted_structural_conflicts.end()) { + + CYG_FAIL("The inference engine has proved too optimistic."); + sub_transaction->cancel(); + delete sub_transaction; + conflict->no_solution = true; + CYG_REPORT_RETURN(); + return; + } + } else { + if (std::find(sub_transaction->deleted_conflicts.begin(), sub_transaction->deleted_conflicts.end(), conflict) + == sub_transaction->deleted_conflicts.end()) { + + CYG_FAIL("The inference engine has proved too optimistic."); + sub_transaction->cancel(); + delete sub_transaction; + conflict->no_solution = true; + CYG_REPORT_RETURN(); + return; + } + } + + // Even if there is a solution it cannot always be applied + // automatically because that would affect existing user + // values. Instead the solution needs to be saved so that + // the user can inspect it later. This should only happen + // at level 0. If we have recursed into the inference + // engine then we should only worry about this right at + // the end, not at every stage (although internally the + // inference code may worry about this when choosing + // between alternatives). + if ((0 == level) && sub_transaction->user_confirmation_required()) { + sub_transaction->save_solution(); + sub_transaction->cancel(); + delete sub_transaction; + this->dirty = true; + + if (0 == conflict->transaction) { + // This is a global conflict, not a per-transaction one. + // There is a separate list of these conflicts. + std::list<CdlConflict>::const_iterator conf_i; + conf_i = std::find(this->global_conflicts_with_solutions.begin(), + this->global_conflicts_with_solutions.end(), + conflict); + if (conf_i == this->global_conflicts_with_solutions.end()) { + this->global_conflicts_with_solutions.push_back(conflict); + } + } + } else { + + // This sub-transaction is safe, it can be applied + // immediately. The commit code detects that the + // solution being committed is for a particular + // resolved conflict and will take care of moving that + // conflict to the resolved list. + conflict->solution_references.clear(); // No point in preserving this information + conflict->no_solution = false; // Redundant + std::map<CdlValuable, CdlValue>::const_iterator soln_i; + for (soln_i = sub_transaction->changes.begin(); soln_i != sub_transaction->changes.end(); soln_i++) { + conflict->solution.push_back(*soln_i); + } + sub_transaction->commit(); + delete sub_transaction; + } + + CYG_REPORT_RETURN(); +} + +//}}} +//{{{ resolve_recursion() + +// ---------------------------------------------------------------------------- +// resolve_recursion() +// +// The inference engine has tried one or more changes in the context of +// a sub-transaction. It is now necessary to check whether these changes +// are beneficial, i.e. whether or not any new problems are introduced +// that cannot be resolved. +bool +CdlTransactionBody::resolve_recursion(int level) +{ + CYG_REPORT_FUNCNAMETYPE("CdlTransaction::resolve_recursion", "result %d"); + CYG_REPORT_FUNCARG2XV(this, level); + CYG_PRECONDITION_THISC(); + + bool result = false; + this->propagate(); + if (0 == new_conflicts.size()) { + result = true; + CYG_REPORT_RETVAL(result); + return result; + } + if (level >= inference_recursion_limit) { + result = false; + CYG_REPORT_RETVAL(result); + return result; + } + + // There are new conflicts, but it may be possible to resolve them + // by a recursive invocation of the inference engine. + bool solutions_possible = false; + do { + this->resolve(level + 1); + std::list<CdlConflict>::const_iterator conf_i; + solutions_possible = false; + for (conf_i = this->new_conflicts.begin(); conf_i != this->new_conflicts.end(); conf_i++) { + if (!(*conf_i)->has_no_solution()) { + solutions_possible = true; + } + } + } while(solutions_possible); + + result = (0 == new_conflicts.size()); + CYG_REPORT_RETVAL(result); + return result; +} + +//}}} + +//}}} +//{{{ Body + +// ---------------------------------------------------------------------------- +// The majority of transactions involve the same set of steps. First one +// or more values are modified. Then there has to be propagation, inference, +// an inference callback, ... There may be a number of iterations. It is +// convenient to have a single transaction body function which takes care +// of all of that. +// +// If automatic inference is disabled then life is pretty simple, there +// should be one propagate() operation followed by a commit. +// +// If automatic inference is enabled but there is no inference callback +// then we need a loop consisting of propagation and inference, while +// progress is made. Progress can be detected by value changes. +// +// If there is an inference callback then life gets pretty complicated. +// The problem is figuring out exactly when the inference callback +// should be invoked: +// +// 1) any new conflicts should certainly result in a callback, to give +// the user a chance to cancel the changes. +// 2) any new solutions that have been applied automatically need to +// be shown to the user, again so that it is possible to cancel +// the changes. +// 3) any existing conflicts with a new solution, albeit one that cannot +// be applied automatically, should result in a callback. This is +// somewhat problematical since the new solution may in fact be +// identical to a previous one that the user has already decided +// against committing. +// +// It is not easy to keep track of when new conflicts or solutions get +// added to a transaction. Simply counting the entries in the +// appropriate STL containers is insufficient, as conflicts come and +// go. Instead it is necessary to have a "dirty" flag. Unfortunately +// this too is not fool-proof: a new conflict may have been created, +// resulting in the dirty flag being set, and then the conflict may +// have disappeared. + +void +CdlTransactionBody::body() +{ + CYG_REPORT_FUNCNAME("CdlTransaction::body"); + CYG_REPORT_FUNCARG1XV(this); + CYG_PRECONDITION_THISC(); + + // The Body() member function can only be applied to a toplevel + // transaction, it does not really make sense to apply it to + // a sub-transaction (at least, not yet); + CYG_PRECONDITIONC((0 == parent) && (0 != toplevel)); + + if (!inference_enabled) { + this->propagate(); + this->commit(); + CYG_REPORT_RETURN(); + return; + } + + if (0 == inference_callback) { + bool progress = false; + do { + progress = false; + this->propagate(); + CYG_LOOP_INVARIANTC(0 == value_changes.size()); + this->resolve(); + if (0 != value_changes.size()) { + progress = true; + } + } while(progress); + + this->commit(); + CYG_REPORT_RETURN(); + return; + } + + bool cancel = false; + + unsigned int resolved_size = 0; + unsigned int globals_with_solutions_size = 0; + + do { + bool progress = false; + do { + progress = false; + this->propagate(); + CYG_LOOP_INVARIANTC(0 == value_changes.size()); + this->resolve(); + if (0 != value_changes.size()) { + progress = true; + } + } while(progress); + + // Sanity check: if there are no conflicts and no new entries in + // the resolved vector, then stop here. The user has already seen + // and taken care of everything of interest. + if ((0 == new_conflicts.size()) && + (resolved_size == resolved_conflicts.size()) && + (globals_with_solutions_size == global_conflicts_with_solutions.size())) { + cancel = false; + break; + } + + // Also, if no conflicts have been added, no new solutions + // have been identified, and no new solutions have been applied, + // then there is no point in asking for user feedback. + if (!this->dirty) { + cancel = false; + break; + } + + // Clear state before invoking the callback. If the user does not + // change anything else then we should get out of the loop next + // time around. + this->dirty = false; + resolved_size = resolved_conflicts.size(); + globals_with_solutions_size = global_conflicts_with_solutions.size(); + + // Invoke the callback. If the result is cancel, do so. Otherwise + // we need to spin while things are changing. + if (CdlInferenceCallbackResult_Cancel == (*inference_callback)(this)) { + cancel = true; + } + } while(!cancel); + + if (cancel) { + this->cancel(); + } else { + this->commit(); + } + + CYG_REPORT_RETURN(); +} + +//}}} +
