Author: thonsew
Date: Sat Aug 20 23:45:35 2011
New Revision: 50869
URL: http://svn.gna.org/viewcvs/wesnoth?rev=50869&view=rev
Log:
Unit map changes
1. Added a self_check() function to verify all invariants
2. Added a recovery mode to search all uids when unit->underlying_id() is
changed after insertion and before extraction of the unit.
Modified:
trunk/src/unit_map.cpp
trunk/src/unit_map.hpp
Modified: trunk/src/unit_map.cpp
URL:
http://svn.gna.org/viewcvs/wesnoth/trunk/src/unit_map.cpp?rev=50869&r1=50868&r2=50869&view=diff
==============================================================================
--- trunk/src/unit_map.cpp (original)
+++ trunk/src/unit_map.cpp Sat Aug 20 23:45:35 2011
@@ -70,56 +70,71 @@
}
unit_map::t_ilist::iterator unit_map::begin_core() const {
+ self_check();
t_ilist::iterator i = ilist_.begin();
while (i != the_end_ && (i->unit_ == NULL)) { ++i; }
return i;
}
std::pair<unit_map::unit_iterator, bool> unit_map::add(const map_location &l,
const unit &u) {
+ self_check();
unit *p = new unit(u);
p->set_location(l);
return insert(p);
}
std::pair<unit_map::unit_iterator, bool> unit_map::move(const map_location
&src, const map_location &dst) {
+ self_check();
DBG_NG << "Unit map: Moving unit from " << src << " to " << dst << "\n";
+ //Find the unit at the src location
t_lmap::iterator i = lmap_.find(src);
if(i == lmap_.end()) { return
std::make_pair(make_unit_iterator(the_end_), false);}
- if(src == dst){ return
std::make_pair(make_unit_iterator(i->second),true);}
-
- unit *p = i->second->unit_;
- if(p == NULL){ return std::make_pair(make_unit_iterator(i->second),
false);}
-
t_ilist::iterator lit(i->second);
+
+ if(src == dst){ return std::make_pair(make_unit_iterator(lit),true);}
+
+ //Fail if there is no unit to move
+ unit *p = lit->unit_;
+ if(p == NULL){ return std::make_pair(make_unit_iterator(lit), false);}
+
+ p->set_location(dst);
///@todo upgrade to quick_erase when boost 1.42 supported by wesnoth
lmap_.erase(i);
- p->set_location(dst);
-
std::pair<t_lmap::iterator,bool> res = lmap_.insert(std::make_pair(dst,
lit));
+
+ //Fail and don't move if the destination is already occupied
if(res.second == false) {
p->set_location(src);
lmap_.insert(std::make_pair(src, lit));
return std::make_pair(make_unit_iterator(lit), false);
}
- return std::make_pair(make_unit_iterator(res.first->second), true);
+ self_check();
+
+ return std::make_pair(make_unit_iterator(lit), true);
}
/** Inserts the unit pointed to by @a p into the unit_map.
-1. Inserts the unit into the ilist.
-2. Inserts the iterator from ilist into the lmap at the desired location.
-If it fails it remove the unit from ilist.
-3. Inserts the iterator to the ilist into the umap with a unique id,
- creating a new one if necessary.
+It needs to succeed on the insertion to the umap and to the lmap
+otherwise all operations are reverted.
+1. Insert a unit_pod into the list
+2. Try insertion into the umap and remove the list item on failure
+3. Try insertion in the lmap and remove the umap and the list item on failure
+
+The one oddity is that to facilitate non-invalidating iterators the list
+sometimes has NULL pointers which should be used when they correspond
+to uids previously used.
*/
-std::pair<unit_map::unit_iterator, bool> unit_map::insert(unit *p)
-{
+std::pair<unit_map::unit_iterator, bool> unit_map::insert(unit *p) {
+ self_check();
+ assert(p);
+
size_t unit_id = p->underlying_id();
const map_location &loc = p->get_location();
@@ -131,31 +146,24 @@
unit_pod upod;
upod.unit_ = p;
- upod.deleted_uid_ = 0;
+ upod.deleted_uid_ = unit_id;
ilist_.push_front(upod);
t_ilist::iterator lit(ilist_.begin());
DBG_NG << "Adding unit " << p->underlying_id() << " - " << p->id()
<< " to location: (" << loc << ")\n";
- std::pair<t_lmap::iterator,bool> res = lmap_.insert(std::make_pair(loc,
lit ));
-
- if(!res.second){
- ilist_.pop_front();
- DBG_NG << "Trying to add " << p->name()
- << " - " << p->id() << " at location ("<<loc <<");
Occupied by "
- <<(res.first->second)->unit_->name()<< " - " <<
res.first->second->unit_->id() <<"\n";
- return std::make_pair(make_unit_iterator(the_end_), false);
- }
-
- std::pair<t_umap::iterator, bool> biter =
- umap_.insert(std::make_pair(unit_id, lit ));
-
- if (!biter.second) {
- if (! biter.first->second->unit_) {
- biter.first->second->unit_ = p;
+ std::pair<t_umap::iterator, bool> uinsert =
umap_.insert(std::make_pair(unit_id, lit ));
+
+ if (! uinsert.second) {
+ //If the UID is empty reinsert the unit in the same list element
+ if ( uinsert.first->second->unit_ == NULL) {
+ ilist_.pop_front();
+ lit = uinsert.first->second;
+ lit->unit_ = p;
+ assert(lit->ref_count_ != 0);
} else {
- unit *q = biter.first->second->unit_;
+ unit *q = uinsert.first->second->unit_;
ERR_NG << "Trying to add " << p->name()
<< " - " << p->id() << " - " <<
p->underlying_id()
<< " (" << loc << ") over " << q->name()
@@ -166,16 +174,37 @@
<< " to prevent duplicate id conflicts.\n";
p->clone(false);
- biter = umap_.insert(std::make_pair(p->underlying_id(),
lit ));
- if (!biter.second) { bool never_happen(false);
assert(never_happen); }
- }
- }
-
+ uinsert =
umap_.insert(std::make_pair(p->underlying_id(), lit ));
+ if (!uinsert.second) { bool never_happen(false);
assert(never_happen); }
+ }
+ }
+
+ std::pair<t_lmap::iterator,bool> linsert =
lmap_.insert(std::make_pair(loc, lit ));
+
+ //Fail if the location is occupied
+ if(! linsert.second) {
+ if(lit->ref_count_ == 0) {
+ //Undo a virgin insertion
+ ilist_.pop_front();
+ ///@todo replace with quick_erase(i) when wesnoth
supports boost 1.42 min version
+ umap_.erase(uinsert.first);
+ } else {
+ //undo a reinsertion
+ uinsert.first->second->unit_ = NULL;
+ }
+ DBG_NG << "Trying to add " << p->name()
+ << " - " << p->id() << " at location ("<<loc <<");
Occupied by "
+ <<(linsert.first->second)->unit_->name()<< " - " <<
linsert.first->second->unit_->id() <<"\n";
+
+ return std::make_pair(make_unit_iterator(the_end_), false);
+ }
+
+ self_check();
return std::make_pair( make_unit_iterator( lit ), true);
}
-std::pair<unit_map::unit_iterator, bool> unit_map::replace(const map_location
&l, const unit &u)
-{
+std::pair<unit_map::unit_iterator, bool> unit_map::replace(const map_location
&l, const unit &u) {
+ self_check();
//when 'l' is the reference to map_location that is part
//of that unit iterator which is to be deleted by erase,
// 'l' is invalidated by erase, too. Thus, 'add(l,u)' fails.
@@ -215,30 +244,74 @@
}
unit *unit_map::extract(const map_location &loc) {
+ self_check();
t_lmap::iterator i = lmap_.find(loc);
- if (i == lmap_.end()) {
- return NULL; }
-
- unit *res = i->second->unit_;
-
- DBG_NG << "Extract unit " << res->underlying_id() << " - " << res->id()
+ if (i == lmap_.end()) { return NULL; }
+
+ t_ilist::iterator lit(i->second);
+
+ unit *u = lit->unit_;
+ size_t uid( u->underlying_id() );
+
+ DBG_NG << "Extract unit " << uid << " - " << u->id()
<< " from location: (" << loc << ")\n";
- i->second->unit_ = NULL;
- i->second->deleted_uid_ = res->underlying_id();
- if(i->second->ref_count_ == 0){
- assert(i->second != the_end_);
- ilist_.erase( i->second );
- }
-
- ///@todo replace with quick_erase(i) when wesnoth supports boost 1.42
min version
- umap_.erase(res->underlying_id());
+ if(lit->ref_count_ == 0){
+ assert(lit != the_end_);
+ if(umap_.erase(uid) != 1){
+ error_recovery_externally_changed_uid(lit); }
+ ilist_.erase( lit );
+ } else {
+ //Soft extraction keeps the old lit item if any iterators
reference it
+ lit->unit_ = NULL;
+ lit->deleted_uid_ = uid;
+ assert( uid != 0);
+ }
+
lmap_.erase(i);
-
- return res;
-}
+ self_check();
+
+ return u;
+}
+
+/** error_recovery_externally_changed_uid is called when an attempt is made to
+ delete a umap_ element, which fails because the underlying_id() has
been changed externally.
+ It searches the entire umap_ to for a matching ilist_ element to erase.
+ */
+void unit_map::error_recovery_externally_changed_uid(t_ilist::iterator const &
lit) const {
+ std::string name, id ;
+ size_t uid;
+ if(lit->unit_ != NULL){
+ unit const * u(lit->unit_);
+ name = u->name();
+ id = u->id();
+ uid = u->underlying_id();
+ } else {
+ name = "unknown";
+ id = "unknown";
+ uid = lit->deleted_uid_;
+ }
+ t_umap::iterator uit(umap_.begin());
+ for(; uit != umap_.end(); ++uit){
+ if(uit->second == lit){ break; }
+ }
+ std::stringstream oldidss;
+ if (uit != umap_.end()) {
+ size_t olduid = uit->first ;
+ umap_.erase(uit);
+ oldidss << olduid;
+ } else {
+ oldidss << "unknown";
+ }
+
+ ERR_NG << "Extracting " << name << " - " << id
+ << " from unit map. Underlying ID changed from "<<
oldidss.str() << " to " << uid
+ << " between insertion and extraction, requiring an
exhaustive search of umap_.\n";
+}
+
size_t unit_map::erase(const map_location &loc) {
+ self_check();
unit *u = extract(loc);
if (!u) return 0;
delete u;
@@ -246,9 +319,13 @@
}
unit_map::unit_iterator unit_map::find(size_t id) {
- return make_unit_iterator<t_umap::iterator>(umap_.find(id) ); }
+ self_check();
+ t_umap::iterator i(umap_.find(id));
+ if((i != umap_.end()) && i->second->unit_==NULL){ i = umap_.end() ;}
+ return make_unit_iterator<t_umap::iterator>( i ); }
unit_map::unit_iterator unit_map::find(const map_location &loc) {
+ self_check();
return make_unit_iterator<t_lmap::iterator>(lmap_.find(loc) ); }
unit_map::unit_iterator unit_map::find_leader(int side) {
@@ -290,4 +367,59 @@
return const_leaders;
}
-
+#ifdef DEBUG
+
+bool unit_map::self_check() const {
+ bool found_the_end(false), good(true);
+ t_ilist::const_iterator lit(ilist_.begin());
+ for(; lit != ilist_.end(); ++lit){
+ if(lit == the_end_){ found_the_end = true; continue; }
+ if(lit->ref_count_ < 0){
+ good=false;
+ ERR_NG << "unit_map list element ref_count_ <0 is " <<
lit->ref_count_<<"\n"; }
+ if(lit->unit_ != NULL){
+ lit->unit_->id(); //crash if bad pointer
+ } else {
+ if(lit->ref_count_ <= 0){
+ good=false;
+ ERR_NG << "unit_map list element ref_count_ <=0
is " << lit->ref_count_<<", when unit deleted.\n"; }
+ if(lit->deleted_uid_ <= 0 ){
+ good=false;
+ ERR_NG << "unit_map list element deleted_uid_
<=0 is " << lit->deleted_uid_<<"\n"; }
+ }
+ }
+
+ if(!found_the_end){
+ good=false;
+ ERR_NG << "unit_map list the_end_ is missing. " <<"\n";}
+
+ t_umap::const_iterator uit(umap_.begin());
+ for(; uit != umap_.end(); ++uit){
+ if(uit->first <= 0){
+ good=false;
+ ERR_NG << "unit_map umap uid <=0 is " << uit->first
<<"\n"; }
+ if(uit->second == the_end_ ){
+ good=false;
+ ERR_NG << "unit_map umap element == the_end_ "<<"\n"; }
+ if(uit->second->unit_ == NULL && uit->second->ref_count_ == 0 ){
+ good=false;
+ ERR_NG << "unit_map umap unit_==NULL when refcount == 0
uid="<<uit->second->deleted_uid_<<"\n";
+ }
+ if(uit->second->unit_ && uit->second->unit_->underlying_id() !=
uit->first){
+ good=false;
+ ERR_NG << "unit_map umap uid("<<uit->first<<") !=
underlying_id()["<< uit->second->unit_->underlying_id()<< "]\n"; }
+ }
+ t_lmap::const_iterator locit(lmap_.begin());
+ for(; locit != lmap_.end(); ++locit){
+ if(locit->second == the_end_ ){
+ good=false;
+ ERR_NG << "unit_map lmap element == the_end_ "<<"\n"; }
+ if(locit->first != locit->second->unit_->get_location()){
+ good=false;
+ ERR_NG << "unit_map lmap location !=
unit->get_location() " <<"\n"; }
+ }
+ //assert(good);
+ return good;
+}
+
+#endif
Modified: trunk/src/unit_map.hpp
URL:
http://svn.gna.org/viewcvs/wesnoth/trunk/src/unit_map.hpp?rev=50869&r1=50868&r2=50869&view=diff
==============================================================================
--- trunk/src/unit_map.hpp (original)
+++ trunk/src/unit_map.hpp Sat Aug 20 23:45:35 2011
@@ -186,13 +186,21 @@
public:
pointer operator->() const {
assert(valid());
+ tank_->self_check();
return i_->unit_; }
reference operator*() const {
+ tank_->self_check();
+ if(!valid()){
+ if(!tank_){std::cerr<<"tank is NULL"<<"\n";}
+ if(i_==the_end()){std::cerr<<"i_ is the
end"<<"\n";}
+ if(i_->unit_==NULL){std::cerr<<"i_ unit is NULL
with uid="<<i_->deleted_uid_<<"\n";}
+ }
assert(valid());
return *i_->unit_; }
iterator_base& operator++() {
assert( valid_entry() );
+ tank_->self_check();
iterator_type new_i(i_);
do{
++new_i;
@@ -212,6 +220,7 @@
iterator_base& operator--() {
assert( tank_ && i_ != the_list().begin() );
+ tank_->self_check();
iterator_type begin(the_list().begin());
dec();
do {
@@ -263,12 +272,14 @@
void inc() { if(valid_ref_count()) { ++(i_->ref_count_); } }
///Decrement the reference counter
- ///Delete the list element if the unit is gone and the
reference counter is zero
+ ///Delete the list element and the dangling umap reference if
the unit is gone and the reference counter is zero
///@note this deletion will advance i_ to the next list element.
void dec() {
if( valid_ref_count() ){
assert(i_->ref_count_ != 0);
if( (--(i_->ref_count_) == 0) && (i_->unit_ ==
NULL) ){
+ if(tank_->umap_.erase(i_->deleted_uid_)
!= 1){
+
tank_->error_recovery_externally_changed_uid(i_); }
i_ = the_list().erase(i_);
} } }
@@ -301,8 +312,6 @@
unit_map(const unit_map &that);
unit_map& operator=(const unit_map &that);
- /** A unit map with a copy of a single unit in it. */
- unit_map(const map_location &loc, const unit &u);
~unit_map();
void swap(unit_map& o);
@@ -398,6 +407,17 @@
* It can be reinserted later, if needed.
*/
unit *extract(const map_location &loc);
+
+ ///Finds and deletes the umap_ item associated with @a lit when the
underlying_id()
+ ///has been changed externally after insertion and before extraction
+ void error_recovery_externally_changed_uid(t_ilist::iterator const &
lit) const;
+
+ ///Checks invariants. For debugging purposes only. Doesn't do
anything in non-debug mode.
+ bool self_check() const
+#ifndef DEBUG
+ { return true; }
+#endif
+ ;
private:
///Creates and inserts a unit_pod called the_end_ as a sentinel in the
ilist
@@ -438,7 +458,7 @@
* underlying_id -> ilist::iterator. This requires that underlying_id be
* unique (which is enforced in unit_map::insert).
*/
- t_umap umap_;
+ mutable t_umap umap_;
/**
* location -> ilist::iterator.
_______________________________________________
Wesnoth-commits mailing list
[email protected]
https://mail.gna.org/listinfo/wesnoth-commits