C++ Multithreading Idioms Part 1

In the current world memory managed programs like Java, Ruby we fail to appreciate some of the idioms that languages like C/C++ provide in their language constructs. Taking advantage of the object lifetimes we can use very cool tricks to make the code more modular and efficient.

For example, let’s say we have a critical section and we want that locked down. Pretty simple to do with pthread API.

pthread_mutex_lock(&m_mutex);
...
some code
...
pthread_mutex_unlock(&m_mutex);

Pretty soon, the code gets complicated and we now see the unlocking part getting duplicated.

pthread_mutex_lock(&m_mutex);
...
if (x !=y ) {
    pthread_mutex_unlock(&m_mutex);
    return;
}
..
some code
...
pthread_mutex_unlock(&m_mutex);

Now what happens when exceptions come into the picture. There is no “finally” here. But wait, we don’t need finally because we have the stack and that is final as anything that can be. So to manage the lock and unlock we just need to take advantage of the lifetime of objects that are on stack. As any API developer you probably abstract your locking code into its own class, so you can support multiple thread libraries and OSes.

    class Mutex {
        public:
            Mutex() { pthread_mutex_init(&m_mutex, NULL); }
            ~Mutex() { pthread_mutex_destroy(&m_mutex); }

            void lock() { pthread_mutex_lock(&m_mutex); }
            void unlock() { pthread_mutex_unlock(&m_mutex); }

        private:
            pthread_mutex_t m_mutex;
    }; 
    

So with this abstracted Mutex’s lock/unlock code, we can write our “AutoLock”. The “AutoLock” will lock the mutex when it takes ownership as it comes to scope and call unlock when it goes out of scope.

    class AutoLock {
        public:
            AutoLock(Mutex *mutex) {
                if (mutex) {
                    m_mutex = mutex;
                    m_mutex->lock();
                }
            }

            ~AutoLock() {
                if(m_mutex) {
                    m_mutex->unlock();
                }
            }
        private:
        Mutex* m_mutex;
    };   
    

Now for any critical section all we have to do now is create a new AutoLock object when we need it.

AutoLock al(&mutex)
...
..
lots of code below and above
...
...

Yes, that’s it. Nothing else to do. Any “if” or “exceptions” or “return” will unlock the critical section.