Sign in to follow this  
scottdewald

boost::mutex design pattern

Recommended Posts

I have just started reading about the Boost Library, and I'm a little confused. Let me first preface this by asking, are users ever supposed to directly use classes in the "details" subfolders (I assume not)? If that is true, can someone please explain why the boost::mutex class does not allow users to simply lock() and unlock() without using one of the scoped lock classes? I assume it is some "nice" design pattern that I am unaware of. In my opinion, adding the unnecessary {} blocks just to use the scoped locks seems messy. What am I missing?

Share this post


Link to post
Share on other sites
The Boost page seems to be down, so can't quote them yet. However I'm pretty sure it's so that it's impossible to forget to unlock a lock (this can become hard when throwing exceptions or when not following using SESE, which very few do). It's a part of the idiom called RAII (Resources acquisition is initialization).

EDIT: About the details folder, I'm quite sure you aren't supposed to use it, however I'm not all that familar with Boost and there might be some obscure cases where it can be used. The problem is that because of the C++ compilation model, some classes/functions/variables are exposed, Boost hides them by putting them in the detail folder/namespace, it's like a private keyword for classes.

Share this post


Link to post
Share on other sites
Using the destructor automatic release of resources is a good thing, as it is reduces code errors and resource leak. Here's an example:


void myFunc()
{
Lock lock;

lock.lock();

...

lock.unlock();
}



The above works just fine. However, what happens if inside the ..., something throws? Like so:


void myFunc()
{
Lock lock;

lock.lock();

...
if (x == 9) throw (std::exception(...));
...

lock.unlock();
}



Hmm, now we have a problem, because unlock doesn't get called when the logic throws.

You could put a catch handler, but what happens if you had other resources to manage there too (memory, etc...)? You'd have to know to release all of them in different places, which could cause code duplication. Furthermore, the issue gets even more complicated if you return in the middle of the method on some cases. For example:


void myFunc()
{
Lock lock;

lock.lock();

...
if (x == 9) throw (std::exception(...));
if (x == 10) return;
...

lock.unlock();
}



Instead, have an object that is self-managing.


void myFunc()
{
AutoLock lock; // auto-acquires lock

...
if (x == 9) throw (std::exception(...));
...
}



Now the above automatically acquires and releases the lock, regardless of whether exceptions or thrown or the function returns early. That's the advantage. Obtaining the lock upon construction is the idiom known as RAII -- resource acquisition is initialization. Self-managing is generally a good thing.

Now having gone through all that, I do not have personal experience with the boost::mutex, as I use my own custom threading library, which sounds like it works similarly to boost. I hoped this helped from a conceptual point of view. :)

Share this post


Link to post
Share on other sites
Quote:
Original post by CTar
... I'm pretty sure it's so that it's impossible to forget to unlock a lock ...

You can get around this by setting the scoped lock to not initially locked.

boost::mutex::scoped_lock lock(mutex, false);
...
lock.lock();
...
lock.unlock();

That just seems messy to be to have to create another object just to lock the mutex. Why didn't they just provide public lock()/unlock() functions in the boost::mutex class?

EDIT: I see what you all are saying about RAII. But if they were going to enforce this, why allow my previous code snippet? And since they allowed that, why not just put lock()/unlock() in the mutex class and strongly suggest that thye aren't used in MOST cases?

Share this post


Link to post
Share on other sites
Quote:
Original post by scottdewald
Quote:
Original post by CTar
... I'm pretty sure it's so that it's impossible to forget to unlock a lock ...

You can get around this by setting the scoped lock to not initially locked.

boost::mutex::scoped_lock lock(mutex, false);
...
lock.lock();
...
lock.unlock();

That just seems messy to be to have to create another object just to lock the mutex. Why didn't they just provide public lock()/unlock() functions in the boost::mutex class?

EDIT: I see what you all are saying about RAII. But if they were going to enforce this, why allow my previous code snippet? And since they allowed that, why not just put lock()/unlock() in the mutex class and strongly suggest that thye aren't used in MOST cases?


Because putting them in the mutex class suggests that they are to be used commonly. They still put that functionality in because certain cases, like you said, may need to control when it's locked and unlocked. But they certainly want to discourage this, so forcing that method makes it clear to the programmer that what they are doing is not standard.

Share this post


Link to post
Share on other sites
Quote:
Original post by scottdewald
[I see what you all are saying about RAII. But if they were going to enforce this, why allow my previous code snippet? And since they allowed that, why not just put lock()/unlock() in the mutex class and strongly suggest that thye aren't used in MOST cases?


But the scoped locking characteristic if RAII is still enforced, despite your use of delayed resource acquisition. They haven't allowed you to arbitrarily acquire and release the lock, they've enforced your acquisition of the lock through the scoped locker, and when the locker goes out of scope the lock is always released. RAII triumphs once again.

See, there is still a semantic difference between the mutex lock/unlock and the scoped locker lock/unlock, even if their syntax is the same.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this