
Howard Hinnant <hinnant <at> twcny.rr.com> writes:
On Feb 8, 2008, at 2:42 PM, Anthony Williams wrote:
Howard Hinnant <hinnant <at> twcny.rr.com> writes:
template <class L1, class L2> void lock(L1& l1, L2& l2) { while (true) { { unique_lock<L1> __u1(l1); if (l2.try_lock()) { __u1.release(); break; } } std::this_thread::yield();
snipped rest of multi-lock code.
3. The yields are absolutely necessary to get the high performance mentioned in the previous note.
I find this comment intriguing. Is this always the case, or does it depend on the specifics of how a mutex is implemented on a given platform?
Thanks for the detailed experience report.
I hope this gives you some insights, despite the lack of a clear answer to your question. Perhaps you or others can present a good explanation for this behavior regarding the yields.
I have a hypothesis about the cause for this behaviour, which is why I asked about trials with various mutexes. My hypothesis is this: if a thread finds a mutex locked (in try_lock), and then immediately does a lock() on that mutex, it will probably still find it locked, and then switch to kernel mode for a blocking wait. A yield between the try_lock() and the lock() will increase the time before the lock() call, and thus increase the chances that the mutex becomes unlocked, particularly if the lock is only held for a short time. Some mutex implementations spin in user mode before switching to kernel mode for a blocking wait, and I can even see the potential benefits of doing the equivalent of try_lock()/yield()/try_lock()/kernel_lock() internally within the mutex. Such mutexes might not benefit from the yield() in the generic lock() template. Do you still have your test code? If so, then I might run a yield/no-yield test with a CRITICAL_SECTION based implementation, a kernel Mutex based implementation, my boost 1.35 atomics/semaphore based mutex and a try/yield/try/block based mutex to see if this makes any difference. Anthony