
Howard Hinnant wrote:
Now consider a variation:
void read_write(rw_mutex& m) { sharable_lock<rw_mutex> read_lock(m); bool b = compute_expensve_result(); if (b) { scoped_lock<rw_mutex> write_lock(move(read_lock)); modify_state(b); } }
Assuming this compiles and attempts to work the same way upgradable did, there is either a deadlock, or a logic problem, depending upon how rw_mutex is implemented. If rw_mutex is implemented such that it will
Thank you for detailed explanation. Indeed, there is a difference in semantics of lock operations. However, this difference could be expressed with different means. I can imagine two different designs: * single template class template <typename Mutex, bool Upgradable = false> class shared_lock {/* ... */}; this design does not change current design much, but allows for easier changes in code when shared lock needs to be updated to upgradable one. * extended interface of shared_lock template <typename Mutex> class shared_lock { public: // new members only shared_lock(Mutex&, const upgradable_t&); void lock(const upgradable_t&); bool try_lock(const upgradable_t&); bool try_lock(const timespan&, const upgradable_t&); bool upgradable() const; scope_lock<Mutex> upgrade() throw (thread::non_upgradable); scope_lock<Mutex> try_upgrade() throw (thread::non_upgradable); scope_lock<Mutex> try_upgrade(const timespan&) throw (thread::non_upgradable); } here decision to lock with ability to upgrade may be deffered to point where mutex is actually locked, which does not have to be place where lock object is created (assuming that you created deffered lock). It also gives more flexibiliy in runtime. You may even create shared lock (non-deffered), then release it and lock again, this time with ability to upgrade - all in one shared_lock variable. Proposed interface does not have atomic function to transform from shared non-upgradable lock to upgradable one in order to avoid deadlocks. I'm not saying that these designs are superior to current, but maybe worth some consideration? B.