Boost C++ Libraries: Ticket #8306: named mutex does not unlock as expected https://svn.boost.org/trac10/ticket/8306 <p> <code>named_mutex</code> internally uses a <code>posix_named_semaphore</code> (in file <code>&lt;boost/interprocess/sync/posix/named_mutex.hpp&gt;</code>). When a mutex is unlocked (via <code>unlock()</code>) multiple times consecutively a single call to <code>lock()</code> will not lock the mutex! </p> <p> In the documentation it says: </p> <blockquote class="citation"> <p> void unlock() </p> <p> Precondition: The thread must have exclusive ownership of the mutex. </p> </blockquote> <p> [Boost]<br /> This precondition is violated within this scenario. But the expected precondition is that <em>no other thread must have exclusive ownership of the mutex</em>. This way a free mutex could be unlocked safely. </p> <p> The <em>expected behaviour</em> is that: </p> <blockquote class="citation"> <p> A mutex is a variable that can be in one of two states: unlocked or locked. </p> </blockquote> <p> [Tanenbaum]<br /> Thus locking an unlocked mutex would definitely lock it. To demonstrate that this expectation is not fulfilled a minimal program will be attached. </p> <p> A workaround to securely unlock a <code>named_mutex</code> could be: </p> <pre class="wiki">if(mutex.try_lock()) { mutex.unlock(); } </pre><p> This way the mutex will only be unlocked if no other thread holds ownership of the mutex. Also the mutex will not be unlocked consecutively in case this snipped is called several times as it would be locked by <code>try_lock()</code> before. After this snipped the mutex will not be locked by the calling thread, but still might be locked by an other thread. </p> <p> A real fix to this issue would probably be to use mutexes (like with <code>pthread_mutex_unlock</code>) instead of semaphores. </p> <p> [Tanenbaum] A. S. Tanenbaum, Operating systems: design and implementation, 3rd ed. Upper Saddle River, N.J: <a class="missing wiki">Pearson/Prentice</a> Hall, 2006.<br /> [Boost] ‘Synchronization mechanisms - 1.53.0’. [Online]. Available: <a href="http://www.boost.org/doc/libs/1_53_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.mutexes.mutexes_mutex_operations">http://www.boost.org/doc/libs/1_53_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.mutexes.mutexes_mutex_operations</a>. [Accessed: 18-Mar-2013]. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/8306 Trac 1.4.3 David Hebbeker <david.hebbeker@…> Mon, 18 Mar 2013 18:26:27 GMT attachment set https://svn.boost.org/trac10/ticket/8306 https://svn.boost.org/trac10/ticket/8306 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">namedMutexExample.cpp</span> </li> </ul> <p> Minimal example of using named mutexes and unlocking them several times consecutively. Linked with -lpthread. The program is expected to never return. </p> Ticket Steven Watanabe Mon, 18 Mar 2013 18:51:53 GMT <link>https://svn.boost.org/trac10/ticket/8306#comment:1 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8306#comment:1</guid> <description> <p> Violating the preconditions of a function has undefined behavior. Once you do this, you cannot rely on the object (or the program) being in a sane state. </p> </description> <category>Ticket</category> </item> <item> <author>David Hebbeker <david.hebbeker@…></author> <pubDate>Mon, 18 Mar 2013 18:58:10 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8306#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8306#comment:2</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8306#comment:1" title="Comment 1">steven_watanabe</a>: </p> <blockquote class="citation"> <p> Violating the preconditions of a function has undefined behavior. Once you do this, you cannot rely on the object (or the program) being in a sane state. </p> </blockquote> <p> Yes, I am acknowledging that this scenario would violate the preconditions defined in the documentation. My intention is that the behaviour is not as one would expect it from a mutex in general (see [Tanenbaum]). </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Steven Watanabe</dc:creator> <pubDate>Mon, 18 Mar 2013 19:23:31 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8306#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8306#comment:3</guid> <description> <p> I don't understand why you expect this behavior. Your quote from Tanenbaum is irrelevant because valid states only apply as long as you follow the rules. Once you break the rules, all bets are off. </p> <pre class="wiki">$ man pthread_mutex_unlock ... If the mutex type is PTHREAD_MUTEX_DEFAULT, ... Attempting to unlock the mutex if it was not locked by the calling thread results in undefined behavior. Attempting to ulock the mutex if it is not locked results in undefined behavior. ... </pre><p> In any case, the precondition that you propose, "no other thread must have exclusive ownership of the mutex", is basically useless. If the current thread doesn't hold the mutex, how do you know that no other thread holds it? </p> </description> <category>Ticket</category> </item> <item> <author>David Hebbeker <david.hebbeker@…></author> <pubDate>Mon, 18 Mar 2013 19:53:26 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8306#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8306#comment:4</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8306#comment:3" title="Comment 3">steven_watanabe</a>: </p> <blockquote class="citation"> <p> I don't understand why you expect this behavior. </p> </blockquote> <p> I expect this behaviour as I imagine that a mutex has some kind of state machine. I hoped that there is no error / undefined state and that an attempt to unlock in the unlocked stated would result in the unlocked state. Just that simple. </p> <blockquote class="citation"> <p> Your quote from Tanenbaum is irrelevant because valid states only apply as long as you follow the rules. Once you break the rules, all bets are off. </p> </blockquote> <p> The point is, that I wish other rules. </p> <blockquote class="citation"> <pre class="wiki">$ man pthread_mutex_unlock ... If the mutex type is PTHREAD_MUTEX_DEFAULT, ... Attempting to unlock the mutex if it was not locked by the calling thread results in undefined behavior. Attempting to ulock the mutex if it is not locked results in undefined behavior. ... </pre></blockquote> <p> What about <code>PTHREAD_MUTEX_ERRORCHECK</code>? In that case an <code>EPERM</code> error would be returned, which is better than undefined behaviour. </p> <blockquote class="citation"> <p> In any case, the precondition that you propose, "no other thread must have exclusive ownership of the mutex", is basically useless. If the current thread doesn't hold the mutex, how do you know that no other thread holds it? </p> </blockquote> <p> I am not sure if I got your question: I could use try_lock() to test if an other thread holds it. </p> <p> Also my workaround from above is not complete! Maybe this can be considered, which is still not perfect: </p> <pre class="wiki">if(mutex.try_lock()) { // The mutex was free and is now locked by this thread mutex.unlock(); } else { // The mutex is locked, try to unlock it mutex.unlock(); // Hopefully it is locked by this thread // in case an other thread holds the mutex &gt; bad things happen } </pre><p> The whole point is that I would like to be able to safely call unlock on a mutex. And the behaviour should not be undefined in case it already was unlocked. </p> <p> <em>I am not saying, that the current implementation is defect'' But that it could maybe be improved by removing the restriction applied in the precondition? </em></p> <p> By the way: Thanks for the fast responses! </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Steven Watanabe</dc:creator> <pubDate>Mon, 18 Mar 2013 21:10:34 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8306#comment:5 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8306#comment:5</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8306#comment:4" title="Comment 4">David Hebbeker &lt;david.hebbeker@…&gt;</a>: </p> <blockquote class="citation"> <p> I expect this behaviour as I imagine that a mutex has some kind of state machine. I hoped that there is no error / undefined state and that an attempt to unlock in the unlocked stated would result in the unlocked state. Just that simple. </p> </blockquote> <p> I'm afraid that isn't how mutexes work. </p> <blockquote class="citation"> <p> What about <code>PTHREAD_MUTEX_ERRORCHECK</code>? In that case an <code>EPERM</code> error would be returned, which is better than undefined behaviour. </p> </blockquote> <p> But note that it's still considered an error. It's just detected. Also, in the standard, C++11 30.4.1.2: "21 The expression m.unlock() shall be well-formed and have the following semantics. 22 Requires: The calling thread shall own the mutex." </p> <blockquote class="citation"> <blockquote class="citation"> <p> In any case, the precondition that you propose, "no other thread must have exclusive ownership of the mutex", is basically useless. If the current thread doesn't hold the mutex, how do you know that no other thread holds it? </p> </blockquote> <p> I am not sure if I got your question: I could use try_lock() to test if an other thread holds it. </p> </blockquote> <p> If you already hold the mutex, then for a non-recursive mutex, you can't call try_lock and for a recursive mutex, calling unlock after try_lock will leave you exactly where you started. </p> <p> Suppose that your semantics were implemented. Then consider the following code: </p> <pre class="wiki">Thread A: 1 - if(foo()) m.lock(); 2 - m.unlock(); Thread B: 3 - m.lock(); 4 - // do something 5 - m.unlock(); </pre><p> If B is at (4) and foo() returns false then, thread A has undefined behavior. In order to make A correct, we'd have to make m.unlock() be a no-op even if another thread holds m. It seems rather counterintuitive that m.unlock() should be legal and leave the mutex locked. </p> <blockquote class="citation"> <p> Also my workaround from above is not complete! Maybe this can be considered, which is still not perfect: </p> <pre class="wiki">if(mutex.try_lock()) { // The mutex was free and is now locked by this thread mutex.unlock(); } else { // The mutex is locked, try to unlock it mutex.unlock(); // Hopefully it is locked by this thread // in case an other thread holds the mutex &gt; bad things happen } </pre></blockquote> <p> This code has exactly the same problems as plain mutex.unlock(). </p> <blockquote class="citation"> <p> The whole point is that I would like to be able to safely call unlock on a mutex. And the behaviour should not be undefined in case it already was unlocked. </p> <p> <em>I am not saying, that the current implementation is defect'' But that it could maybe be improved by removing the restriction applied in the precondition? </em></p> </blockquote> <p> It's not going to happen. The bottom line is that you have to know whether you hold a mutex in order to do anything useful with it. I don't think that the library should go out of its way to support usage which is very dangerous and which is not supported by any other mutex implementation that I'm aware of. </p> <p> Also, I don't even want to think about the implications of your proposed behavior for recursive mutexes. </p> </description> <category>Ticket</category> </item> <item> <author>David Hebbeker <david.hebbeker@…></author> <pubDate>Tue, 19 Mar 2013 01:33:32 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/8306#comment:6 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/8306#comment:6</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8306#comment:5" title="Comment 5">steven_watanabe</a>: </p> <blockquote class="citation"> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/8306#comment:4" title="Comment 4">David Hebbeker &lt;david.hebbeker@…&gt;</a>: </p> <blockquote class="citation"> <p> What about <code>PTHREAD_MUTEX_ERRORCHECK</code>? In that case an <code>EPERM</code> error would be returned, which is better than undefined behaviour. </p> </blockquote> <p> But note that it's still considered an error. It's just detected. </p> </blockquote> <p> [...] </p> <blockquote class="citation"> <p> It's not going to happen. The bottom line is that you have to know whether you hold a mutex in order to do anything useful with it. I don't think that the library should go out of its way to support usage which is very dangerous and which is not supported by any other mutex implementation that I'm aware of. </p> </blockquote> <p> As I understand the behaviour I described is implemented by pthread mutexes of type <code>PTHREAD_MUTEX_ERRORCHECK</code>. It improves the behaviour as it may not result in an undefined state in contrast to the default mutex, but it returns an error value. The downside would be the performance overhead. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Ion Gaztañaga</dc:creator> <pubDate>Wed, 29 May 2013 07:42:30 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/8306#comment:7 https://svn.boost.org/trac10/ticket/8306#comment:7 <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">worksforme</span> </li> </ul> <p> As Steven correctly points out, violating the precondition must not be supported. std::mutex behaves also this way. ERRORCHECK mutexes are not available in al platforms and they have a perfomance impact. </p> Ticket