Reputation: 9
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
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