PDA

View Full Version : Strange problem with QThread



akaWolf
30th May 2013, 12:51
Hello!

I'm writing a small program in Qt 5.0 under Windows 7 with msvc2010 toolchain. But in the process of writing I met a rather problem: Exception. Access violation - code c0000005.
Created a test project that enclose below.
The problem as follows:
I'm creating a "thread", in which a "test" is being perfomed. "Test" creates another "thread", which runs in an endless cycle of reading. In the test I create CCANReceiver, which begins to read data from the device, and then send the data. I wait for 5 seconds and close the thread with CCANReceiver. But when trying to call StopReceiving () function while calling IsRunning () I get the exception. Empirically I found that if a function ReadDMADataOneBlock
ReadDMADataOneBlock () - function of libraries provided by me to work with the device. The source codes are available.
Source code in attachment. I do not include the library, because without the device driver and it will not work, but I can put the source code.

Detailed error information:

(d08.d68): Access violation - code c0000005 (first chance)
s
sException at 0x6704c87c, code: 0xc0000005: write access violation at: 0x1, flags=0x0 (first chance) at q:\qt5_workdir\w\s\qtbase\src\corelib\arch\qatomic _msvc.h:351
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00000000 ecx=00000001 edx=00000040 esi=00000007 edi=01d6dfc4
eip=6704c87c esp=01d6d444 ebp=01d6d444 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
Qt5Cored!QAtomicOps<QMutexData *>::testAndSetRelaxed+0xc:
3516704c87c f00fb10a lock cmpxchg dword ptr [edx],ecx ds:0023:00000040=????????

Call stack at the moment of program down:

0 QAtomicOps<QMutexData *>::testAndSetRelaxed qatomic_msvc.h 351 0x6704c87c
1 QGenericAtomicOps<QAtomicOps<QMutexData *> >::testAndSetAcquire<QMutexData *,QMutexData *> qgenericatomic.h 150 0x6704a8a5
2 QBasicAtomicPointer<QMutexData>::testAndSetAcquire qbasicatomic.h 229 0x67048e08
3 QBasicMutex::fastTryLock qmutex.h 87 0x670486f7
4 QMutex::lock qmutex.cpp 212 0x67069e11
5 QMutexLocker::QMutexLocker qmutex.h 133 0x670487e8
6 QThread::isRunning qthread.cpp 448 0x67070243
7 CCANReceiver::StopReceiving ccanreceiver.cpp 45 0x41c5e3

I do not understand how this could happen.
Debugging, I found out that this is most likely due to the fact that QScopedPointer >::data returns a value of 0.
Call stack:

0 QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> >::data qscopedpointer.h 135 0x67041d4c
1 qGetPtrHelper<QScopedPointer<QObjectData,QScopedPointerDeleter<QObjectData> > > qglobal.h 991 0x670581ab
2 QThread::d_func qthread.h 136 0x670573d3
3 QThread::isRunning qthread.cpp 446 0x67070231
4 CCANReceiver::StopReceiving ccanreceiver.cpp 44 0x420423
5 CTest0::OnTimerTimeOut ctest0.cpp 324 0x41580e

Some source code:
ctest0.h

#ifndef CTEST0_H
#define CTEST0_H

#include "itest.h"
#include "CANDeviceEx.h"
#include "ccanreceiver.h"
#include <QTimer>

class CTest0 : public ITest
{
Q_OBJECT
public:
CTest0(ILogger* _Logger);
virtual void SetFeature(quint64 NumFeat, quint64 ValFeat);
public slots:
virtual void Run();
void OnTimerTimeOut();
public:
virtual QString GetName() const;
virtual ~CTest0();

private:
CCANDeviceEx CANDevice;
CCANReceiver *CANReceiver;

quint32 sendbuff[3];
QTimer* Timer;

quint32 mode1, mode2, sendchannel, bufnum;
quint8 brp1, sjw1, sam1, btlmode1, phseg11, phseg21, prseg1, wakfil1;
quint8 brp2, sjw2, sam2, btlmode2, phseg12, phseg22, prseg2, wakfil2;
};

#endif // CTEST0_H
ctest0.cpp

#include "ctest0.h"

CTest0::CTest0(ILogger* _Logger) :
ITest(_Logger),
CANReceiver(NULL),
Timer(NULL)
{
}

void CTest0::Run()
{
quint32 tmp = 0;
quint32 tmpmode = 0;
sendbuff[0] = 0x01020304;
sendbuff[1] = 0x05060708;
sendbuff[2] = 0x090A0B0C;

if (CANReceiver != NULL)
{
delete CANReceiver;
CANReceiver = NULL;
}

if (Timer != NULL)
{
delete Timer;
Timer = NULL;
}

if (CANDevice.IsOpen())
CANDevice.Close();

Timer = new QTimer;

QObject::connect(Timer, SIGNAL(timeout()), /*qobject_cast<CTest0*>*/(this), SLOT(OnTimerTimeOut()));
QObject::connect(Timer, SIGNAL(qr()), this, SLOT(OnTimerTimeOut()));
Timer->setInterval(5000);
Timer->setSingleShot(true);

/* ... */

Logger->AddToLog("Trying to receive data");

CANReceiver = new CCANReceiver(&CANDevice);
CANReceiver->StartReceiving();

Logger->AddToLog("Start 5s wait");

Timer->start();

Logger->AddToLog("Trying to send data over " + QString::number(sendchannel) + QString::number(bufnum));

if ((tmp = CANDevice.SendData(sendchannel, bufnum, 0, sendbuff, 12)) != 1)
{
SetError("Can't send data, error = " + QString::number(tmp));
Timer->stop();
emit TestEnd();
return;
}

Logger->AddToLog("Send " + QString::number(sizeof(sendbuff)) + " bytes");
}

