This deadlocks on Linux but not on Windows

I would like it to deadlock on windows, because I just wrote a large header file, which assumes that a deadlock will occur. In fact, I don't know how I am going to program mutex's and locks, if they act like recursive locks on windows, but scoped locks on linux. #include <boost/thread/thread.hpp> #include <iostream> using namespace std; void hello() { cerr << "this should deadlock" << endl; boost::mutex foo; boost::mutex::scoped_lock lock1(foo); boost::mutex::scoped_lock lock2(foo); cerr << "if you see this, then there was no deadlock" << endl; } int main(int argc, char* argv[]) { boost::thread thrd(&hello); thrd.join(); return 0; } --------------------------------- Never miss a thing. Make Yahoo your homepage.

On 07/02/2008, Clark Sims <clark_sims_boost@yahoo.com> wrote:
I would like it to deadlock on windows, because I just wrote a large header file, which assumes that a deadlock will occur. In fact, I don't know how I am going to program mutex's and locks, if they act like recursive locks on windows, but scoped locks on linux.
We've noticed this variation behaviour in the past when using boost::mutex. Under the hood on Windows it uses a critical section which I believe is recursive. Not sure if the rewritten version of Boost.Threads will do the same. Sorry, but all I can do is confirm the behaviour. I believe the Boost.Threads concept documentation for mutex and scoped_lock say that attempting a recursive lock gives "undefined behaviour" rather than definitely deadlocking. Regards, Jos

Jos Hickson wrote:
On 07/02/2008, Clark Sims <clark_sims_boost@yahoo.com> wrote:
I would like it to deadlock on windows, because I just wrote a large header file, which assumes that a deadlock will occur. In fact, I don't know how I am going to program mutex's and locks, if they act like recursive locks on windows, but scoped locks on linux.
We've noticed this variation behaviour in the past when using boost::mutex. Under the hood on Windows it uses a critical section which I believe is recursive. Not sure if the rewritten version of Boost.Threads will do the same.
Yes, critical sections are recursive. I looked a bit at this: http://rafb.net/p/u0LDDP45.html Did not look into too much detail, *but* you can definitely make it work even in the presence of recursive mutexes. Best, John
Sorry, but all I can do is confirm the behaviour. I believe the Boost.Threads concept documentation for mutex and scoped_lock say that attempting a recursive lock gives "undefined behaviour" rather than definitely deadlocking.
Regards,
Jos _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
-- http://John.Torjo.com -- C++ expert http://blog.torjo.com ... call me only if you want things done right

Clark Sims wrote:
I would like it to deadlock on windows, because I just wrote a large header file, which assumes that a deadlock will occur. In fact, I don't know how I am going to program mutex's and locks, if they act like recursive locks on windows, but scoped locks on linux.
boost::mutex foo; boost::mutex::scoped_lock lock1(foo); boost::mutex::scoped_lock lock2(foo);
Boost.Threads provides recursive_mutex, which is recursive, and mutex, which has unspecified behaviour if locked multiple times by the same thread. It looks as if this unspecified behaviour is actually different on those two platforms. If you want consistent behaviour you need to use recursive_mutex. Phil.

On Feb 6, 2008 9:22 PM, Clark Sims <clark_sims_boost@yahoo.com> wrote:
I would like it to deadlock on windows,
As others have said, the windows critical section is recursive (and very lightweight). If you need non-recursive behavior, try CreateMutex(). http://msdn2.microsoft.com/en-us/library/ms682411(VS.85).aspx Check out the wxMutex impl in wxWidgets for one example (not great), which I *think* uses this call. If you extract the src, you can find it in: wxWidgets-2.8.6/src/msw/thread.cpp Jon

Jonathan Franklin <franklin.jonathan <at> gmail.com> writes:
As others have said, the windows critical section is recursive (and very lightweight). If you need non-recursive behavior, try CreateMutex(). http://msdn2.microsoft.com/en-us/library/ms682411(VS.85).aspx
CreateMutex also creates a recursive mutex. As far as I know there is no such thing as a non-recursive mutex/critical section in Windows. This is kind of a pain when writing cross-platform code. What we do is just make sure we run all our tests on Linux as well as Windows, on the basis that if the code works with non-recursive mutexes it'll almost certainly work with recursive ones as well. The reverse is generally not true of course. Using boost::recursive_mutex is an option that I'd recommend only if you actually make use of the recursive behaviour. Incidentally the Boost.Thread documentation mentions a "Checked Locking Strategy" which would throw an exception if a thread attempts to lock a mutex that it has already locked. But it then goes on to say that this strategy is not implemented. Not sure if there are plans to do this in the future? Colin

