Gerassimos Barlas
Gerassimos Barlas

Reputation: 9

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.

The code works if I replace the block mutex with a condition variable.

#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;
}

Upvotes: 0

Views: 124

Answers (1)

user1143634
user1143634

Reputation:

Undefined behaviors:

  • try_lock cannot be called by a thread that already owns the mutex, otherwise, the behavior is undefined. Line while( block.try_lock()); is incorrect.
  • unlock can only be called by a thread that owns the mutex, otherwise, the behavior is undefined.

The #2 is also wrong for QtMutex. If the code was "working", it did so by an accident.

You need a proper semaphore implementation, something that is partially covered here.

Upvotes: 1

Related Questions