开发者

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:

  1. The element update_hint points to is erased somewhere else in Implementation code.
  2. You don't properly initialize update_hint with a valid value at Implementation construction time.

HTH

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