Changed unit_map to not store invalidated iterators.

Added incremental recovery of invalidated but reference counted
unit_map::iterators.
This commit is contained in:
Thonsew 2011-08-10 12:45:43 +00:00
parent 53d83051de
commit f92dd22281
8 changed files with 268 additions and 171 deletions

View file

@ -2263,7 +2263,7 @@ WML_HANDLER_FUNCTION(heal_unit, event_info, cfg)
const bool animate = cfg["animate"].to_bool(false);
const vconfig healed_filter = cfg.child("filter");
unit_map::const_unit_iterator u;
unit_map::unit_iterator u;
bool only_unit_at_loc1 = healed_filter.null();
bool heal_amount_to_set = true;
for(u = units->begin(); u != units->end(); ++u) {

View file

@ -1689,7 +1689,7 @@ void menu_handler::continue_move(mouse_handler &mousehandler, int side_num)
side_num, mousehandler);
}
void menu_handler::move_unit_to_loc(const unit_map::const_iterator &ui,
void menu_handler::move_unit_to_loc(const unit_map::iterator &ui,
const map_location& target, bool continue_move, int side_num,
mouse_handler &mousehandler)
{

View file

@ -91,7 +91,7 @@ public:
unit_map::iterator current_unit();
unit_map::const_iterator current_unit() const
{ return const_cast<menu_handler *>(this)->current_unit(); }
void move_unit_to_loc(const unit_map::const_iterator& ui, const map_location& target,
void move_unit_to_loc(const unit_map::iterator& ui, const map_location& target,
bool continue_move, int side_num, mouse_handler &mousehandler);
///@return Whether or not the recruit was successful
bool do_recruit(const std::string& name, int side_num, const map_location& last_hex);

View file

@ -29,12 +29,15 @@ static lg::log_domain log_engine("engine");
#define LOG_NG LOG_STREAM(info, log_engine)
#define DBG_NG LOG_STREAM(debug, log_engine)
unit_map::unit_map(const unit_map& that) :
map_(),
umap_(),
lmap_(),
ilist_(),
num_iters_(0),
num_invalid_(0)
{
init_end();
for (const_unit_iterator i = that.begin(); i != that.end(); i++) {
add(i->get_location(), *i);
}
@ -47,38 +50,24 @@ unit_map &unit_map::operator=(const unit_map &that)
return *this;
}
void unit_map::swap(unit_map &o)
{
assert(num_iters_ == 0 && o.num_iters_ == 0);
std::swap(map_, o.map_);
void unit_map::swap(unit_map &o) {
assert(num_iters()==0 && o.num_iters() == 0);
std::swap(umap_, o.umap_);
std::swap(lmap_, o.lmap_);
std::swap(ilist_, o.ilist_);
std::swap(the_end_, o.the_end_);
std::swap(num_invalid_, o.num_invalid_);
}
unit_map::~unit_map() {
clear();
clear(true);
}
unit_map::unit_iterator unit_map::begin() {
// This call just needs to go somewhere that is likely to be
// called when num_iters_ == 0. This seems as good a place as any.
clean_invalid();
umap::iterator i = map_.begin();
while (i != map_.end() && !is_valid(i)) {
++i;
}
return unit_iterator(i, this);
}
unit_map::const_unit_iterator unit_map::begin() const {
umap::const_iterator i = map_.begin();
while (i != map_.end() && !is_valid(i)) {
++i;
}
return const_unit_iterator(i, this);
unit_map::t_ilist::iterator unit_map::begin_core() const {
t_ilist::iterator i = ilist_.begin();
while (i != the_end_ && (i->unit_ == NULL)) { ++i; }
return i;
}
void unit_map::add(const map_location &l, const unit &u) {
@ -88,11 +77,23 @@ void unit_map::add(const map_location &l, const unit &u) {
}
void unit_map::move(const map_location &src, const map_location &dst) {
DBG_NG << "Unit map: Moving unit from " << src << " to " << dst << "\n";
unit *p = extract(src);
assert(p);
p->set_location(dst);
insert(p);
DBG_NG << "Unit map: Moving unit from " << src << " to " << dst << "\n";
if(src != dst){
t_lmap::iterator i = lmap_.find(src);
assert(i != lmap_.end()) ;
unit *p = i->second->unit_;
assert(p);
p->set_location(dst);
std::pair<t_lmap::iterator,bool> res = lmap_.insert(std::make_pair(dst, i->second));
assert(res.second);
///@todo upgrade to quick_erase when boost 1.42 supported by wesnoth
lmap_.erase(i);
}
}
void unit_map::insert(unit *p)
@ -107,15 +108,21 @@ void unit_map::insert(unit *p)
return;
}
std::pair<umap::iterator, bool> biter =
map_.insert(std::make_pair(unit_id, p));
unit_pod upod;
upod.unit_ = p;
ilist_.push_front(upod);
t_ilist::iterator lit(ilist_.begin());
std::pair<t_umap::iterator, bool> biter =
umap_.insert(std::make_pair(unit_id, lit ));
if (biter.second) {
} else if (!biter.first->second) {
biter.first->second = p;
} else if (! biter.first->second->unit_) {
biter.first->second->unit_ = p;
--num_invalid_;
} else {
unit *q = biter.first->second;
unit *q = biter.first->second->unit_;
ERR_NG << "Trying to add " << p->name()
<< " - " << p->id() << " - " << p->underlying_id()
<< " (" << loc << ") over " << q->name()
@ -132,7 +139,7 @@ void unit_map::insert(unit *p)
DBG_NG << "Adding unit " << p->underlying_id() << " - " << p->id()
<< " to location: (" << loc << ")\n";
std::pair<lmap::iterator,bool> res = lmap_.insert(std::make_pair(loc, p));
std::pair<t_lmap::iterator,bool> res = lmap_.insert(std::make_pair(loc, lit ));
assert(res.second);
}
@ -148,35 +155,49 @@ void unit_map::replace(const map_location &l, const unit &u)
add(loc, u);
}
void unit_map::clear()
{
assert(num_iters_ == 0);
size_t unit_map::num_iters() const {
//Check for outstanding iterators
size_t num_iters(0);
t_ilist::const_iterator ii(ilist_.begin());
for( ; ii != the_end_ ; ++ii){
if(ii->ref_count_ < 0){
bool a_reference_counter_overflowed(false);
assert(a_reference_counter_overflowed); }
num_iters += ii->ref_count_; }
for (umap::iterator i = map_.begin(); i != map_.end(); ++i) {
return num_iters;
}
void unit_map::clear(bool force) {
assert(force || (num_iters() == 0));
for (t_ilist::iterator i = ilist_.begin(); i != the_end_; ++i) {
if (is_valid(i)) {
DBG_NG << "Delete unit " << i->second->underlying_id() << "\n";
delete i->second;
DBG_NG << "Delete unit " << i->unit_->underlying_id() << "\n";
delete i->unit_;
}
}
lmap_.clear();
map_.clear();
umap_.clear();
ilist_.clear();
}
unit *unit_map::extract(const map_location &loc)
{
lmap::iterator i = lmap_.find(loc);
unit *unit_map::extract(const map_location &loc) {
t_lmap::iterator i = lmap_.find(loc);
if (i == lmap_.end()) {
return NULL; }
unit *res = i->second;
unit *res = i->second->unit_;
DBG_NG << "Extract unit " << res->underlying_id() << " - " << res->id()
<< " from location: (" << loc << ")\n";
i->second = NULL;
++num_invalid_;
map_.erase(res->underlying_id());
///todo replace with quick_erase(i) when boost min version supports it
i->second->unit_ = NULL;
if(i->second->ref_count_ == 0){ ilist_.erase( i->second ); }
///@todo replace with quick_erase(i) when wesnoth supports boost 1.42 min version
umap_.erase(res->underlying_id());
lmap_.erase(i);
return res;
@ -190,39 +211,17 @@ size_t unit_map::erase(const map_location &loc)
return 1;
}
void unit_map::clean_invalid() {
if (num_iters_ > 0 || num_invalid_ < lmap_.size())
return;
size_t num_cleaned = 0;
umap::iterator iter = map_.begin();
while (iter != map_.end()) {
if (!is_valid(iter)) {
iter = map_.erase(iter);
++num_cleaned;
} else {
++iter;
}
}
num_invalid_ -= num_cleaned;
LOG_NG << "unit_map::clean_invalid - removed " << num_cleaned << " invalid map entries.\n";
}
unit_map::unit_iterator unit_map::find(size_t id) {
umap::iterator iter = map_.find(id);
if (!is_valid(iter)) iter = map_.end();
return unit_iterator(iter, this);
t_umap::iterator iter = umap_.find(id);
if (!is_valid(iter)) { iter = umap_.end(); }
return unit_iterator(iter, & ilist_);
}
unit_map::unit_iterator unit_map::find(const map_location &loc) {
lmap::const_iterator i = lmap_.find(loc);
t_lmap::iterator i = lmap_.find(loc);
if (i == lmap_.end()) {
return unit_iterator(map_.end(), this);
}
return(find(i->second->underlying_id()));
return unit_iterator(the_end_, & ilist_); }
return unit_iterator(i, & ilist_);
}
unit_map::unit_iterator unit_map::find_leader(int side)

View file

@ -19,10 +19,11 @@
#ifndef UNIT_MAP_H_INCLUDED
#define UNIT_MAP_H_INCLUDED
#include "utils/reference_counter.hpp"
#include "map_location.hpp"
#include <cassert>
#include <map>
#include <list>
#include <boost/unordered_map.hpp>
class unit;
@ -43,133 +44,143 @@ class unit;
* never be stored into data structures, as it will cause slowdowns!
*/
class unit_map {
public:
typedef boost::unordered_map<size_t, unit *> umap;
typedef boost::unordered_map<map_location, unit *> lmap;
struct unit_pod {
unit * unit_;
mutable n_ref_counter::t_ref_counter<signed int> ref_count_;
};
typedef std::list<unit_pod> t_ilist;
typedef boost::unordered_map<size_t, typename t_ilist::iterator> t_umap;
typedef boost::unordered_map<map_location, typename t_ilist::iterator> t_lmap;
unit_map() : map_(), lmap_(), num_iters_(0), num_invalid_(0) { };
public:
unit_map() : umap_(), lmap_(), ilist_(), num_iters_(0), num_invalid_(0) { init_end();};
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);
// ~~~ Begin iterator code ~~~
template<typename iter_types>
struct iterator_base
{
typedef unit value_type;
typedef std::forward_iterator_tag iterator_category;
typedef int difference_type;
typedef unit *pointer;
typedef unit &reference;
typedef typename iter_types::map_type map_type;
typedef typename iter_types::value_type value_type;
typedef typename iter_types::pointer_type pointer;
typedef typename iter_types::reference_type reference;
typedef typename iter_types::container_type container_type;
typedef typename iter_types::iterator_type iterator_type;
iterator_base(): map_(NULL), i_() { }
iterator_base(): tank_(NULL), i_() { }
iterator_base(iterator_type i, map_type *m)
: map_(m), i_(i)
{ map_->add_iter(); }
iterator_base(iterator_type i, container_type *m)
: tank_(m), i_(i) {
inc(); }
~iterator_base()
{ if (map_) map_->remove_iter(); }
~iterator_base() { dec(); }
template<typename that_types>
iterator_base(const iterator_base<that_types> &that)
: map_(that.map_), i_(that.i_)
{ if (map_) map_->add_iter(); }
: tank_(that.tank_), i_(that.i_)
{ inc(); }
template<typename that_types>
iterator_base &operator=(const iterator_base<that_types> &that)
{
if (map_) map_->remove_iter();
map_ = that.map_;
iterator_base &operator=(const iterator_base<that_types> &that) {
dec();
tank_ = that.tank_;
i_ = that.i_;
if (map_) map_->add_iter();
inc();
return *this;
}
iterator_base(const iterator_base &that)
: map_(that.map_), i_(that.i_)
{ if (map_) map_->add_iter(); }
private:
iterator_base(t_umap::iterator ui, container_type *m)
: tank_(m), i_(ui->second) {
inc(); }
iterator_base &operator=(const iterator_base &that)
{
if (map_) map_->remove_iter();
map_ = that.map_;
i_ = that.i_;
if (map_) map_->add_iter();
iterator_base(t_lmap::iterator ui, container_type *m)
: tank_(m), i_(ui->second) {
inc(); }
void inc() { if(valid()){ ++(i_->ref_count_); } }
void dec() {
if( valid() && (--(i_->ref_count_) == 0) && (i_->unit_ == NULL) ){
i_ = tank_->erase(i_); } }
iterator_type the_end() const {return --tank_->end();}
public:
pointer operator->() const { assert(valid()); return i_->unit_; }
reference operator*() const { assert(valid()); return *i_->unit_; }
iterator_base& operator++() {
assert( valid() );
iterator_type new_i(i_);
++new_i;
while (new_i != the_end() && (new_i->unit_ == NULL)) {
++new_i;
}
dec();
i_ = new_i;
inc();
return *this;
}
pointer operator->() const
{ assert(valid()); return i_->second; }
reference operator*() const
{ assert(valid()); return *i_->second; }
iterator_base& operator++()
{
assert(map_ && i_ != map_->map_.end());
do ++i_;
while (i_ != map_->map_.end() && !i_->second);
return *this;
}
iterator_base operator++(int)
{
iterator_base operator++(int) {
iterator_base temp(*this);
operator++();
return temp;
}
iterator_base& operator--()
{
assert(map_ && i_ != map_->map_.begin());
iterator_type next(map_->map_.begin()), oldi(i_);
do { i_ = ++next;
}while(next != oldi );
iterator_base& operator--() {
assert( tank_ && i_ != tank_->begin() );
iterator_type begin(tank_->begin());
dec();
do {
--i_ ;
}while(i_ != begin && (i_->unit_ == NULL));
inc();
return *this;
}
iterator_base operator--(int)
{
iterator_base operator--(int) {
iterator_base temp(*this);
operator--();
return temp;
}
bool valid() const
{ return map_ && map_->is_valid(i_); }
bool valid() const { return tank_ && i_ != the_end(); }
bool operator==(const iterator_base &rhs) const
{ assert(map_ == rhs.map_); return i_ == rhs.i_; }
{ assert(tank_ == rhs.tank_); return i_ == rhs.i_; }
bool operator!=(const iterator_base &rhs) const
{ return !operator==(rhs); }
map_type* get_map() const { return map_; }
// container_type* get_map() const { return tank_; }
template<typename Y> friend struct iterator_base;
private:
map_type* map_;
friend class unit_map;
container_type* tank_;
iterator_type i_;
};
struct standard_iter_types {
typedef unit_map map_type;
typedef unit_map::umap::iterator iterator_type;
typedef unit_map::t_ilist container_type;
typedef unit_map::t_ilist::iterator iterator_type;
typedef unit value_type;
typedef value_type* pointer_type;
typedef value_type& reference_type;
};
struct const_iter_types {
typedef const unit_map map_type;
typedef unit_map::umap::const_iterator iterator_type;
typedef unit_map::t_ilist container_type;
typedef unit_map::t_ilist::iterator iterator_type;
typedef const unit value_type;
typedef value_type* pointer_type;
typedef value_type& reference_type;
@ -188,14 +199,11 @@ public:
unit_iterator find(const map_location &loc);
const_unit_iterator find(const map_location &loc) const
{ return const_cast<unit_map *>(this)->find(loc); }
const_unit_iterator find(size_t id) const
{ return const_cast<unit_map *>(this)->find(id); }
const_unit_iterator find(const map_location &loc) const { return const_cast<unit_map *>(this)->find(loc); }
const_unit_iterator find(size_t id) const { return const_cast<unit_map *>(this)->find(id); }
unit_iterator find_leader(int side);
const_unit_iterator find_leader(int side) const
{ return const_cast<unit_map *>(this)->find_leader(side); }
const_unit_iterator find_leader(int side) const { return const_cast<unit_map *>(this)->find_leader(side); }
unit_iterator find_first_leader(int side);
std::vector<unit_iterator> find_leaders(int side);
@ -203,15 +211,16 @@ public:
size_t count(const map_location& loc) const { return static_cast<size_t>(lmap_.count(loc)); }
unit_iterator begin();
const_unit_iterator begin() const;
unit_iterator begin() { return unit_iterator( begin_core(), & ilist_); }
const_unit_iterator begin() const { return const_unit_iterator( begin_core(), & ilist_); }
unit_iterator end() { return iterator(map_.end(), this); }
const_unit_iterator end() const { return const_iterator(map_.end(), this); }
unit_iterator end() { return iterator(the_end_, & ilist_); }
const_unit_iterator end() const { return const_iterator(the_end_, & ilist_); }
size_t size() const { return lmap_.size(); }
size_t num_iters() const ;
void clear();
void clear(bool force = false);
/**
* Adds a copy of unit @a u at location @a l of the map.
@ -258,28 +267,42 @@ public:
*/
unit *extract(const map_location &loc);
void swap(unit_map& o);
private:
void init_end(){
assert(ilist_.empty());
ilist_.push_front(unit_pod());
the_end_ = ilist_.begin();
};
t_ilist::iterator begin_core() const ;
/** Removes invalid entries in map_ if safe and needed. */
void clean_invalid();
// void clean_invalid();
void add_iter() const { ++num_iters_; }
void remove_iter() const { --num_iters_; }
// void add_iter() const { ++num_iters_; }
// void remove_iter() const { --num_iters_; }
bool is_valid(const umap::const_iterator &i) const
{ return i != map_.end() && i->second; }
bool is_valid(const t_ilist::const_iterator &i) const
{ return i != the_end_ && (i->unit_ != NULL); }
bool is_valid(const t_umap::const_iterator &i) const
{ return i != umap_.end() && (i->second->unit_ != NULL); }
/**
* underlying_id -> unit. This requires that underlying_id be
* underlying_id -> ilist::iterator. This requires that underlying_id be
* unique (which is enforced in unit_map::insert).
*/
umap map_;
t_umap umap_;
/**
* location -> underlying_id.
* location -> ilist::iterator.
*/
lmap lmap_;
t_lmap lmap_;
/**
* List of unit pointers and ref counts for the iterators
*/
mutable t_ilist ilist_;
t_ilist::iterator the_end_; /// Last list item
mutable size_t num_iters_;
size_t num_invalid_;

View file

@ -0,0 +1,75 @@
/* $Id: tstring.hpp 48153 2011-01-01 15:57:50Z mordante $ */
/*
Copyright (C) 2004 - 2011 by Philippe Plantier <ayin@anathas.org>
Part of the Battle for Wesnoth Project http://www.wesnoth.org
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.
See the COPYING file for more details.
*/
#ifndef UTILS_REFERENCE_COUTER_H_INCLUDED
#define UTILS_REFERENCE_COUTER_H_INCLUDED
/**
* @file
*/
#include <limits>
#include <boost/static_assert.hpp>
//debug
#include <iostream> //std::cerr
namespace n_ref_counter {
/**
@class t_ref_counter
@brief t_ref_counter is a reference counter. If the counter overflows it stops counting.
So any negative count disables reference counting.
**/
template <typename T_integral> class t_ref_counter {
BOOST_STATIC_ASSERT( std::numeric_limits<T_integral>::is_signed);
T_integral count_;
public:
enum {NEW=0, NOT_COUNTED = -1};
explicit t_ref_counter(T_integral x = 0){count_=x;}
t_ref_counter(t_ref_counter const &a){count_ = a.count_;}
t_ref_counter & operator=(t_ref_counter const a){count_ = a.count_; return *this;}
operator T_integral const () const {return count_;}
T_integral const set(T_integral const a) { count_=a; return count_; }
T_integral const inc(){
if (count_ >= 0) { count_ += 1; }
return count_; }
T_integral const dec(){
if( count_ > 0) { count_ -= 1; }
return count_; }
T_integral const enable_count(){
if (count_ < 0) {count_ = 0;}
return count_; }
T_integral const disable_count(){
count_= NOT_COUNTED;
return count_; }
T_integral const operator++(){return inc();}
T_integral const operator++(int){T_integral ret(count_); inc(); return ret;}
T_integral const operator--(){return dec();}
T_integral const operator--(int){T_integral ret(count_); dec(); return ret;}
};
}//end namepace
#endif

View file

@ -67,7 +67,7 @@ void highlight_visitor::set_mouseover_hex(const map_location& hex)
scoped_real_unit_map ensure_real_map;
mouseover_hex_ = hex;
//if we're right over a unit, just highlight all of this unit's actions
unit_map::const_iterator it = unit_map_.find(hex);
unit_map::iterator it = unit_map_.find(hex);
if (it != unit_map_.end())
{
selection_candidate_ = &(*it);

View file

@ -86,7 +86,7 @@ validate_visitor::VALIDITY validate_visitor::evaluate_move_validity(move_ptr m_p
return WORTHLESS;
//Check that the unit still exists in the source hex
unit_map::const_iterator unit_it;
unit_map::iterator unit_it;
unit_it = resources::units->find(m.get_source_hex());
if (unit_it == resources::units->end())
return WORTHLESS;