Boost C++ Libraries: Ticket #1968: Dead loop bug in multi_index https://svn.boost.org/trac10/ticket/1968 <p> The bug can be replayed in the following attachment. The program can run ok if debug using gdb and run in single steps. But in full speed running test, the program will fall in a dead loop if modify is used, maybe it will cause the change of iterator? </p> <p> The bug can be replayed on linux 2.6.25-2-686 gcc version 4.2.4 and windows vs2005. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/1968 Trac 1.4.3 Zachary_zhou <zac_zhou@…> Fri, 30 May 2008 01:34:50 GMT attachment set https://svn.boost.org/trac10/ticket/1968 https://svn.boost.org/trac10/ticket/1968 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">test.tar.gz</span> </li> </ul> <p> the bug project sample </p> Ticket Joaquín M López Muñoz Fri, 30 May 2008 16:18:41 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/1968#comment:1 https://svn.boost.org/trac10/ticket/1968#comment:1 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">invalid</span> </li> </ul> <p> Hello Zachary, I'm closing this as a non-bug, but you've touched on a very tricky issue and an explanation is in order. I've been able to reproduce the problem you describe, except that the neverending loop happens always, regardless of whether I'm in debug mode or no (so, I don't have an explanation for your differing behavior when using gdb). The problem can be reduced to the following snippet: </p> <pre class="wiki">#include &lt;boost/multi_index_container.hpp&gt; #include &lt;boost/multi_index/hashed_index.hpp&gt; #include &lt;boost/multi_index/identity.hpp&gt; struct do_nothing { template&lt;typename T&gt; void operator()(const T&amp;)const{} }; int main() { using namespace boost::multi_index; typedef multi_index_container&lt; int, indexed_by&lt;hashed_non_unique&lt;identity&lt;int&gt; &gt; &gt; &gt; hashed_container; hashed_container c; c.insert(0); c.insert(0); std::pair&lt; hashed_container::iterator, hashed_container::iterator &gt; p=c.equal_range(0); while(p.first!=p.second){ c.modify(p.first,do_nothing()); ++p.first; } } </pre><p> The program inserts two identical elements 0 into a multi_index_container with a hashed index and then iterate over them and modify them, although the modifying functor does not really do anything. Nevertheless, the loop does not end! How come? The key point to understand here is that </p> <pre class="wiki"> modify(it,mod) </pre><p> is free to rearrange the element pointed to by it even if the key has not been modified: by virtue of its internal machinery, an element in a (non-unique) hashed index gets moved right before every other equivalent element after modify(); this is legal as the only requirement is that equivalent elements remain together. So, when the program reaches the second 0 element, this is moved to the front position and the loop keeps stepping into itself ad infinitum. The same thing happens with the example you've provided. </p> <p> So, inconvenient as the behavior is I'm not labeling it as a bug since the guarantee you relied upon (unmodified elements retain their position) has never been given (and it's not easy to provide for hashed indices). What can you do to circumvent this? Three options come to mind (I continue my exposition with the snippet above, but translating to your own code should be straigthforward): </p> <ol><li>Increment the iterator before modifying the element: <pre class="wiki"> while(p.first!=p.second){ c.modify(p.first++,do_nothing()); } </pre></li></ol><p> This *happens* to work but again you're relying and undocumented behavior: a conformant implementation of B.MI could move elements after their equivalent elements and you'd be in a neverending loop again. </p> <ol start="2"><li>Use a ordered_non_unique index instead of a </li></ol><p> hashed_non_unique: ordered indices do not move equivalent elements around in a modify() operation; again, this is undocumented and you'd be playing at your own risk. </p> <ol start="3"><li>Use replace() instead of modify(): <pre class="wiki"> while(p.first!=p.second){ c.replace(p.first,0); ++p.first; } </pre></li></ol><p> replace() does guarantee that an element with an unchanged key is not moved anywhere (I've reread the docs and this guarantee is not explicitly mentioned, though, I'll update the reference accordingly). </p> <p> So, (3) is the only legal choice, but if you don't mind playing with undocumented features you can stick with the simpler (and marginally faster) option (1). </p> <p> Hope this helps, please reopen the ticket if something is not entirely clear. </p> Ticket