Saturday, May 11, 2013

Update on recursive_mutex.


/**
 * In one of my last posts, I started looking
 * at recursive_mutex as a "clean" way to handle
 * recursive functions and locking. What I didn't
 * realize is how "frowned upon" they are- Even
 * the bad features have their appropriate uses.
 * So why so much hate for recursive_mutex?
 *
 * The first con to using a recursive_mutex is
 * the cost involved in calculating the count.
 * Every time you lock there is a count that is
 * increased on the mutex. The same thread increments
 * this use count every time it locks. However,
 * to fully unlock it also needs to call
 * unlock() the same number of times.
 *
 * The second con of using a recursive_mutex
 * is that it doesn't work well with scoped_lock,
 * which is a nice convenience class provided
 * by boost (remember the RAII implementation).
 *
 * The third reason (and the one I don't fully
 * understand) is that unlocking the resource
 * is sometimes not unlocking the resource. I
 * don't want to reference where I read this-
 * Mainly because it's from a blog that reads
 * more like a rant.
 *
 * Considering the three reasons, I started
 * playing around with some new implementations.
 * I am most concerned about the speed of using
 * the recursive mutex and second most concerned
 * about the readability. Therefore the prior
 * blogpost was re-written to use regular
 * vanilla mutexes.
 */

// This header is needed to get the awesome
// scoped_lock provided by boost.
#include <boost/thread/mutex.hpp>

// This is needed to use the conditions.
#include <boost/thread/condition.hpp>
#include <boost/thread.hpp>

#include <iostream>

// We have one mutex for output.
static boost::mutex mtx;


/**
 * Notice that the following two methods are
 * exactly the same as before, with the exception
 * that they have the underscore afterwords.
 * I hope that this would be a clue to readers
 * that this function is not to be called directly.
 * Normally you would make this innaccessable to
 * other developers by making it private.
 */
template <typename T>
void print_(T const& t)
{
std::cout << t;
}


/**
 * These methods are exact copies of the prior
 * blogpost with the exception that they do not
 * lock at all. This is recursive behavior- so
 * locking would cause deadlocks.
 */
template <typename T, typename... Parameters>
void print_(T const& t1, Parameters... parms)
{
std::cout << t1;

if (sizeof...(Parameters))
{
print_(parms...);
}

}


/**
 * Here is where the implementation design comes
 * in. Our public function that is non-recursive
 * does the locking! It then simply calls our
 * private recursive functions. This eliminates
 * the need for a recursive lock.
 */
template <typename T>
void print(T const& t)
{
boost::mutex::scoped_lock lock(mtx);
print_(t);
}


/**
 * Same comment as above.
 */
template <typename T, typename... Parameters>
void print(T const& t1, Parameters... parms)
{
boost::mutex::scoped_lock lock(mtx);
print_(t1, parms...);
}


/**
 * For right now, ignore the condition parameter. I
 * haven't quite figured out how that all works
 * yet.
 */
void runThread(int id, boost::shared_ptr<boost::condition> cond)
{
for(int i=0; i<5; ++i)
{
print("Thread #", id, " (iteration=", i, ")\n");
}
}


int main(int argc, char** argv)
{
boost::shared_ptr<boost::condition> cond(new boost::condition());

boost::thread t1(runThread, 1, cond);
boost::thread t2(runThread, 2, cond);
boost::thread t3(runThread, 3, cond);
boost::thread t4(runThread, 4, cond);

t1.join();
t2.join();
t3.join();
t4.join();
}

/**
 * In conclusion, developers are a very passionate bunch.
 * I understand and get it- I have the same vehement hatred
 * for auto_ptr (I want to rant about it but I won't).
 * I think when I see one person speak up about how bad
 * something is I try to push the criticism aside. When
 * an entire group of developers shun something I take
 * notice; I won't be using recursive_mutex's in my code
 * anytime soon!
 */

No comments:

Post a Comment