void CTest0::SetFeature(quint64 NumFeat, quint64 ValFeat)
{
switch (NumFeat)
{
case 0:
mode1 = ValFeat;
break;

case 1:
mode2 = ValFeat;
break;

/* ... */

case 19:
bufnum = ValFeat;
break;

default:
break;
}
}

CTest0::~CTest0()
{
if (CANDevice.IsOpen())
CANDevice.Close();

if (CANReceiver != NULL)
delete CANReceiver;

if (Timer != NULL)
delete Timer;
}

QString CTest0::GetName() const
{
return "Test 0";
}

void CTest0::OnTimerTimeOut()
{
Logger->AddToLog("Stop 5s wait");

CANReceiver->StopReceiving();

if (!CANReceiver->GetLastError().isEmpty())
{
SetError("Can't receive data, error \"" + CANReceiver->GetLastError() + "\"");
emit TestEnd();
return;
}

Logger->AddToLog("Succesfully receive " + QString::number(CANReceiver->GetBufferSize()) + " bytes");

}
ccanreceiver.h

#ifndef CCANRECEIVER_H
#define CCANRECEIVER_H

#include <QList>
#include "clasterror.h"
#include "CANDeviceEx.h"
#include <QThread>
#include "CANHeader.h"

class CCANReceiver;

class CCANReceiverHelper : public QObject, public CLastError
{
Q_OBJECT
public:
CCANReceiverHelper(CCANReceiver* _CANReceiver, CCANDeviceEx* _CANDevice);
~CCANReceiverHelper();
void StopWork();

public slots:
void DoWork();

private:
CCANReceiver* CANReceiver;
CCANDeviceEx* CANDevice;
bool Stopped;
_MSG_INPUT rcvblock;
};

class CCANReceiver : public CLastError
{
public:
CCANReceiver(CCANDeviceEx* _CANDevice);
~CCANReceiver();

void ClearBuffer();
QList<quint8> GetBuffer() const;
quint64 GetBufferSize() const;
void StartReceiving();
void StopReceiving();
friend class CCANReceiverHelper;

private:
void AddToBuffer(quint8 data);

private:
QList<quint8> buffer;
CCANReceiverHelper CANReceiverHelper;
QThread CANReceiverHelperThread;
};
ccanreceiver.cpp

#include "ccanreceiver.h"

CCANReceiver::CCANReceiver(CCANDeviceEx *_CANDevice) :
CANReceiverHelper(this, _CANDevice)
{
buffer.clear();
CANReceiverHelper.moveToThread(&CANReceiverHelperThread);
QObject::connect(&CANReceiverHelperThread, SIGNAL(started()), &CANReceiverHelper, SLOT(DoWork()));
}

CCANReceiver::~CCANReceiver()
{

}

void CCANReceiver::ClearBuffer()
{
buffer.clear();
}

QList<quint8> CCANReceiver::GetBuffer() const
{
return buffer;
}

quint64 CCANReceiver::GetBufferSize() const
{
return buffer.size();
}

void CCANReceiver::AddToBuffer(quint8 data)
{
buffer.append(data);
}

void CCANReceiver::StartReceiving()
{
if (!CANReceiverHelperThread.isRunning())
CANReceiverHelperThread.start();
}

void CCANReceiver::StopReceiving()
{
if (CANReceiverHelperThread.isRunning())
{
CANReceiverHelper.StopWork();
CANReceiverHelperThread.quit();
CANReceiverHelperThread.wait();
}
}

CCANReceiverHelper::CCANReceiverHelper(CCANReceive r *_CANReceiver, CCANDeviceEx *_CANDevice) :
CANReceiver(_CANReceiver),
CANDevice(_CANDevice),
Stopped(false)
{

}

void CCANReceiverHelper::StopWork()
{
Stopped = true;
}

CCANReceiverHelper::~CCANReceiverHelper()
{

}

quint64 fact(quint64 n)
{
if (n <= 1)
return 1;
else
return fact(n - 1) * n;
}

void CCANReceiverHelper::DoWork()
{
while (1)
{
quint32 res = 0;
quint32 tmp = 0;

do
{
if (Stopped)
return;

if ((tmp = CANDevice->GetDMAReadyStatus(&res)) != 1)
{
CANReceiver->SetError("Can't get DMA ready status, error = " + QString::number(tmp));
return;
}
}
while (!res);
if ((tmp = CANDevice->ReadDMADataOneBlock((void*)&rcvblock)) != 1)
{
CANReceiver->SetError("Can't read DMA one block, error = " + QString::number(tmp));
return;
}
for (quint8 i = 0; i < rcvblock.RXB0DLC.DLC; i++)
{
CANReceiver->AddToBuffer(rcvblock.data.all[i]);
}
}
}
#endif // CCANRECEIVER_H

I beg for your help.
Sincerely, akaWolf.

Added after 8 minutes:

I described (http://www.qtforum.org/article/39262/strange-problem-with-threads.html) this problem before, but no one could help me.

anda_skoa
30th May 2013, 13:59
The code looks very fragile, it access data from different threads without any protection.

To further simplify the problem I would recommend getting rid of the outer thread.

Cheers,
_

akaWolf
30th May 2013, 15:10
Thank you, I've already solved this problem: it was problem with my colleauge's library because it wrote where it shouldn't. This bug happened to be very covert and i could recognize it long time ago after wrong piece of code was executed..

anda_skoa
31st May 2013, 15:25
Ah, good :)

Btw, you don't need to check a pointer before calling delete on it, delete 0 is perfectly fine for C++.

Cheers,
_