3
\$\begingroup\$

I was curious to implement something similar to SharedMutex concept. So far only 4 methods implemented. I decided to omit try_lock, try_lock_shared and native_handle for the sake of simplicity.

#ifndef READ_WRITE_MUTEX_H__
#define READ_WRITE_MUTEX_H__

#include <atomic>
#include <mutex>
#include <condition_variable>


struct ReadWriteMutex {

  void lock() { // block everyone
    std::unique_lock<std::mutex> lock(_mutex);

    if (_writing || _readers > 0) {
      // wait while writer and/or reader(s) have finished
      _cv_writing.wait(lock, [this] {return !_writing && _readers == 0;});
    }

    _writing = true;  // set flag to block everyone
                      // current thread continue run
  }

  void unlock() {
    std::lock_guard<std::mutex> lock(_mutex);
    _writing = false;
    _cv_writing.notify_all();
    _cv_reading.notify_all();
  }

  void lock_shared() { // block writers, allow readers
    std::unique_lock<std::mutex> lock(_mutex);
    if (_writing) { // we should wait : writer is working
      _cv_reading.wait(lock, [this] {return !_writing; });
    }

    ++_readers;
  }

  void unlock_shared() {
    std::lock_guard<std::mutex> lock(_mutex);
    if (--_readers == 0) {
      _cv_writing.notify_all();
    }

  }

private:
  mutable std::mutex _mutex;
  mutable std::condition_variable _cv_writing;
  mutable std::condition_variable _cv_reading;

  std::atomic<bool> _writing = false;
  std::atomic<int> _readers = 0;

};

#endif // READ_WRITE_MUTEX_H__

C++11 support assumed.

\$\endgroup\$
2
\$\begingroup\$

The issue here is that with any readers the writers are never going to get accesses (or are unlikely to get access), they will constantly be stuck waiting for a very quiet time.

Even when you have a writer that releases the lock you get a random chance that another writer will get the lock.

void unlock() {
  std::lock_guard<std::mutex> lock(_mutex);
  _writing = false;
  _cv_writing.notify_all();  // Just because this is first provides
                             // no guarantees that a writer will get
                             // the lock
  _cv_reading.notify_all();
}

Not sure what the best is. But if there are any writers waiting then I would pause readers (no new read locks) until the writer has got and finished his job.

But that may starve readers (especially if there are a lot of writers). So some way to configure the lock behavior to decide how it should behave.

\$\endgroup\$
  • \$\begingroup\$ I had the same comment, but interestingly neither cppreference nor boost seem to specify a specific behavior with regard to preferring readers or writers or balancing lock requests. I didn't not check the standard. It seems to be up to the user to be aware of possible starvation issues. It would be worth a look into the implementations \$\endgroup\$ – Harald Scheirich Mar 8 '18 at 3:44
2
\$\begingroup\$

Just a quick heads up about your use of leading underscores. This is discouraged because they are reserved by the standard.
You should find another way to mark variables as members (if you find it's necessary). I've seen a lot of people using trailing underscores for this. In my opinion trailing indentifiers are smarter as otherwise you will have to type whatever prefix you picked everytime you want to address a member variable (which also slows down autocomplete).

\$\endgroup\$
  • \$\begingroup\$ I no longer use them at all. There's no need for them. \$\endgroup\$ – Robinson Mar 7 '18 at 12:08
  • \$\begingroup\$ @Robinson I didn't mean to imply they are necessary but rather that if you want to use an identifier then don't use leading underscores. \$\endgroup\$ – yuri Mar 7 '18 at 12:10
  • 1
    \$\begingroup\$ @yuri Exacerbating this problem, all identifiers containing double underscores are reserved for the implementation. Thus, this program has undefined behavior, since the include guard has a double underscore as part of its name. \$\endgroup\$ – Ben Steffan Mar 7 '18 at 15:45
  • \$\begingroup\$ @BenSteffan Thanks for pointing this out. (also that's a fancy word to say "making worse", actually had to look that up) \$\endgroup\$ – yuri Mar 7 '18 at 16:04
  • \$\begingroup\$ Best to avoid underscore in C++ code. Before anybody complains I am not going to defend never user underscore (sometimes its nice). But easier to avoid all these issues. \$\endgroup\$ – Martin York Mar 7 '18 at 18:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.