Boost C++ Libraries: Ticket #8730: Race condition in once_block.hpp https://svn.boost.org/trac10/ticket/8730 <p> First of all, <code>once_block_flag</code> is not thread safe. <code>status</code> variable shall be an atomic variable there. </p> <p> Another issue is <code>static boost::log::once_block_flag flag_var</code>. When previous issue will be fixed and <code>once_block_flag</code> will become thread safe, keyword <code>static</code> will do bad things in C++03. Static variable <code>foo_type foo</code> inside method in C++03 is equal to: </p> <pre class="wiki">// In global namespace bool is_foo_inited = false; char place_for_foo[sizeof(foo_type)]; // in place where a static variable is used if (!is_foo_inited) { new (place_for_foo) foo_type(some parameters); is_foo_inited = true; } return reinterpret_cast&lt;foo_type&amp;&gt;(*place_for_foo); </pre><p> Beacuse <code>is_foo_inited</code> is not atomic and does not act as a spin-lock during initialization - race conditions are possible. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/8730 Trac 1.4.3 Andrey Semashev Tue, 25 Jun 2013 16:01:08 GMT <link>https://svn.boost.org/trac10/ticket/8730#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:1</guid> <description> <p> Which implementation are you referring to? </p> <blockquote class="citation"> <p> First of all, once_block_flag is not thread safe. </p> </blockquote> <p> What makes you think so? It is POD, and all its modifications in the library are made in a thread-safe way (given that you use the multi-threaded build of the library). </p> <blockquote class="citation"> <p> keyword static will do bad things in C++03 </p> </blockquote> <p> I'm well aware of that, and that's the reason why once_block_flag is POD. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Antony Polukhin</dc:creator> <pubDate>Tue, 25 Jun 2013 19:03:30 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:2</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:1" title="Comment 1">andysem</a>: </p> <blockquote class="citation"> <p> Which implementation are you referring to? </p> <blockquote class="citation"> <p> First of all, once_block_flag is not thread safe. </p> </blockquote> <p> What makes you think so? It is POD, and all its modifications in the library are made in a thread-safe way (given that you use the multi-threaded build of the library). </p> </blockquote> <p> POD *does not* guarantee atomicity. Take a look at Boost.Atomic library and note that even bools and integers are wrapped around in classes and specific processor instructions are used to gurantee atomicity. Those instructions are *not* generated by compiler when compiler sees POD type, because they work much slower than usual ones. </p> <blockquote class="citation"> <blockquote class="citation"> <p> keyword static will do bad things in C++03 </p> </blockquote> <p> I'm well aware of that, and that's the reason why once_block_flag is POD. </p> </blockquote> <p> Even if you rewrite it to Boost.Atomic it is not safe to use it that way. Back to the example: </p> <pre class="wiki">if (!is_foo_inited) { new (place_for_foo) foo_type(some parameters); is_foo_inited = true; } </pre><p> Thread no1 checks for <code>!is_foo_inited</code>, enters the <code>if</code> block and starts to execute the placement <code>new</code>. At that time thread no2 checks for <code>!is_foo_inited</code> and enters the <code>if</code> block... That is not good, leads to memory leaks and undefined behavior! </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Andrey Semashev</dc:creator> <pubDate>Tue, 25 Jun 2013 20:28:35 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:3</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:2" title="Comment 2">apolukhin</a>: </p> <blockquote class="citation"> <p> POD *does not* guarantee atomicity. Take a look at Boost.Atomic library and note that even bools and integers are wrapped around in classes and specific processor instructions are used to gurantee atomicity. Those instructions are *not* generated by compiler when compiler sees POD type, because they work much slower than usual ones. </p> </blockquote> <p> I repeat, all modifications to the flag are done in a thread-safe manner. There is no need for explicit atomic ops in this case, because only the fast path check is done unprotected. The secondary check and all modifications are done under the mutex lock, which ensures all the necessary memory barriers. </p> <p> Do you have a particular use case that may show breakage with the current implementation of once_block? On what hardware? With references to the code, if possible. </p> <blockquote class="citation"> <p> Even if you rewrite it to Boost.Atomic it is not safe to use it that way. Back to the example: </p> <pre class="wiki">if (!is_foo_inited) { new (place_for_foo) foo_type(some parameters); is_foo_inited = true; } </pre><p> Thread no1 checks for <code>!is_foo_inited</code>, enters the <code>if</code> block and starts to execute the placement <code>new</code>. At that time thread no2 checks for <code>!is_foo_inited</code> and enters the <code>if</code> block... That is not good, leads to memory leaks and undefined behavior! </p> </blockquote> <p> That is not the code that is present in once_block.hpp/cpp, nor it is intended to be that way. I'm not using Boost.Atomic in that code for very specific reasons. Please, do not digress to problems in hypothetical code in this report. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Antony Polukhin</dc:creator> <pubDate>Tue, 25 Jun 2013 21:07:53 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:4</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:3" title="Comment 3">andysem</a>: </p> <blockquote class="citation"> <p> Do you have a particular use case that may show breakage with the current implementation of once_block? On what hardware? With references to the code, if possible. </p> </blockquote> <p> Let's take a look at <code>boost/log/utility/once_block.hpp</code>. Macro <code>BOOST_LOG_ONCE_BLOCK()</code> is defined as </p> <pre class="wiki"> BOOST_LOG_ONCE_BLOCK_INTERNAL(\ BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_once_block_flag_),\ BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_once_block_sentry_)) </pre><p> That block contains <code>BOOST_LOG_ONCE_BLOCK_INTERNAL</code> will unwrap to something like </p> <pre class="wiki"> static boost::log::once_block_flag flag_var = { boost::log::once_block_flag::uninitialized }; </pre><p> where <code>boost::log::once_block_flag</code> is a POD type. Note the <code>static</code> keyword usage. It means that it will be converted by compiler to something like this: </p> <pre class="wiki">if (!is_once_block_flag_inited) { // Variable constructed with compiler new (flag_var) boost::log::once_block_flag(boost::log::once_block_flag::uninitialized); is_once_block_flag_inited = true; } </pre><p> Now consider the situation: Thread no1 checks for !is_foo_inited, enters the if block and starts to execute the placement new. At that time thread no2 checks for !is_foo_inited and enters the if block... That is not good, leads to memory leaks and undefined behavior! </p> <p> That's only top of the iceberg. There are some more problems in <code>once_block_sentry::executed()</code>. What for is all the code inside once_block.cpp: you are using <code>boost::thread</code> but do not use <code>boost::call_once</code>? What is the reason? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Andrey Semashev</dc:creator> <pubDate>Tue, 25 Jun 2013 21:24:00 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:5</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:4" title="Comment 4">apolukhin</a>: </p> <blockquote class="citation"> <pre class="wiki"> static boost::log::once_block_flag flag_var = { boost::log::once_block_flag::uninitialized }; </pre><p> where <code>boost::log::once_block_flag</code> is a POD type. Note the <code>static</code> keyword usage. It means that it will be converted by compiler to something like this: </p> <pre class="wiki">if (!is_once_block_flag_inited) { // Variable constructed with compiler new (flag_var) boost::log::once_block_flag(boost::log::once_block_flag::uninitialized); is_once_block_flag_inited = true; } </pre></blockquote> <p> No, it won't. Since it's POD, its initialization is performed during the static initialization stage, which happens before any dynamic initialization. See 3.6.2 [basic.start.init] in the standard. </p> <blockquote class="citation"> <p> That's only top of the iceberg. There are some more problems in <code>once_block_sentry::executed()</code>. </p> </blockquote> <p> What problems in particular? </p> <blockquote class="citation"> <p> What for is all the code inside once_block.cpp: you are using <code>boost::thread</code> but do not use <code>boost::call_once</code>? What is the reason? </p> </blockquote> <p> Once blocks offer more convenient syntax and are more lightweight than boost::call_once. BTW, boost::call_once also uses static initialization of its once_flag to achieve thread safety. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Antony Polukhin</dc:creator> <pubDate>Tue, 25 Jun 2013 21:50:11 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:6 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:6</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:5" title="Comment 5">andysem</a>: </p> <blockquote class="citation"> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:4" title="Comment 4">apolukhin</a>: </p> <blockquote class="citation"> <pre class="wiki"> static boost::log::once_block_flag flag_var = { boost::log::once_block_flag::uninitialized }; </pre><p> where <code>boost::log::once_block_flag</code> is a POD type. Note the <code>static</code> keyword usage. It means that it will be converted by compiler to something like this: </p> <pre class="wiki">if (!is_once_block_flag_inited) { // Variable constructed with compiler new (flag_var) boost::log::once_block_flag(boost::log::once_block_flag::uninitialized); is_once_block_flag_inited = true; } </pre></blockquote> <p> No, it won't. Since it's POD, its initialization is performed during the static initialization stage, which happens before any dynamic initialization. See 3.6.2 [basic.start.init] in the standard. </p> </blockquote> <p> This is not correct for *local objects with static storage duration*, see 6.7 of C++03: A local object of POD type (3.9) with static storage duration initialized with constant-expressions is initialized before its block is first entered. An implementation is permitted to per-form early initialization of other local objects with static storage duration under the same conditions that an implementation is permitted to statically initialize an object with static storage duration in namespace scope (3.6.2). Otherwise such an object is initialized the first time control passes through its declaration; such an object is considered initialized upon the completion of its initialization. </p> <p> So you can not have 100% guarantee for that. </p> <blockquote class="citation"> <blockquote class="citation"> <p> That's only top of the iceberg. There are some more problems in <code>once_block_sentry::executed()</code>. </p> </blockquote> <p> What problems in particular? </p> </blockquote> <p> Did not noticed the <code>enter_once_block()</code> in it, so sorry - it is OK. </p> <blockquote class="citation"> <blockquote class="citation"> <p> What for is all the code inside once_block.cpp: you are using <code>boost::thread</code> but do not use <code>boost::call_once</code>? What is the reason? </p> </blockquote> <p> Once blocks offer more convenient syntax and are more lightweight than boost::call_once. BTW, boost::call_once also uses static initialization of its once_flag to achieve thread safety. </p> </blockquote> <p> Please show me the line where it uses local object with static storage duration. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Andrey Semashev</dc:creator> <pubDate>Wed, 26 Jun 2013 07:08:58 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:7 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:7</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:6" title="Comment 6">apolukhin</a>: </p> <blockquote class="citation"> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:5" title="Comment 5">andysem</a>: </p> <blockquote class="citation"> <p> No, it won't. Since it's POD, its initialization is performed during the static initialization stage, which happens before any dynamic initialization. See 3.6.2 [basic.start.init] in the standard. </p> </blockquote> <p> This is not correct for *local objects with static storage duration*, see 6.7 of C++03: </p> <p> So you can not have 100% guarantee for that. </p> </blockquote> <p> Ok, you have a point. But note that once_block_flag::uninitialized is 0 and zero initialization for local static objects is still performed before any other initialization. Most compilers will simply ignore the initializer for this reason. But I guess, it can be just removed from the macro definition to avoid the confusion and guarantee the correct behavior. </p> <blockquote class="citation"> <blockquote class="citation"> <p> Once blocks offer more convenient syntax and are more lightweight than boost::call_once. BTW, boost::call_once also uses static initialization of its once_flag to achieve thread safety. </p> </blockquote> <p> Please show me the line where it uses local object with static storage duration. </p> </blockquote> <p> I was referring to the once_flag usage in the user's code (as a function-local static). But I guess since it is not guaranteed to be initialized statically (e.g. if BOOST_ONCE_INIT is not equivalent to zero initialization or the compiler is not smart enough to initialize the flag statically), it cannot be used that way. </p> <p> To be honest, I have yet to see the compiler that does not perform static initialization for local static PODs, even with non-zero values. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Antony Polukhin</dc:creator> <pubDate>Wed, 26 Jun 2013 08:44:04 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:8 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:8</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:7" title="Comment 7">andysem</a>: </p> <blockquote class="citation"> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:6" title="Comment 6">apolukhin</a>: </p> <blockquote class="citation"> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:5" title="Comment 5">andysem</a>: </p> <blockquote class="citation"> <p> No, it won't. Since it's POD, its initialization is performed during the static initialization stage, which happens before any dynamic initialization. See 3.6.2 [basic.start.init] in the standard. </p> </blockquote> <p> This is not correct for *local objects with static storage duration*, see 6.7 of C++03: </p> <p> So you can not have 100% guarantee for that. </p> </blockquote> <p> Ok, you have a point. But note that once_block_flag::uninitialized is 0 and zero initialization for local static objects is still performed before any other initialization. Most compilers will simply ignore the initializer for this reason. But I guess, it can be just removed from the macro definition to avoid the confusion and guarantee the correct behavior. </p> </blockquote> <p> OK, you've convinced me. </p> <p> May I ask a few more questions. Why there is so many things 'reinvented' in Boost.Log? Foe example I can see light_rw_mutex.cpp, thread_safe_queue.cpp, thread_id.cpp, thread_specific.cpp and others... They are all already implemented in Boost and tested for a long time. Why don't you use them? </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Andrey Semashev</dc:creator> <pubDate>Wed, 26 Jun 2013 09:26:18 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:9 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:9</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:8" title="Comment 8">apolukhin</a>: </p> <blockquote class="citation"> <p> May I ask a few more questions. Why there is so many things 'reinvented' in Boost.Log? Foe example I can see light_rw_mutex.cpp, thread_safe_queue.cpp, thread_id.cpp, thread_specific.cpp and others... They are all already implemented in Boost and tested for a long time. Why don't you use them? </p> </blockquote> <p> Because for various reasons these implementations do not fit the pressing requirements of the library. </p> <p> light_rw_mutex is more lightweight in terms of compile times and probably run times (I haven't benchmarked it thoroughly) than shared_mutex. </p> <p> Unlike thread::id, thread id attribute provides ids equivalent to those you would typically see in debugger or system monitoring tools (i.e. some notion of a system thread ids), which simplifies greatly debugging. </p> <p> TLS, as it is implemented in Boost.Log, guarantees maximum performance the underlying system provides (unlike Boost.Thread implementation which provided O(N) complexity of every access to the thread specific value at some point; I think it's O(log(N)) now, but it is still worse than the typical O(1) of the system TLS). </p> <p> There is no direct counterpart for thread_safe_queue, aside from the recently added Boost.Lockfree. Even then, Boost.Lockfree containers have restrictions on the element types that are not compatible with Boost.Log. It is possible to change the implementation to employ Boost.Lockfree, but the resulting code will be less efficient and also it adds an unnecessary dependency, so I don't think this is reasonable. </p> <p> I did propose some of these custom implementations to the respective libraries but there was little interest. I may try again in the future though. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Antony Polukhin</dc:creator> <pubDate>Wed, 26 Jun 2013 09:44:44 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:10 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:10</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:9" title="Comment 9">andysem</a>: </p> <blockquote class="citation"> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8730#comment:8" title="Comment 8">apolukhin</a>: </p> <blockquote class="citation"> <p> May I ask a few more questions. Why there is so many things 'reinvented' in Boost.Log? Foe example I can see light_rw_mutex.cpp, thread_safe_queue.cpp, thread_id.cpp, thread_specific.cpp and others... They are all already implemented in Boost and tested for a long time. Why don't you use them? </p> </blockquote> <p> Because for various reasons these implementations do not fit the pressing requirements of the library. </p> <p> light_rw_mutex is more lightweight in terms of compile times and probably run times (I haven't benchmarked it thoroughly) than shared_mutex. </p> <p> Unlike thread::id, thread id attribute provides ids equivalent to those you would typically see in debugger or system monitoring tools (i.e. some notion of a system thread ids), which simplifies greatly debugging. </p> <p> TLS, as it is implemented in Boost.Log, guarantees maximum performance the underlying system provides (unlike Boost.Thread implementation which provided O(N) complexity of every access to the thread specific value at some point; I think it's O(log(N)) now, but it is still worse than the typical O(1) of the system TLS). </p> <p> I did propose some of these custom implementations to the respective libraries but there was little interest. I may try again in the future though. </p> </blockquote> <p> All the changes sound good. Why don't you cooperate with Vicente Botet (current maintainer of Boost.Thread) and propose your changes to him? I don not really wish to see two different thread libraries in Boost - they make resulting users code bigger and require more effort for maintaining. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Andrey Semashev</dc:creator> <pubDate>Sun, 30 Jun 2013 13:29:26 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8730#comment:11 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8730#comment:11</guid> <description> <p> (In <a class="changeset" href="https://svn.boost.org/trac10/changeset/84918" title="Refs #8730. The once_flag doesn't have initializer now when defined by ...">[84918]</a>) Refs <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/8730" title="#8730: Bugs: Race condition in once_block.hpp (closed: fixed)">#8730</a>. The once_flag doesn't have initializer now when defined by the BOOST_LOG_ONCE_BLOCK macro. This is done in order to guarantee it is initialized at static initialization stage even on (theoretical) compilers that would not do so if the initializer is present. The BOOST_LOG_ONCE_BLOCK_FLAG macro has been fixed so that it actually uses the flag variable specified in its argument. The flag itself is now a single byte internally in order to minimize the chance of reading an inaccurate value at the fast path. The test robustness improved. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Andrey Semashev</dc:creator> <pubDate>Sat, 20 Jul 2013 18:09:34 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/8730#comment:12 https://svn.boost.org/trac10/ticket/8730#comment:12 <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">fixed</span> </li> </ul> <p> (In <a class="changeset" href="https://svn.boost.org/trac10/changeset/85093" title="Merged recent changes from trunk. Fixes #8730.">[85093]</a>) Merged recent changes from trunk. Fixes <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/8730" title="#8730: Bugs: Race condition in once_block.hpp (closed: fixed)">#8730</a>. </p> Ticket