I've got a monitor class similar to the monitors in Java and .NET. It can be implemented somewhat more efficient than a simple combination of mutex and condition variable. Therefore I've got a semaphore which allows to enter a critical section that has previously locked and a semaphore that is used to notify a thread which is waiting for the slow contended path. If a thread is waiting is is waiting on both semaphores.
With Win32 both semaphores are separate, on Linux I'm using a SysV semaphore set to allow sth. like WaitForMultipleObjects() on Win32 to wait for the "mutex"-part of the monitor to be unlocked as well as the notifying part to be singlalled.
In the above code I simulate that by having two semaphore sets consisting of two semaphores (not using semctl() for initialization because this isn't necessary for Linux). The thread ist spawned twice and each thread first waits for both semaphores to be signalled and signals both semaphores of the opposite threads's semaphore set. The waiting part is similar to my monitor class, but the singalling part signals both semaphores of the sets at once whereas I signal them separately when doing a notify() and when leaving the "mutex"-guarded section; therefore the separate initialization of the first set to check if this works.
But unfortunately both threads stop at the first semop and sleep forever. Can you tell me what's the mistake I made ? The code is C++20 to keep it as short as possible.
#include <thread>
#include <initializer_list>
#include <sys/types.h>
#include <sys/sem.h>
#include <sys/stat.h>
using namespace std;
int main()
{
int
setA = semget( IPC_PRIVATE, 2, S_IRUSR | S_IWUSR ),
setB = semget( IPC_PRIVATE, 2, S_IRUSR | S_IWUSR );
if( setA == -1 || setB == -1 )
return EXIT_FAILURE;
static auto xSemop = []( int set, std::initializer_list<sembuf> sems )
{
int ret;
while( (ret = ::semop( set, const_cast<sembuf *>(sems.begin()), sems.size() )) == EINTR );
return ret;
};
if( xSemop( setA, { { 0, 1, 0 } } ) || xSemop( setA, { { 1, 1, 0 } } ) )
return EXIT_FAILURE;
constexpr size_t ROUNDS = 1'000'000;
auto thr = []( int mySet, int yourSet )
{
for( size_t r = ROUNDS; r--; )
if( xSemop( mySet, { { 0, -1, 0 }, { 1, -1, 0 } } )
|| xSemop( yourSet, { { 0, 1, 0 }, { 1, 1, 0 } } ) )
terminate();
};
jthread
thrA( thr, setA, setB ),
thrB( thr, setB, setA );
}
I've got it: I've got to set IPC_NOWAIT on sembuf::sem_flg if I increment a semaphore's counter, otherwise I think the thread will wait until there's another waiter decrementing this sempahore in the set. I don't know for what this should be good for but I can live with that.