[Development] Crash in QSystemSemaphore acquire/release

Calogero Mauceri mauceri at actgate.com
Tue Mar 25 10:24:20 CET 2014


Hi,

I've been digging for days in a very weird problem I found testing a 
producer/consumer implementation using QSystemSemaphore for IPC 
communication.

I used QSystemSemaphore to synchronize the producer and the consumer 
processes. The implementation is working like a charm on Windows/Linux 
while it is crashing on Mac OS when an acquire on the semaphore is 
performed.

Looking into the Qt implementation, I found out that on unix, the system 
semaphore acquire/release is implemented calling semop with SEM_UNDO 
flag. If the semop fails a new semaphore is created and a new semop is 
performed on it and so on till there is a successful semop. The crash is 
due to an infinite recursive loop due to a permanent failure in 
modifySemaphore() call.
This is happening because on Mac OS there is a limit set to 10 for the 
maximum number of SEM_UNDO structures a process can have initialized to 
a value different than 0 (just launch from command line 'sysctl 
kern.sysv.semume' to know that value). When more than 10 semaphores have 
the SEM_UNDO initialized, then the modifySemaphore() will always fail 
causing the crash.

Attached is a simple example demonstrating the issue. A pool of 
producers threads are created. After acquire/release have been performed 
on more than 10 semaphores I get the crash on the mac.

Why the modifySemaphore, in the unix implementation, should create a new 
semaphore if the semop is failing?
Furthermore, I think calling the semop with the SEM_UNDO flag is 
basically wrong in the producer/consumer case, where a resource is 
always acquired by a process and released by another one (the SEM_UNDO 
counter for each process would be wrong, causing a bad semaphore value 
reset on process closure). The Qt API should provide a way to 
acquire/release the semaphore without the SEM_UNDO flag.

I just made a bugreport https://bugreports.qt-project.org/browse/QTBUG-37766

Thanks,
     Calogero

-- 
Calogero Mauceri
Software Engineer

Applied Coherent Technology Corporation (ACT)
www.actgate.com

-------------- next part --------------
#include <QCoreapplication>
#include <QDebug>
#include <QSystemSemaphore>
#include <QThread>


class Producer : public QThread
{
public:
    Producer(int idx)
	{
		threadIdx = idx;

        QString freeBytesSemKey = QString("FB%1").arg(idx);
        QString usedBytesSemKey = QString("UB%1").arg(idx);

        // qDebug () << "key " << idx << freeBytesSemKey << usedBytesSemKey;

		// system semaphores shared with consumer
        freeBytes = new QSystemSemaphore(freeBytesSemKey, 1, QSystemSemaphore::Create);
        usedBytes = new QSystemSemaphore(usedBytesSemKey, 0, QSystemSemaphore::Create);
	}
	~Producer()
	{
		delete freeBytes;
		delete usedBytes;
	}
	void run()
	{
        int i = 0;
		while (1)
		{
			freeBytes->acquire();
			// ...
			qDebug() << "thread" << threadIdx << " block " <<  ++i << " data produced ...";
			usedBytes->release();

            // just to emulate other stuff delay
            sleep(10);
		}
	}

private:
	int threadIdx;
	QSystemSemaphore *freeBytes;
	QSystemSemaphore *usedBytes;
};

int main(int argc, char ** argv)
{
	QCoreApplication app(argc, argv);
    const int nThreads = 10;
	QThread *producersPool[nThreads];

    // launch a pool of producers
	for (int i = 0; i < nThreads; i++)
	{
		producersPool[i] = new Producer(i);
	}
    for (int i = 0; i < nThreads; i++)
    {
        qDebug() << "Launching producer thread " << i;
        producersPool[i]->start();
    }

	// wait
	producersPool[0]->wait();

	return 0;
}


More information about the Development mailing list