mardi 13 août 2019

Strange C++11 mutex behavior

I am trying to implement a counting semaphore using binary ones.

The code logic is well known and it has been tested before using Qt's QMutex class and it is known to work properly (tested with a producer-consumer multithreaded driver). I have tried manual control of the l mutex (to lock the class), lock_guard and unique_lock objects, to no effect.

A thread calling acquire() seems to get blocked on the "block" mutex (not consistently though), even when other threads call block.unlock(). The only way to make the code work is to replace the block.lock() line with while(block.try_lock()); Then everything works properly.

#include <mutex>

class semaphore
{
private:
    int value=0;
    std::mutex l, block;

public:
    semaphore(int i=0);
    semaphore(const semaphore&)=delete;
    semaphore(const semaphore&&)=delete;
    semaphore & operator=(const semaphore&)=delete;
    semaphore & operator=(const semaphore&&)=delete;
    void acquire();
    void acquire(unsigned int i);
    void release(unsigned int i=1);
    int available();
    bool try_acquire(unsigned int i=1);
};

//---------------------------------
semaphore::semaphore(int i)
{
    value=i;
    block.lock(); // make sure that any thread trying to lock will block
}
//---------------------------------
void semaphore::acquire()
{
    l.lock();
    value--;  

    if(value <0)
     {
        l.unlock();  // release the semaphore while waiting
        while( block.try_lock());  // LINES IN QUESTION 
        //block.lock();            // LINES IN QUESTION 
     }
     else
         l.unlock();
}
//---------------------------------
void semaphore::acquire(unsigned int i)
{ 
    while(i--)
        this->acquire();
}
//---------------------------------
void semaphore::release(unsigned int i)
{
  l.lock();
  while(i--)
    {
      value++;  
      if(value <=0)
        block.unlock();
    }
  l.unlock();  
}
//---------------------------------
int semaphore::available()
{
    std::unique_lock<std::mutex> guard(l);
    return value;
}
//---------------------------------
bool semaphore::try_acquire(unsigned int i)
{
    l.lock();
    bool res=false;
    if(value>=(int)i)
    {
        value-=i;
        res=true;
    }
    l.unlock();
    return res;
}

Aucun commentaire:

Enregistrer un commentaire