Clark Sims <clark_sims_boost <at> yahoo.com> writes:
I would like it to deadlock on windows, because I just wrote a large header
file, which assumes that a
deadlock will occur. In fact, I don't know how I am going to program mutex's and locks, if they act like recursive locks on windows, but scoped locks on linux.
The new boost 1.35 implementation of mutexes on Windows are not recursive, and will also deadlock on this code. However, as others have said, there is no sensible use for code that deadlocks --- once you have a deadlock, your code is dead. It is an implementation detail how the thread library behaves when you are trying to lock a non-recursive mutex recursively. It is within the scope of POSIX threads for an attempt to lock a mutex like this to return an error code rather than deadlock, which will trigger the BOOST_ASSERT in the mutex implementation. Anthony

On 08/02/2008, Anthony Williams <anthony_w.geo@yahoo.com> wrote:
Clark Sims <clark_sims_boost <at> yahoo.com> writes:
I would like it to deadlock on windows, because I just wrote a large header
file, which assumes that a
deadlock will occur. In fact, I don't know how I am going to program mutex's and locks, if they act like recursive locks on windows, but scoped locks on linux.
The new boost 1.35 implementation of mutexes on Windows are not recursive, and will also deadlock on this code. However, as others have said, there is no sensible use for code that deadlocks --- once you have a deadlock, your code is dead.
There may be no use for code that deadlocks but it is very useful when doing cross-platform development if the behaviour of mutexes are the same especially if the primary development platform is Windows so that you only then see the deadlocks when you switch to Linux. Admittedly not writing code that can potentially lock recursively is better and not sharing data across threads even better still as I have subsequently learnt. Jos
It is an implementation detail how the thread library behaves when you are trying to lock a non-recursive mutex recursively. It is within the scope of POSIX threads for an attempt to lock a mutex like this to return an error code rather than deadlock, which will trigger the BOOST_ASSERT in the mutex implementation.
Anthony
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Friday 08 February 2008 21:33, Jos Hickson wrote:
On 08/02/2008, Anthony Williams <anthony_w.geo@yahoo.com> wrote:
Clark Sims <clark_sims_boost <at> yahoo.com> writes:
I would like it to deadlock on windows, because I just wrote a large header
file, which assumes that a
deadlock will occur. In fact, I don't know how I am going to program mutex's and locks, if they act like recursive locks on windows, but scoped locks on linux.
The new boost 1.35 implementation of mutexes on Windows are not recursive, and will also deadlock on this code.
How so? And is there a rationale for that?
However, as others have said, there is no sensible use for code that deadlocks --- once you have a deadlock, your code is dead.
There may be no use for code that deadlocks but it is very useful when doing cross-platform development if the behaviour of mutexes are the same especially if the primary development platform is Windows so that you only then see the deadlocks when you switch to Linux.
I'm currently working on a codebase that is way too multithreaded and not really correctly designed. One idea I came across there is that Boost's threading library could offer a diagnostic mode, just like STLport's debug mode with checked iterators. This could be used to detect undefined behaviour, like e.g. locking a non-recursive mutex recursively or maybe even to detect deadlocks (How I wish there was something like that!) in a running program.
It is an implementation detail how the thread library behaves when you are trying to lock a non-recursive mutex recursively.
Just wondering, but isn't it rather called 'undefined behaviour'? I mean the distinction is important, because invoking UB is definitely bad, while IDB is bearable and just a problem of portability. Uli

