c++multithreadingmutexboost-thread

Select mutex or dummy mutex at runtime


I have a class that is shared between several projects, some uses of it are single-threaded and some are multi-threaded. The single-threaded users don't want the overhead of mutex locking, and the multi-threaded users don't want to do their own locking and want to be able to optionally run in "single-threaded mode." So I would like to be able to select between real and "dummy" mutexes at runtime.

Ideally, I would have a shared_ptr<something> and assign either a real or fake mutex object. I would then "lock" this without regard to what's in it.

unique_lock<something> guard(*mutex);
... critical section ...

Now there is a signals2::dummy_mutex but it does not share a common base class with boost::mutex.

So, what's an elegant way to select between a real mutex and a dummy mutex (either the one in signals2 or something else) without making the lock/guard code more complicated than the example above?

And, before you point out the alternatives:


Solution

  • How about something like this? Its untested but should be close to OK. You might consider making the template class hold a value rather than a pointer if your mutexes support the right kinds of constructions. Otherwise you could specialise the MyMutex class to get value behaviour.

    Also it's not being careful about copying or destruction .. I leave that as an exercise to the reader ;) ( shared_ptr or storing a value rather than a pointer should fix this)

    Oh and the code would be nicer using RAII rather than explicit lock/unlock... but that's a different question.I assume thats what the unique_lock in your code does?

    struct IMutex
    {
      virtual ~IMutex(){}
      virtual void lock()=0;
      virtual bool try_lock()=0;
      virtual void unlock()=0;
    };
    
    template<typename T>
    class MyMutex : public IMutex
    {
      public:
        MyMutex(T t) : t_(t) {}
        void lock() { t_->lock(); }
        bool try_lock() { return t_->try_lock(); }
        void unlock() { t_->unlock(); }
      protected:
        T* t_;
    };
    
    IMutex * createMutex()
    {
      if( isMultithreaded() )
      {
         return new MyMutex<boost::mutex>( new boost::mutex );
      }
      else
      {
         return new MyMutex<signal2::dummy_mutex>( new signal2::dummy_mutex );
      }
    }
    
    
    int main()
    {
       IMutex * mutex = createMutex();
       ...
       {
         unique_lock<IMutex> guard( *mutex );
         ...
       }
    
    }