Search code examples
c++mutexraii

Initialising an anonymous mutex-lock-holding class instance in the LHS of a comma operator


Suppose I have code something like this:

#include "boost/thread/mutex.hpp"

using boost::mutex;
typedef mutex::scoped_lock lock;

mutex mut1, mut2;

void Func() {
 // ...
}

void test_raiicomma_1() {
 lock mut1_lock(mut1);
 Func();
}

void test_raiicomma_2() {
 (lock(mut1)), Func();
}

void test_raiicomma_3() {
 (lock(mut1)), (lock(mut2)), Func(); // Warning!
}

int main()
{
 test_raiicomma_1();
 test_raiicomma_2();
 test_raiicomma_3();
 return 0;
}

If the function test_raiicomma_1() were called from multiple threads, it locks a mutex to prevent any other thread also calling Func() at the same time. The mutex is locked when the variable mut1_lock is constructed, and released when it goes out of scope and is destructed.

This works perfectly fine, but as a matter of style, needing to give a name to the temporary object holding the lock irked me. The function test_raiicomma_2() attempts to avoid this, by inialising the lock object and calling the function Func() within one expression.

Is it correct that the temporary object destructor will not be called until the end of the expression, after Func() has returned? (If so, do you think it's ever worthwhile to use this idiom, or is it always clearer to declare the lock in a separate statement?)

If the function test_raiicomma_3() needs to lock two mutexes, is it correct that the mutexes will be locked in order before calling Func(), and released afterwards, but may unfortunately be released in either order?


Solution

  • Is it correct that the temporary object destructor will not be called until the end of the expression, after Func() has returned?

    It is guaranteed that both constructor and destructor are called, as they have side effects, and that destruction will only happen at the end of a full expression.

    I believe it should work

    If the function test_raiicomma_3() needs to lock two mutexes, is it correct that the mutexes will be locked in order before calling Func(), and released afterwards, but may unfortunately be released in either order?

    Comma is always evaluated left to right, and automatic variables within a scope are always destroyed in reverse order of creation, so I think it's even guaranteed they are released in the (correct) order too

    As litb points out in the comments, you need braces or your expression will be parsed as a declaration.

    (If so, do you think it's ever worthwhile to use this idiom, or is it always clearer to declare the lock in a separate statement?)

    I don't think so no, to confusing for very, very little gain... I always use very explicit locks, and very explicit scopes (often extra {} within a block), decent threadsafe code is hard enough without 'special' code, and warrants very clear code in my opinion.

    YMMW of course :)