vendredi 6 octobre 2017

Is there a sort fo weak mutex concept?

Let's consider this piece of code (does not necssarily make sense, it's just a MCVE):

class Foo
{
public:
    // this function is thread safe
    void doSomething();
};

static std::mutex mutex;
static std::shared_ptr<Foo> instance;

void workerThread()
{
    while ( true )
    {
        mutex.lock();
        if ( instance )
        {
            instance->doSomething();
        }
        mutex.unlock();
        msleep( 50 );
    }
}

void messupThread()
{
    while ( true )
    {
        mutex.lock();
        instance.reset( new Foo() );
        mutex.unlock();
        msleep( 1000 );
    }
}

A workerThread do operations on the Foo instance. A messupThread modifies the instance on which operations should be done.

This code is thread safe.

Problem comes if you start 10 workerThread, when one workerThread uses the instance, it will lock the other nine one (while doSomething is thread safe, they should be able to work concurrently).

Is there a kind of weak mutex/lock mechanism that would make any workerThread prevent messupThread from changing the instance but would not prevent a concurrent workerThread to use it.

I could do this, but I'm wondering if there is an easier way to implement that using standard objects/mechanisms:

class Foo
{
public:
    // this function is thread safe
    void doSomething();
};

static int useRefCount = 0;
static std::mutex work_mutex; // to protect useRefCount concurrent access
static std::mutex mutex;      // to protect instance concurrent access
static std::shared_ptr<Foo> instance;

void workerThread()
{
    while ( true )
    {
        work_mutex.lock();
        if ( useRefCount == 0 )
            mutex.lock(); // first one to start working, prevent messupThread() to change instance
        // else, another workerThread already locked the mutex
        useRefCount++;   
        work_mutex.unlock();           

        if ( instance )
        {
            instance->doSomething();
        }

        work_mutex.lock();
        useRefCount--;
        if ( useRefCount == 0 )
            mutex.unlock(); // no more workerThread working
        //else, keep mutex locked as another workerThread is still working
        work_mutex.unlock();

        msleep( 50 );
    }
}

void messupThread()
{
    while ( true )
    {
        mutex.lock();
        instance.reset( new Foo() );
        mutex.unlock();
        msleep( 1000 );
    }
}

Aucun commentaire:

Enregistrer un commentaire