Ulrich Eckhardt <doomster <at> knuut.de> writes:
On Friday 08 February 2008 21:33, Jos Hickson wrote:
On 08/02/2008, Anthony Williams <anthony_w.geo <at> yahoo.com> wrote:
Clark Sims <clark_sims_boost <at> yahoo.com> writes:
I would like it to deadlock on windows, because I just wrote a large header
file, which assumes that a
deadlock will occur. In fact, I don't know how I am going to program mutex's and locks, if they act like recursive locks on windows, but scoped locks on linux.
The new boost 1.35 implementation of mutexes on Windows are not recursive, and will also deadlock on this code.
How so? And is there a rationale for that?
Windows native Mutex objects are very expensive to use (as they are Kernel objects), and CRITICAL_SECTIONs have their own problems (such as lock hand-off and the lack of a timed_lock facility). The boost 1.35 implementation uses InterlockedXXX operations to try and avoid waiting on a kernel object, and an Event object for blocking waits. In my tests, this exhibits greater fairness, and runs faster on average than either Mutex or CRITICAL_SECTION based implementations. See http://www.justsoftwaresolutions.co.uk/articles/implementing_mutexes.html for the article I wrote for Overload describing the implementation and rationale.
However, as others have said, there is no sensible use for code that deadlocks --- once you have a deadlock, your code is dead.
There may be no use for code that deadlocks but it is very useful when doing cross-platform development if the behaviour of mutexes are the same especially if the primary development platform is Windows so that you only then see the deadlocks when you switch to Linux.
I'm currently working on a codebase that is way too multithreaded and not really correctly designed. One idea I came across there is that Boost's threading library could offer a diagnostic mode, just like STLport's debug mode with checked iterators. This could be used to detect undefined behaviour, like e.g. locking a non-recursive mutex recursively or maybe even to detect deadlocks (How I wish there was something like that!) in a running program.
I have some proof-of-concept code for a mutex that will detect deadlocks and other undefined behaviour, but it's not production-ready yet.
It is an implementation detail how the thread library behaves when you are trying to lock a non-recursive mutex recursively.
Just wondering, but isn't it rather called 'undefined behaviour'? I mean the distinction is important, because invoking UB is definitely bad, while IDB is bearable and just a problem of portability.
Yes, it's undefined behaviour. Quite how that manifests is an implementation detail. Anthony -- Anthony Williams Just Software Solutions Ltd - http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL

Anthony Williams wrote:
Windows native Mutex objects are very expensive to use (as they are Kernel objects), and CRITICAL_SECTIONs have their own problems (such as lock hand-off and the lack of a timed_lock facility). The boost 1.35 implementation uses InterlockedXXX operations to try and avoid waiting on a kernel object, and an Event object for blocking waits. In my tests, this exhibits greater fairness, and runs faster on average than either Mutex or CRITICAL_SECTION based implementations. See http://www.justsoftwaresolutions.co.uk/articles/implementing_mutexes.html for the article I wrote for Overload describing the implementation and rationale.
Thanks for the link to the interesting article. What you end up with looks remarkably similar to how my futex-based mutex works. The main difference is that my counter saturates at 2, which makes things a bit simpler for me. With a bit of tweaking it could fit in to the Mutex<Futex>, Mutex<Yield>, Mutex<Spin> and Mutex<EvilSpinThenYield> scheme that I have been thinking about: struct WinEvent_WaitWake { // psuedo-code WinEvent_WaitWake(int& val) {} // val can be ignored void wait(int expected) { WaitForSingleEvent(); } // expected can be ignored void wake(int n_to_wake) { assert(n_to_wake==1); SetEvent(); } }; template <typename FUT_T = WinEvent_WaitWake> class Mutex: boost::noncopyable { int state; // 0=unlocked, 1=locked no waiters, 2=locked with waiters FUT_T fut; public: Mutex(): state(0), fut(state) {} ~Mutex() {} // Lock, waiting if necessary. inline void lock() { if (!try_lock()) { lock_body(); } } // Try to lock, but don't wait if it's not possible. // Return indicating whether lock is now held. inline bool try_lock() { return atomic_compare_and_swap(state,0,1); } inline void unlock() { if (atomic_post_dec(state) != 1) { unlock_body(); } } private: void lock_body() { while (atomic_swap(state,2) != 0) { // worry about a concurrent unlock() <HERE> fut.wait(2); // expected=2 means "return immediately if state!=2"; // this is needed for the futex implementation as it will drop // any wakes that were sent <HERE>. But with the win32 // implementation those wakes have been stored and will be // applied to this wait (IIUC), so the "expect" feature is // not needed. } } void unlock_body() { atomic_write(state,0); fut.wake(1); } }; Regards, Phil.
participants (8)
-
Anthony Williams
-
Clark Sims
-
Colin Caughie
-
John Torjo
-
Jonathan Franklin
-
Jos Hickson
-
Phil Endecott
-
Ulrich Eckhardt