PDA

View Full Version : QMutex is not working in release mode



bitChanger
24th April 2007, 20:29
Qt 4.2.2 and MSVC 2003

I have a class that inherits QThread with a buffer, QMutex, and QWaitCondition for data members.

The run function of the thread does a mutex.lock() and then sends whatever is in the buffer over TCP and then does a wcond.wait(&mutex).

Then whenever the classes event function receives an event it does a mutex.lock() fills the buffer and calls mutex.unlock() wcond.wakeall().

This works great in debug mode, but not in release mode.

I seem to have narrowed it down to whether the system linking option is on console or window. When on console the application works as expected. When you turn off the console by selecting window, the application seems to ignore the QMutex in that if the run function is sending the buffer the event will overwrite the buffer at the same time like they both have the mutex.

I've tried with and without optimization but it's all dependent on that linker system option.

Let me know if you need more information in order to guide me in the right direction. Thanks.

Bitto
24th April 2007, 21:06
The order of the calls is important. Especially releasing the mutex before waking the wait condition - if other things happen in the mean time you could end up in trouble.

The difference in release and debug mode could just be a sign that you've got a race condition. QMutex's behavior is the same in debug and release mode.

Could you post some sample code?

wysota
24th April 2007, 21:30
I guess it could help to swap the order of unlock() and wakeAll(), but as Andreas said, seeing the complete code of the section would help.

bitChanger
25th April 2007, 14:32
#include <QtGui>
#include <iostream>
#include "controlThread.h"
#include "controlEvent.h"

QMutex mutex;
QWaitCondition cond;

using namespace std;

ControlThread::ControlThread(QObject* parent)
: QThread(parent),
_quit(false),
_sendLen(0),
_connected(false)
{
for (int i=0; i<MAX_SEND_BUFS; ++i)
_sendBuf[i] = new char[64];
}

ControlThread::~ControlThread()
{
for (int i=0; i<MAX_SEND_BUFS; ++i)
delete [] _sendBuf[i];

_quit = true;
cond.wakeAll();
wait();
}

void ControlThread::establishConnection()
{
mutex.lock();
_connected = false;
mutex.unlock();

printf("Control Thread: connection attempt\n");
emit sigDataConnected(false);

// clean up socket descriptor before reconnect
if (_socketFD)
{
perror("Conrol Thread: warning: socket deletion\n");
closesocket(_socketFD);
}

// create socket
while ((_socketFD = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))
== INVALID_SOCKET)
{
perror("Control Thread: error: socket creation error\n");
msleep(500);
continue;
}

// connect socket
while ((::connect(_socketFD, (struct sockaddr*) &_tcpAddr,
sizeof(struct sockaddr))) == SOCKET_ERROR &&
(WSAGetLastError() != WSAEISCONN))
{
perror("Control Thread: error: connection timed out\n");
msleep(500);
continue;
}

printf("Control Thread: connection successful\n");

mutex.lock();
_connected = true;
mutex.unlock();
}

void ControlThread::run()
{
// init socket lib
_socketFD = 0;
WSADATA wsaData;
while ((WSAStartup(MAKEWORD(1, 1), &wsaData)) != 0)
{
perror("Control Thread: error: socket lib init failed\n");
msleep(100);
}

_tcpAddr.sin_family = AF_INET;
_tcpAddr.sin_port = ::htons(CONTROL_PORT);
_tcpAddr.sin_addr.s_addr = ::htonl(CONTROLLER_IP);

establishConnection();

// create timeout variable
timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 200000;
mutex.lock();

while (!_quit)
{
// send the commands and verify
for (int i=0; i<_sendLen; ++i)
{
if ((sendToSocket(_sendBufLen[i], _sendBuf[i])) == 1)
{
perror("Control Thread: error: failed to send\n");
mutex.unlock();
establishConnection();
mutex.lock();
}
}

cond.wait(&mutex);
}

// cleanup
closesocket(_socketFD);
WSACleanup();
}

int ControlThread::sendToSocket(int len, char *buf)
{
int s = 0;
if (len > 0 && len != (s = ::send(_socketFD, buf, len, 0)))
return 1;
return 0;
}

bool ControlThread::event(QEvent* evt)
{
if (evt->type() == (QEvent::Type) CONTROLID)
{
ControlEvent* cte = static_cast<ControlEvent*>(evt);
bool keepTrying = true;

QMutexLocker m(&mutex);

_sendLen = 0;
char* tmp = 0;

// check for position update
float pos[3] = {0.0f, 0.0f, 0.0f};
if (cte->getPositions(pos[0], pos[1], pos[2]))
{
_sendBufLen[_sendLen] = 0;
cout << "Position ";
tmp = _stream.PackData(AEZ, sizeof(float)*3, pos,_sendBufLen[_sendLen]);
memcpy(_sendBuf[_sendLen], tmp, _sendBufLen[_sendLen]);
_sendLen++;
}

// check for timing dialog updates
unsigned char timingCode[32];
unsigned int timingVal[32];
int len = 0;
if (cte->getTimingUpdate(timingCode, timingVal, len))
{
cout << "timing update";
for (int i=0; i<len; ++i)
{
_sendBufLen[_sendLen] = 0;
tmp = _stream.PackData(timingCode[i], 4, timingVal+i, _sendBufLen[_sendLen]);
memcpy(_sendBuf[_sendLen], tmp, _sendBufLen[_sendLen]);
_sendLen++;
}
}

cout << " sent\n";

if (!isRunning())
start();
else
cond.wakeAll();

// accept this event
evt->accept();
return true;
}
return QThread::event(evt);
}