Crash with QVector and freeing memory?
-
For the past week I have been trying to track this bug in which is causing my Qt application to crash and have been unsuccessful in finding the source of the corruption.
What I have done is created 2 global QVectors<double> using "extern", as well as 3 Qwt plots that are plotting what ever is in those vectors every 20 mS on a timer. Meanwhile, a QThread is running at the same time looping at 20 mS as well, populating, resizing, and appending values to the arrays to update the plot on the main thread so it updates realtime.
I am only allowing 30 seconds worth of data to be displayed at all times on all three plots, and once the 30 seconds has been met, I clear the arrays and start appending again in a loop...
30 seconds, with 20 mS refresh, the counter will have to reach 1500 when it needs to refresh...so my counter is from 0 to 1500
Here is the code in the thread that handles the resizing and clearing ect.. ect. ect.
@
// check if the packet counter is maxed for the graph
if (p_counter >= 1500){ // check packet counter// clear the data xDataGlobal.clear(); xAxisGlobal.clear(); // resize arrays xDataGlobal.resize(0); xAxisGlobal.resize(0); p_counter = 1; // clear packet counter } // check values if (typeid(xVal) == typeid(double)){ // append data to packets xDataGlobal.resize(p_counter + 1); xAxisGlobal.resize(p_counter + 1); xDataGlobal[p_counter] = xVal; xAxisGlobal[p_counter] = (double)p_counter; // increment the packet counter p_counter++; // increment packet counter } else { qDebug() << "type id error"; }
@
This code is in a loop every 20 mS...
Now on the main thread, the 3 graphs are being updated using the QwtPlot::timerEvent in which is triggering every 20 mS and setting the data to a QwtPlotCurves as shown below@
void Plot::timerEvent(QTimerEvent *event)
{
if (event->timerId() == d_timerId){
setCurveData(xDataGlobal, xAxisGlobal);
replot();
return;
}
QwtPlot::timerEvent(event);
}/*
// set the curve data
*/
void Plot::setCurveData(QVector<double> xData, QVector<double> xAxisData)
{
d_curveX->setSamples(xAxisData, xData);
}
@At random times, if I have this application running in debug mode, or release, within an hour or two it crashes (in consistant on the timing though...) and I am unable to figure out why. The crash is difficult to pin point in the crash log and the debugger but it always seems to point to a bad alloc or a QVector on the last couple arguments in the stack.
The most recent crash, I opened the application up in the MSVC2008 debugger when it gave me the option and it showed the free.c file for deleting allocated memory. This is the crash line
@
#endif /* CRTDLL /
else // __active_heap == __SYSTEM_HEAP
#endif / _WIN64 */
{
retval = HeapFree(_crtheap, 0, pBlock);
if (retval == 0) // says it crashes right here and that retval is invalid
{
errno = _get_errno_from_oserr(GetLastError());
}
}
}
@From the Qt documentation, clear() frees all of the allocated memory, and resize also removes any extra allocated memory not being used...Am I pushing the QVector to hard? or does anyone have any tips on helping solve this crash? I feel its a memory error...
My system is running Windows 7 using msvc2008 in the QtCreator envoirnment
-
I don't see any mutexes protecting your vector from being read while the thread is writing to them. It's no surprise that your application crashes. If your GUI reads the vector while at the same time the thread writes to it, it could be that the memory that is used to store the values is released and no longer used for the vector but for something else.
-
My first thought was that, but if I have a global QVector in globals.h and globals.cpp as shown:
@
#include "globalexterns.h"QVector<double> xDataGlobal;
QVector<double> xAxisGlobal;
double xVal;
@@
extern QVector<double> xDataGlobal;
extern QVector<double> xAxisGlobal;
extern double xVal;
@I thought they were thread safe. But now that I think about the fact that when my de-allocations and allocations happen, Qwt could be stepping on the memory somewhere internally, which would only make the xVal double thread safe.
I have added the mutex's and will see how it goes. I would just think that the trip up would happen more often then it is if it was stepping on the de-allocated memory somewhere due to the timing and how fast I am updating that array, as well as the plot. But time will tell!
Thank you Volker!
-
It doesn't matter if a data structure is global or part of an object. If you write to it while another thread is reading it, that cries for disaster.
Not even the single double is thread safe without a mutex!
-
I actually ran a test over night and it did actually crash again even with the mutex's... what I find weird is when the debugger crashes and shows the stack, and on both of my threads I was inside my lock on each side, which technically should be impossible since I believe the QMutex's are blocking correct? Unless I am implementing them wrong. So if I lock one, the other will wait until it is unlocked and vise versa?
Heres what it looks like.
20mS thread loop:
@
// lock mutex
memoryMutex.lock();// check if the packet counter is maxed for the graph
if (p_counter >= 1500){ // check packet counter// clear the data xDataGlobal.clear(); xAxisGlobal.clear(); // resize arrays xDataGlobal.resize(0); xAxisGlobal.resize(0); p_counter = 1; // clear packet counter } // check values if (typeid(xVal) == typeid(double)){ // append data to packets xDataGlobal.resize(p_counter + 1); xAxisGlobal.resize(p_counter + 1); xDataGlobal[p_counter] = xVal; xAxisGlobal[p_counter] = (double)p_counter; // increment the packet counter p_counter++; // increment packet counter } else { qDebug() << "type id error"; } // unlock mutex memoryMutex.unlock();
@
main gui thread:
@
/*
// timer trigger 20 mS
*/
void Plot::timerEvent(QTimerEvent *event)
{
if (event->timerId() == d_timerId){
// lock mutex
memoryMutex.lock();setCurveData(xDataGlobal, xAxisGlobal); // unlock mutex memoryMutex.unlock(); replot(); return; } QwtPlot::timerEvent(event);
}
/*
// set the curve data
*/
void Plot::setCurveData(QVector<double> xData, QVector<double> xAxisData)
{
d_curveX->setSamples(xAxisData, xData);
}
@globalexterns.h
@
extern QVector<double> xDataGlobal;
extern QVector<double> xAxisGlobal;
extern double xVal;
extern QMutex memoryMutex;
@globalexterns.cpp
@
#include "globalexterns.h"QVector<double> xDataGlobal;
QVector<double> xAxisGlobal;
double xVal;
QMutex memoryMutex;
@It crashed on the setCurveData() function (shown from the debugger assembly) but when I look at the debugger output, it looks like in both of the threads, it crashed between the lock() and unlock()...on BOTH threads? So thread 1 locked, and thread 2 locked, with the same global QMutex. Is this possible??
-
Hi,
Two things that come to mind, but using a 20msec timer is very tricky! When using Windows timers below 50msec are very unreliable and the 20msec isn't guaranteed. Also for the GUI only about 25fps are needed, so I would suggest lowering the timer to around 40 msec. That might free some processor time and handling between the threads. You can check if the 20msec is achieved using the every time you run the timer.
@
qDebug << QTime::currentTime.toString();
@
Also it is bad practice to use the global variables. It is called "Global Namespace polution" and should be avoided in object orientated programming.
Hope that lowering the timer increases the reliability of your program. -
I am not actually using a timer :) I wish this was the case though!
I have an while loop like so:
@
void newThread::run()
while (t_thread){ // inifinite loop... // do things ... // do things msleep(18); }
}
@I have calculated that it takes about 2 mS to execute the code that I am doing using the Qt mS timer. On application close, I just toggle the boolean and wait for the thread to finish cleanly before I terminate.
my background has been C all through school mainly for mp's as well as the linux OS, so the object oriented programming in C++/Qt is new to me this year and i'm still learning "its ways" :)
And from what Volker has stated before, does Qt allocate memory differently that what the underlying c++ would do for using standard c++ types (doubles, ints, ect.)? I can understand if the memory was being tampered with, but the memory location of the single double should be able to be read from anywhere, anytime, from whomever if it does indeed exist. I might be wrong since I am not sure what Qt does in the background or how it deals with it.