Qt-interest Archive, September 2007
Different behavior between Debug and Release??
Message 1 in thread
Hi,
I have an application that uses QThread, and I'm using the commercial
version of Qt with Visual Studio. When I build the application for debug,
the behavior is as expected, but when I build it for release, it's not as
expected. The specific behavior is below:
in the "run" function of the QThread subclass, I have the following:
{
...code
updateLog("here");
while (!shouldQuitThread)
{
}
}
i know that updateLog is fine because it is a signal, and the log is
updated. therefore, the thread just waits in the while loop after executing
its code.
there's another function that looks like:
if (!isRunning())
start();
else
{
shouldQuitThread = true;
updateLog("Trying to end thread.");
}
now, when this function is called, the log says "Trying to end thread" on
both debug and release configurations. On the debug configuration, the
thread ends, but on the release configuration, it doesn't. Why would this
happen? I can't even debug it because when I build in release configuration
there's no symbols.
Thank you,
Cesar
--
[ signature omitted ]
Message 2 in thread
Dude,
You are using variables to synchronize threads. That is not the way to do
it. In the release version the code is highly optimized and that can lead to
undesired effects. Maybe the assembly code doesn't even read your variable.
What you are in dire need of is to read a book on multithreading.
"cesar del solar" <cdelsolar@xxxxxxxxxxxxxxx> wrote in message
news:fbpi2h$bu9$1@xxxxxxxxxxxxxxxxxxxxx
> Hi,
>
> I have an application that uses QThread, and I'm using the commercial
> version of Qt with Visual Studio. When I build the application for debug,
> the behavior is as expected, but when I build it for release, it's not as
> expected. The specific behavior is below:
>
> in the "run" function of the QThread subclass, I have the following:
>
> {
> ...code
> updateLog("here");
> while (!shouldQuitThread)
> {
>
> }
>
> }
>
> i know that updateLog is fine because it is a signal, and the log is
> updated. therefore, the thread just waits in the while loop after
executing
> its code.
>
> there's another function that looks like:
>
> if (!isRunning())
> start();
> else
> {
> shouldQuitThread = true;
> updateLog("Trying to end thread.");
> }
>
> now, when this function is called, the log says "Trying to end thread" on
> both debug and release configurations. On the debug configuration, the
> thread ends, but on the release configuration, it doesn't. Why would this
> happen? I can't even debug it because when I build in release
configuration
> there's no symbols.
>
> Thank you,
>
> Cesar
>
>
--
[ signature omitted ]
Message 3 in thread
B.C. schrieb:
> Dude,
>
> You are using variables to synchronize threads. That is not the way to do
> it.
So what in your opinion is the proper way then? As as I understand the
problem is somewhat more specific than "thread synchronisation". It's
really about "proper thread termination", and right now I cannot think
of any other solution except the "abort the thread now" variable approach.
For me "thread synchronisation" in general means: "Wait until to threads
have passed the same checkpoint" etc. and for this one uses indeed other
concepts such as "barriers" etc. (and not ordinary variables).
I have used it a couple of times as to properly terminate a thread like
this and it works, but see below.
And if you have a look at the Qt "mandelbrot" example (renderthread.cpp)
you'll quickly see that they do it the same way, using the variable "abort":
RenderThread::~RenderThread()
{
mutex.lock();
abort = true; // if the thread is still running,
// terminate it "properly"
condition.wakeOne();
mutex.unlock();
wait();
}
void RenderThread::run()
{
forever {
...
for (int y = -halfHeight; y < halfHeight; ++y) {
if (restart)
break;
if (abort)
return;
...
}
}
}
(and with "properly" I mean any method but calling QThread::terminate())
> In the release version the code is highly optimized and that can lead to
> undesired effects. Maybe the assembly code doesn't even read your variable.
That's rather unlikely: why should a compiler optimize away the
following statement:
while (!shouldQuitThread)
After all, a compiler cannot guarantee that 'shouldQuitThread' is
modified somewhere else in the code - and in fact, it is in the given
example.
> What you are in dire need of is to read a book on multithreading.
... or look at the Qt threading examples ;)
>> ...
>> there's another function that looks like:
>>
>> if (!isRunning())
>> start();
>> else
>> {
>> shouldQuitThread = true;
>> updateLog("Trying to end thread.");
>> }
>>
>> now, when this function is called, the log says "Trying to end thread" on
>> both debug and release configurations. On the debug configuration, the
>> thread ends, but on the release configuration, it doesn't. Why would this
>> happen? I can't even debug it because when I build in release
> configuration
>> there's no symbols.
What I would add to your code would be a QThread::wait(), as to
synchronise with the thread termination:
if (!isRunning())
start();
else
{
shouldQuitThread = true;
updateLog("Trying to end thread.");
this->wait(); // wait until the thread really has finished.
}
See QThread::wait docs.
I don't know whether that helps in your case (I don't know all your
code), but for sure this is the way to do it(tm).
B.C. is right off course when saying that depending on how the compiler
optimizes your code runtime behaviour can be different between debug and
release.
For example Visual Studio initializes variables in DEBUG with some value
I cannot remember right now (0 maybe?), and when you free a pointer it's
value becomes 0xfeeefeee, so it's easier for the debugger to see when
you are using an uninitialised value or are referencing a pointer which
has been freed before. In the release version though such variables just
contain "garbage" and hence runtime behaviour can be different when you
wrongly "rely" on such behaviour (read: you have done a programming
mistake ;). Typically code like:
void foo() {
int uninitialized; // BUG!
if (uninitialized != 0) {
// executed or not, depending on debug or release
...
}
}
is very likely to behave differently in debug and release.
In your case it could well be that thread scheduling is done differently
by the operating system, simply because the debug version runtime
execution speed is different than the release (hopefully the release
version is "faster"), and in combination with programming errors this
can indeed lead to different behaviour in debug and release.
But probably you already know all this... ;)
Cheers, Oliver
--
[ signature omitted ]
Message 4 in thread
On fredag den 7. September 2007, Till Oliver Knoll wrote:
> > In the release version the code is highly optimized and that can lead to
> > undesired effects. Maybe the assembly code doesn't even read your
> > variable.
>
> That's rather unlikely: why should a compiler optimize away the
> following statement:
>
> while (!shouldQuitThread)
>
> After all, a compiler cannot guarantee that 'shouldQuitThread' is
> modified somewhere else in the code - and in fact, it is in the given
> example.
Actually, the compiler will always assume it has completely control over a
variable. Consider this code:
shouldQuitThread = false;
if (shouldQuitThread) ...
All optimizing compilers will see that it doesn't need to generate code for
the if statement, but just go straight to the else block. The case you stated
is completely equivalent, just slightly more difficult to find. But the
compiler is free to store a copy of the shouldQuitThread in a register and
not consult the physical location of the variable at all.
The trick needed is to declare your variable volatile. This storage modifier
tells the compiler that the variable can change by other means (this can be
by another thread or by hardware), and it can never assume any knowledge
about the contents, but must always read it.
So just do "volatile bool abort;" in the class declaration, and you can use
this between your threads.
Bo.
--
[ signature omitted ]
Message 5 in thread
Bo Thorsen schrieb:
> On fredag den 7. September 2007, Till Oliver Knoll wrote:
>>> In the release version the code is highly optimized and that can lead to
>>> undesired effects. Maybe the assembly code doesn't even read your
>>> variable.
>> That's rather unlikely: why should a compiler optimize away the
>> following statement:
>>
>> while (!shouldQuitThread)
>>
>> After all, a compiler cannot guarantee that 'shouldQuitThread' is
>> modified somewhere else in the code - and in fact, it is in the given
>> example.
>
> Actually, the compiler will always assume it has completely control over a
> variable. Consider this code:
>
> shouldQuitThread = false;
> if (shouldQuitThread) ...
>
> All optimizing compilers will see that it doesn't need to generate code for
> the if statement, but just go straight to the else block. The case you stated
> is completely equivalent, just slightly more difficult to find. But the
> compiler is free to store a copy of the shouldQuitThread in a register and
> not consult the physical location of the variable at all.
Good point!
Hmmm, just let me complete your example to make sure we are talking
about the same thing:
class MyThread : public QThread {
...
public:
void stopIt();
private:
bool m_shouldQuitThread; // should be VOLATILE, see below
};
void MyThread::stopIt() {
m_shouldQuitThread = true;
this->wait();
}
void MyThread::run() {
...
m_shouldQuitThread = false;
forever {
...
if (m_shouldQuitThread == true) {
return;
}
}
}
Now in my understanding you have described two different issues (equally
bad to our problem):
1. The compiler optimizes completely away the if-body (hence never
executing the return statement.
2. The compiler optimizes the data access to m_shouldQuitThread, storing
its value "false" in a register and never sees a change in the actual
memory address when some other thread sets it to "true".
Now while I can clearly see that using the keyword "volatile", as in:
volatile bool m_shouldQuitThread;
would solve case 2., are you saying that it also solves case 1. by
prohibiting the compiler to "optimize away" the if-body?
Anyway, I have never used the keyword volatile (until now this belonged
into the "dark areas of C++" which I never intended to touch), but this
is now clearly a case where I see it would indeed be very useful and
needed! - probably I have just been too lucky until this very day (uh
oh, quickly need to fix all my code... ah and guys at Trolltech: what
about your "mandelbrot" example? ;) or the compilers have just been too
stupid until now and luckily didn't optimize this case ;)
Cheers, Oliver
--
[ signature omitted ]
Message 6 in thread
On fredag den 7. September 2007, Till Oliver Knoll wrote:
> Bo Thorsen schrieb:
> > On fredag den 7. September 2007, Till Oliver Knoll wrote:
> >>> In the release version the code is highly optimized and that can lead
> >>> to undesired effects. Maybe the assembly code doesn't even read your
> >>> variable.
> >>
> >> That's rather unlikely: why should a compiler optimize away the
> >> following statement:
> >>
> >> while (!shouldQuitThread)
> >>
> >> After all, a compiler cannot guarantee that 'shouldQuitThread' is
> >> modified somewhere else in the code - and in fact, it is in the given
> >> example.
> >
> > Actually, the compiler will always assume it has completely control over
> > a variable. Consider this code:
> >
> > shouldQuitThread = false;
> > if (shouldQuitThread) ...
> >
> > All optimizing compilers will see that it doesn't need to generate code
> > for the if statement, but just go straight to the else block. The case
> > you stated is completely equivalent, just slightly more difficult to
> > find. But the compiler is free to store a copy of the shouldQuitThread in
> > a register and not consult the physical location of the variable at all.
>
> Good point!
>
> Hmmm, just let me complete your example to make sure we are talking
> about the same thing:
>
> class MyThread : public QThread {
> ...
>
> public:
> void stopIt();
>
> private:
> bool m_shouldQuitThread; // should be VOLATILE, see below
> };
>
> void MyThread::stopIt() {
> m_shouldQuitThread = true;
> this->wait();
> }
>
> void MyThread::run() {
> ...
> m_shouldQuitThread = false;
> forever {
> ...
> if (m_shouldQuitThread == true) {
> return;
> }
> }
> }
>
> Now in my understanding you have described two different issues (equally
> bad to our problem):
>
> 1. The compiler optimizes completely away the if-body (hence never
> executing the return statement.
>
> 2. The compiler optimizes the data access to m_shouldQuitThread, storing
> its value "false" in a register and never sees a change in the actual
> memory address when some other thread sets it to "true".
>
> Now while I can clearly see that using the keyword "volatile", as in:
>
> volatile bool m_shouldQuitThread;
>
> would solve case 2., are you saying that it also solves case 1. by
> prohibiting the compiler to "optimize away" the if-body?
Yes, it solves both issues. It must never ever assume anything about a
volatile variable. For example, consider this code (v is a volatile int):
int a = v;
int b = v;
It can't even optimize this to a = b = v, because v could change upon reading.
Some hardware randomizers do this. Or it could have been changed by another
thread between the two statements.
> Anyway, I have never used the keyword volatile (until now this belonged
> into the "dark areas of C++" which I never intended to touch), but this
> is now clearly a case where I see it would indeed be very useful and
> needed! - probably I have just been too lucky until this very day (uh
> oh, quickly need to fix all my code... ah and guys at Trolltech: what
> about your "mandelbrot" example? ;) or the compilers have just been too
> stupid until now and luckily didn't optimize this case ;)
It's an impossible problem to handle variables optimally by an optimizer, so
it's not too surprising that a compiler can't optimize any non-trivial use of
a variable read away. But declare it volatile, or the next compiler version
might give you one of the hardest bugs of your life to find :)
Bo.
--
[ signature omitted ]
Message 7 in thread
Thanks guys! I will make sure to declare the variable volatile next time (I
changed some stuff around and don't need it anymore). I've used volatile on
microprocessor code a lot, for similar situations, but for some reason
thought it wasn't needed on a PC compiler.
Cesar
"Bo Thorsen" <bo@xxxxxxxxxxxxxxxxxxxxx> wrote in message
news:200709071205.29471.bo@xxxxxxxxxxxxxxxxxxxxxxxx
> On fredag den 7. September 2007, Till Oliver Knoll wrote:
>> Bo Thorsen schrieb:
>> > On fredag den 7. September 2007, Till Oliver Knoll wrote:
>> >>> In the release version the code is highly optimized and that can lead
>> >>> to undesired effects. Maybe the assembly code doesn't even read your
>> >>> variable.
>> >>
>> >> That's rather unlikely: why should a compiler optimize away the
>> >> following statement:
>> >>
>> >> while (!shouldQuitThread)
>> >>
>> >> After all, a compiler cannot guarantee that 'shouldQuitThread' is
>> >> modified somewhere else in the code - and in fact, it is in the given
>> >> example.
>> >
>> > Actually, the compiler will always assume it has completely control
>> > over
>> > a variable. Consider this code:
>> >
>> > shouldQuitThread = false;
>> > if (shouldQuitThread) ...
>> >
>> > All optimizing compilers will see that it doesn't need to generate code
>> > for the if statement, but just go straight to the else block. The case
>> > you stated is completely equivalent, just slightly more difficult to
>> > find. But the compiler is free to store a copy of the shouldQuitThread
>> > in
>> > a register and not consult the physical location of the variable at
>> > all.
>>
>> Good point!
>>
>> Hmmm, just let me complete your example to make sure we are talking
>> about the same thing:
>>
>> class MyThread : public QThread {
>> ...
>>
>> public:
>> void stopIt();
>>
>> private:
>> bool m_shouldQuitThread; // should be VOLATILE, see below
>> };
>>
>> void MyThread::stopIt() {
>> m_shouldQuitThread = true;
>> this->wait();
>> }
>>
>> void MyThread::run() {
>> ...
>> m_shouldQuitThread = false;
>> forever {
>> ...
>> if (m_shouldQuitThread == true) {
>> return;
>> }
>> }
>> }
>>
>> Now in my understanding you have described two different issues (equally
>> bad to our problem):
>>
>> 1. The compiler optimizes completely away the if-body (hence never
>> executing the return statement.
>>
>> 2. The compiler optimizes the data access to m_shouldQuitThread, storing
>> its value "false" in a register and never sees a change in the actual
>> memory address when some other thread sets it to "true".
>>
>> Now while I can clearly see that using the keyword "volatile", as in:
>>
>> volatile bool m_shouldQuitThread;
>>
>> would solve case 2., are you saying that it also solves case 1. by
>> prohibiting the compiler to "optimize away" the if-body?
>
> Yes, it solves both issues. It must never ever assume anything about a
> volatile variable. For example, consider this code (v is a volatile int):
>
> int a = v;
> int b = v;
>
> It can't even optimize this to a = b = v, because v could change upon
> reading.
> Some hardware randomizers do this. Or it could have been changed by
> another
> thread between the two statements.
>
>> Anyway, I have never used the keyword volatile (until now this belonged
>> into the "dark areas of C++" which I never intended to touch), but this
>> is now clearly a case where I see it would indeed be very useful and
>> needed! - probably I have just been too lucky until this very day (uh
>> oh, quickly need to fix all my code... ah and guys at Trolltech: what
>> about your "mandelbrot" example? ;) or the compilers have just been too
>> stupid until now and luckily didn't optimize this case ;)
>
> It's an impossible problem to handle variables optimally by an optimizer,
> so
> it's not too surprising that a compiler can't optimize any non-trivial use
> of
> a variable read away. But declare it volatile, or the next compiler
> version
> might give you one of the hardest bugs of your life to find :)
>
> Bo.
>
> --
>
> Thorsen Consulting ApS - Qt consulting services
> http://www.thorsen-consulting.dk
>
> --
> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with
> "unsubscribe" in the subject or the body.
> List archive and information: http://lists.trolltech.com/qt-interest/
--
[ signature omitted ]