-1
#include <iostream>
#include <boost/thread.hpp>
#include <boost/date_time.hpp>
#include <csignal>

namespace
{
    volatile std::sig_atomic_t gSignalStatus = 1;
}

void sig_handler(int sig){
    gSignalStatus = 0;
}

boost::shared_mutex g_mutex;

using namespace std;

void reader(int id)
{
    cerr<<"reader"<<id<<"started"<<endl;
    while(gSignalStatus) {
        boost::shared_lock<boost::shared_mutex> lock(g_mutex);
        cerr << "reader"<<id << ": Got the lock" << endl;
        boost::this_thread::sleep(boost::posix_time::milliseconds(200));
    }
}

void writer(int id)
{
    cerr<<"writer"<<id<<"started"<<endl;
    while(gSignalStatus) {
        boost::upgrade_lock<boost::shared_mutex> lock(g_mutex);
        boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock);
        cout <<"writer"<< id << ": Got the lock" << endl;
        boost::this_thread::sleep(boost::posix_time::milliseconds(200));
    }
}

int main(int argc, char* argv[])
{
    std::signal(SIGINT, sig_handler);

    std::vector<boost::thread*> writerthread(1);
    std::vector<boost::thread*> readerthread(4);
    int id = 0;
    for(auto& w:writerthread) w = new boost::thread(writer, id++);

    id=0;
    for(auto& r:readerthread) r = new boost::thread(reader, id++);

    for(auto& w:writerthread){
        w->join();
        delete w;
    }
    for(auto&r:readerthread){
        r->join();
        delete r;
    }

    return 0;
}

I implemented multi readers/ single writer example.

The problem is once writer owns mutex or reader(s) own mutex, the owernership is not transferred to its opposite thread(readers->writer / writer->readers)

So the output of the program can be one of two.

When writer got the lock

writer0started
readerwriterreader0: Got the lock
readerreader21started30started
started

started
writer0: Got the lock
writer0: Got the lock
writer0: Got the lock
writer0: Got the lock
writer0: Got the lock

When reader(s) got the lock

writerreader0started
reader3startedreader
0: Got the lock
0reader2reader3: Got the lock
reader1started
reader1: Got the lock
started
started
reader2: Got the lock
reader0: Got the lock
reader3: Got the lock
reader1: Got the lock
reader2: Got the lock
reader1: Got the lock
reader2: Got the lock
reader0: Got the lock
reader3: Got the lock
readerreader3: Got the lock
reader2: Got the lock
0: Got the lock

The output was different than I expected.

What I expected was writer and reader(s) own the lock alternately.

Is this behavior is normal?

Is there any preference of locking mechanism? i.e. shared_lock is preferred than upgrade_lock.

Community
  • 1
  • 1
JaeJun LEE
  • 1,234
  • 3
  • 11
  • 27
  • _"The problem is once writer owns mutex or reader(s) own mutex, the owernership is not transferred to its opposite thread(readers->writer / writer->readers)"_ Isn't that the whole purpose of a mutex (**mut**ually **ex**clusive) synchronization mechanism is about?? Did you mean to implement a [read/write lock](http://stackoverflow.com/questions/989795/example-for-boost-shared-mutex-multiple-reads-one-write/6450576#6450576)? – πάντα ῥεῖ May 09 '16 at 16:43
  • I think that's already available from the current c+ standard BTW. No need to use boost necessarily. – πάντα ῥεῖ May 09 '16 at 16:47

1 Answers1

2

The problem is that you have tight loops which either reader or writer can't overcome easily once the other one grabbed the mutex. Look at your loops gist:

  1. Lock mutex
  2. Sleep
  3. Release mutex
  4. Go to step 1

And the window after step 3 is the only chance for the readers or writer to grab the mutex. This window is pretty short so the chance is small that it will actually grab it. That's why you see only writer or only readers printing to the console. Actually, if you wait forever you will most likely see that different entity will get its chance to work.

So how to fix it? It's pretty easy: just move sleeping out of the lock, like this:

void writer(int id)
{
    cerr << "writer" << id << "started" << endl;
    while(gSignalStatus) {
        {
            boost::upgrade_lock<boost::shared_mutex> lock(g_mutex);
            boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock);
            cout << "writer" << id << ": Got the lock" << endl;
        }
        boost::this_thread::sleep(boost::posix_time::milliseconds(200));
    }
}

void reader(int id)
{
    cerr << "reader" << id << "started" << endl;
    while(gSignalStatus) {
        {
            boost::shared_lock<boost::shared_mutex> lock(g_mutex);
            cerr << "reader" << id << ": Got the lock" << endl;
        }
        boost::this_thread::sleep(boost::posix_time::milliseconds(200));
    }
}
ixSci
  • 13,100
  • 5
  • 45
  • 79