Boost multi_index_container crash in release mode
I have a program that I just changed to using a boost::multi_index_container collection. After I did that and tested my code in debug mode, I was feeling pretty good about myself.
However, then I compiled a release build with NDEBUG set, and the code crashed. Not immediately, but sometimes in single-threaded tests and often in multi-threaded tests.
The segmentation faults happen deep inside boost insert and rotate functions related to the index updates and they are happening because a node has NULL left and right pointers.
My code looks a bit like this:
struct Implementation {
typedef std::pair<uint32_t, uint32_t> update_pair_type;
struct watch {};
struct update {};
typedef boost::multi_index_container<
update_pair_type,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<
boost::multi_index::tag<watch>,
boost::multi_index::member<update_pair_type, uint32_t, &update_pair_type::first>
>,
boost::multi_index::ordered_non_unique<
boost::multi_index::tag<update>,
boost::multi_index::member<u开发者_开发问答pdate_pair_type, uint32_t, &update_pair_type::second>
>
>
> update_map_type;
typedef std::vector< update_pair_type > update_list_type;
update_map_type update_map;
update_map_type::iterator update_hint;
void register_update(uint32_t watch, uint32_t update);
void do_updates(uint32_t start, uint32_t end);
};
void Implementation::register_update(uint32_t watch, uint32_t update)
{
update_pair_type new_pair( watch_offset, update_offset );
update_hint = update_map.insert(update_hint, new_pair);
if( update_hint->second != update_offset ) {
bool replaced _unused_ = update_map.replace(update_hint, new_pair);
assert(replaced);
}
}
I knew the answer when I posted the question, but I thought I would share this for everyone's edification. I didn't find any answers when I Googled for them myself so I had to figure this out on my own.
I can see other programmers easily falling into the same trap.
The problem is with the update_hint
in my code. It is used by the next register_update
call. Normally this works really well.
BUT!
Using an insertion hint after calling replace
causes that insert hint to be invalid! For some reason it works most of the time and always seems to work in debug mode. Boost seems to use the hint unchecked in some cases in release mode compiles though, and that turns out to be the killer.
I think your self-answer is incorrect. replace
preserves the validity of update_hint
in all cases (see docs). In fact, this operation is not implemented as an erasure followed by an insertion (as suggested by @Matthieu), but more smartly than that (replacement is done in place).
Inspecting your code, it is easy to prove that, if update_hint
is a valid iterator when register_update
is invoked, it will remain valid after execution: insert
always returns a valid (and dereferenceable) iterator, and replace
does not change or invalidate update_hint
. So, the only possibility is that update_hint
is invalidated outside register_update
. Likely causes:
- The element
update_hint
points to is erased somewhere else inImplementation
code. - You don't properly initialize
update_hint
with a valid value atImplementation
construction time.
HTH
精彩评论