c++c++11readwritelock

Upgrading read lock to write lock without releasing the first in C++11?


I know it's possible using boost::UpgradeLockable in C++14.
Is there anything similar for C++11?


Solution

  • An upgradeable lock can be written on top of simpler locking primitives.

    struct upgradeable_timed_mutex {
      void lock() {
        upgradable_lock();
        upgrade_lock();
      }
      void unlock() {
        upgrade_unlock();
        upgradable_unlock();
      }
      void shared_lock() { shared.shared_lock(); }
      void shared_unlock() { shared.shared_unlock(); }
    
      void upgradable_lock() { unshared.lock(); }
      void ungradable_unlock() { unshared.unlock(); }
    
      void upgrade_lock() { shared.lock(); }
      void upgrade_unlock() { shared.unlock(); }
    private:
      friend struct upgradable_lock;
      std::shared_timed_mutex shared;
      std::timed_mutex unshared;
    };
    

    and similar for the timed and try variants. Note that timed variants which access two mutexes in a row have to do some extra work to avoid spending up to 2x the requested time, and try_lock has to be careful about the first lock's state in case the 2nd fails.

    Then you have to write upgradable_lock, with the ability to spawn a std::unique_lock upon request.

    Naturally this is hand-written thread safety code, so it is unlikely to be correct.

    In C++1z you can write an untimed version as well (with std::shared_mutex and std::mutex).


    Less concretely, there can be exactly one upgradeable or write lock at a time. This is what the unshared mutex represents.

    So long as you hold unshared, nobody else is writing to the guarded data, so you can read from it without holding the shared mutex at all.

    When you want to upgrade, you can grab a unique lock on the shared mutex. This cannot deadlock so long as no readers try to upgrade to upgradable. This excludes readers from reading, you can write, and then you release it and return back to a read only state (where you only hold the unshared mutex).