Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Thread Deadlock

40 views
Skip to first unread message

rob

unread,
Sep 14, 2004, 1:08:08 AM9/14/04
to
Hi All,

I have a dialog based main app that spins off a thread that is running for
the lifetime of the app. The thread is doing some trivial stuff but when
clicking a button it starts a long lasting task. The event handler for
pressing the button also pops up a modal dialog box so the user can abort
the task being performed by the thread. The thread executing the task
accesses functions in the main app to update the GUI and retrieves some
data. When the task if finished the thread goes back into the normal state
and the dialog box disapears. All this works without any problem.

The problem occurs when quiting the app. In OnDestroy I set a flag for the
thread to exit. Then I call GetExitCodeThread in a loop to wait for the
thread to die. At this point the app sometimes locks up. It never locks up
when I remove the code in the thread that updates the GUI of the main app.
My conclusion is that the thread for the main app is locked in OnDestroy. If
at that time the thread tries to call some function in the main app it waits
until OnDestroy is done but OnDestroy waits for the thread to die ->
deadlock. But if this is true then why is there no deadlock when the thread
is doing the long task described above? At this time the main app is stuck
in the event handler for the pressed button (because it is showing the modal
dialog box). Applying the same logic the thread can't access functions in
the main app because the main app is stuck in the even handler.

What is the solution to this problem? On idea is to send messages back and
forth instead of the thread calling functions directly. Because there are a
lot of functions being called this would be very cumbersom, though. Another
solution would be to do something with critical sections. Finally, when the
user choses to exit I could spin off another thread that waits for all other
threads to die and only then this thread send the actual message to
terminate the app.

Any inputs/comments are appreciated.

Rob


Heinz Ozwirk

unread,
Sep 14, 2004, 2:31:15 AM9/14/04
to
> The problem occurs when quiting the app. In OnDestroy I set a flag for the
> thread to exit. Then I call GetExitCodeThread in a loop to wait for the
> thread to die. At this point the app sometimes locks up. It never locks up
> when I remove the code in the thread that updates the GUI of the main app.
> My conclusion is that the thread for the main app is locked in OnDestroy. If
> at that time the thread tries to call some function in the main app it waits
> until OnDestroy is done but OnDestroy waits for the thread to die ->
> deadlock. But if this is true then why is there no deadlock when the thread
> is doing the long task described above? At this time the main app is stuck
> in the event handler for the pressed button (because it is showing the modal
> dialog box). Applying the same logic the thread can't access functions in
> the main app because the main app is stuck in the even handler.


If your worker thread uses the main thread to update the GUI, the main thread must be prepared to process messages. If your OnDestroy is simply looping on GetExitCodeThread, no messages will be processed. You have to get and dispatch those messages yourself. On the other hand, when your main thread is waiting for a modal dialog to close, the thread is not stuck in the event handler. Modal dialogs implement their own message loop, that keep the system responding.

To solve your problem, you can call PeekMessage and DispatchMessage while you are waiting for the thread to terminate.

HTH
Heinz

rob

unread,
Sep 14, 2004, 3:02:30 AM9/14/04
to

"Heinz Ozwirk" <wans...@gmx.de> wrote in message
news:ci6369$ijn$07$1...@news.t-online.com...

Heinz & all,

Thanks for your input. I have a question, though. How can an app update
itself when an event handler of this app creates a modal dialog? The app
does not get out of the event handler until the dialog is closed. So how
does the message loop in the modal dialog help the app to be updated? In my
thread I can't directly call a function in the main app (I am not sending
messages but do a direct call). So how can a function in the main app be
executed when the app is stuck in the event handler that popped up the modal
dialog?

Regards,
Robert


Mihajlo Cvetanovic

unread,
Sep 14, 2004, 5:18:18 AM9/14/04
to
rob wrote:
> Thanks for your input. I have a question, though. How can an app update
> itself when an event handler of this app creates a modal dialog? The app
> does not get out of the event handler until the dialog is closed. So how
> does the message loop in the modal dialog help the app to be updated? In my
> thread I can't directly call a function in the main app (I am not sending
> messages but do a direct call). So how can a function in the main app be
> executed when the app is stuck in the event handler that popped up the modal
> dialog?

1. Message handling

Every window has a function that gets called when a message needs to be
dispatched to it. This function is registered to a window, and this is
hidden from you in MFC, but create non-MFC project and see what wizard
gave you. This function is called from a Message Pump. All message pumps
(including those in both MFC and non-MFC projects, dialog boxes, even in
message boxes (created with [Afx]MessageBox) look pretty much like this:

while( (bRet = GetMessage( &msg, NULL, 0, 0 )) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

So, message handler for one window (dialog) gets called from message
handler of another window (main window). While in dialog, message pump
can call main window's message handler just as well. Only thing to
remember is that message pump must always be "alive", which means that
you can't wait too long in one message handler and not do something that
creates another message pump.

2. Call Stack of a Thread

When debugging observe the Call Stack. You'll see a number of functions
one on top of another. It doesn't matter what are those functions, or
whose classes are they belonging to. What matters is that every thread
has its own call stack, totally independent of stacks of other threads.
To put it another way: threads have context, classes don't have context.
It doesn't matter if that class is derived from CWinApp, or whatever.

HTH

Scott McPhillips [MVP]

unread,
Sep 14, 2004, 9:38:35 AM9/14/04
to
rob wrote:
> Heinz & all,
>
> Thanks for your input. I have a question, though. How can an app update
> itself when an event handler of this app creates a modal dialog? The app
> does not get out of the event handler until the dialog is closed. So how
> does the message loop in the modal dialog help the app to be updated? In my
> thread I can't directly call a function in the main app (I am not sending
> messages but do a direct call). So how can a function in the main app be
> executed when the app is stuck in the event handler that popped up the modal
> dialog?
>
> Regards,
> Robert

The modal dialog

The modal dialog does not interfere with custom message processing by
the application's other windows. Direct calls that update windows
created in the main thread are not supported by MFC and can lead to
several problems. Your thread should PostMessage to request the main
thread to update things, and PostMessage to signal when it has finished.
See http://www.mvps.org/vcfaq/mfc/index.htm

--
Scott McPhillips [VC++ MVP]

OL

unread,
Sep 14, 2004, 9:12:08 AM9/14/04
to
"rob" <rmdi...@hotmail.com> wrote in message news:<aIw1d.742$Y65...@newsread1.news.pas.earthlink.net>...

Hi there Robert,
A classic problem , that is why you should use modalless dialogs that
doesn't hold the main thread message loop blocked so it can process
incoming messages.
Or you could set up a timer or override DoModal() to do a
PeekMessage() and CWinApp::PumpMessage() that will let the applicaion
a chance to process some messages once in a while.

Hope it helped,
Omry Levy

Doug Harrison [MVP]

unread,
Sep 14, 2004, 11:13:44 AM9/14/04
to
rob wrote:

>Hi All,
>
>I have a dialog based main app that spins off a thread that is running for
>the lifetime of the app. The thread is doing some trivial stuff but when
>clicking a button it starts a long lasting task. The event handler for
>pressing the button also pops up a modal dialog box so the user can abort
>the task being performed by the thread. The thread executing the task
>accesses functions in the main app to update the GUI and retrieves some
>data. When the task if finished the thread goes back into the normal state
>and the dialog box disapears. All this works without any problem.
>
>The problem occurs when quiting the app. In OnDestroy I set a flag for the
>thread to exit. Then I call GetExitCodeThread in a loop to wait for the
>thread to die.

All things being equal, you should be using WaitForSingleObject on the
thread handle. That method waits using zero CPU time, while your loop
consumes all available cycles. See this message for instructions on safely
waiting for MFC threads to exit:

http://groups.google.com/groups?selm=m3ep501falcm3ecgdb1tsof6p42f32fvk7%404ax.com

>At this point the app sometimes locks up. It never locks up
>when I remove the code in the thread that updates the GUI of the main app.

"Updating the GUI" implies SendMessage, and you're talking about
inter-thread SendMessage, which is indeed prone to deadlock unless you
design to avoid it. See these messages for more on this subject and
alternative approaches:

http://groups.google.com/groups?selm=6v28f0d5aadcafkfp2682ecjva7qltssif%404ax.com
http://groups.google.com/groups?selm=gli6k0p8t7elm3sb6pid88rqbl03f8o4m6%404ax.com

In a nutshell, you'll probably want to define a custom message and
PostMessage it to the dialog window, which is described in the
mvps.org/vcfaq link.

>My conclusion is that the thread for the main app is locked in OnDestroy.

Correct! Your main thread is blocked in OnDestroy, not processing the
messages the secondary thread is trying to send it. This would also happen
with WaitForSingleObject, but keep reading.

>If at that time the thread tries to call some function in the main app it waits
>until OnDestroy is done but OnDestroy waits for the thread to die ->
>deadlock. But if this is true then why is there no deadlock when the thread
>is doing the long task described above? At this time the main app is stuck
>in the event handler for the pressed button (because it is showing the modal
>dialog box). Applying the same logic the thread can't access functions in
>the main app because the main app is stuck in the even handler.

Modal dialogs boxes run their own message loops, and as explained in the
messages linked to above, they call functions that allow inter-thread
SendMessage calls to go through. Note that SendMessage is a very special
function; ordinary functions of course don't block or context switch to the
target thread as SendMessage does. Instead, the calling thread executes
ordinary functions concurrently with other threads.

>What is the solution to this problem? On idea is to send messages back and
>forth instead of the thread calling functions directly. Because there are a
>lot of functions being called this would be very cumbersom, though.

But SendMessage is your problem, not your solution. :) Practically every
CWnd function amounts to one or more SendMessage calls under the hood, and
this is also true for many Windows API functions, such as SetWindowText. As
mentioned earlier, custom messages and PostMessage represent your probable
solution.

>Another solution would be to do something with critical sections.

Not sure how that would help, though if you're accessing data in multiple
threads and modifying it at all, you do need to synchronize this access.

>Finally, when the
>user choses to exit I could spin off another thread that waits for all other
>threads to die and only then this thread send the actual message to
>terminate the app.

You're right to want to join with all secondary threads before allowing the
primary thread to exit. If you don't, they'll continue to run while the
environment in which they live is being destroyed all around them, including
things like static duration objects, MFC, the CRT, and so forth, which can
have all sorts of nasty side effects. A secondary manager thread might work,
but you'd have to return to your message loop in order for the SendMessage
calls to work. This leaves your app in a weird state in which it's logically
dead but still needs to process messages from the secondary thread, and
dropping back into the MFC message loop is probably not the best way to deal
with this, as it can leave you open to subsequent user input messages. I've
dealt with a similar situation by not using a manager thread, but instead
using MsgWaitForMultipleObjects in a loop that discards input messages, so
that user input is ruled out while I wait for my handles to become signaled.
This method works well when the waiting period is brief. It avoids changes
in window appearance that may occur when you disable a window. It keeps
things local, avoiding any possibility that MFC will shut down the app on
its own, as it does when the main window is closed, or otherwise interfere
with your plan.

As the MsgWaitForMultipleObjects function is tricky to use, see this message
for a template you can modify to suit your needs:

http://groups.google.com/groups?selm=gq79f0tf209iivgo5ovucqtbm4tf72v90u%404ax.com

You would need to replace the following:

if (PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
theApp.PumpMessage();

with a version that discards input messages. However, the obvious approach
of examining the message before calling PumpMessage doesn't work. Do you see
why? The comment in the function below explains it (note that WM_PAINT is a
low priority message):

// For WM_KICKIDLE
#include <afxpriv.h>

bool
PumpMessageDiscardingInputMessages(HWND hWnd)
{
// It's possible for PeekMessage to return WM_PAINT but for a subsequent
// GetMessage to return WM_KEYDOWN. Thus, we can't call PumpMessage
// directly, as it does its own GetMessage.

MSG msg;
if (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
{
if (msg.message != WM_KICKIDLE)
{
if (IsInputMessage(msg)
&& (!hWnd
|| msg.hwnd == hWnd
|| (msg.hwnd && IsChild(hWnd, msg.hwnd))))
return true;
if (!AfxGetApp()->PreTranslateMessage(&msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return true;
}
return false;
}

Finally, here's my IsInputMessage:

#include <algorithm>

inline bool
FindMsg(const UINT msg, const UINT* pTab, const UINT* pTabEnd)
{
return std::find(pTab, pTabEnd, msg) != pTabEnd;
}

bool
IsInputMessage(
const UINT msg,
const bool keys /*= true*/,
const bool mice /*= true*/,
const bool commands /*= true*/)
{
static const UINT keyMsgTab[] =
{
WM_KEYDOWN,
WM_KEYUP,
WM_SYSKEYDOWN,
WM_SYSKEYUP,
WM_CONTEXTMENU
};

static const UINT miceMsgTab[] =
{
WM_LBUTTONDOWN, WM_LBUTTONUP,
WM_LBUTTONDBLCLK,
WM_MBUTTONDOWN, WM_MBUTTONUP,
WM_MBUTTONDBLCLK,
WM_RBUTTONDOWN, WM_RBUTTONUP,
WM_RBUTTONDBLCLK,
WM_XBUTTONDOWN, WM_XBUTTONUP,
WM_XBUTTONDBLCLK,
WM_NCLBUTTONDOWN, WM_NCLBUTTONUP,
WM_NCLBUTTONDBLCLK,
WM_NCMBUTTONDOWN, WM_NCMBUTTONUP,
WM_NCMBUTTONDBLCLK,
WM_NCRBUTTONDOWN, WM_NCRBUTTONUP,
WM_NCRBUTTONDBLCLK,
WM_NCXBUTTONDOWN, WM_NCXBUTTONUP,
WM_NCXBUTTONDBLCLK,
};

static const UINT commandMsgTab[] =
{
WM_COMMAND,
WM_SYSCOMMAND,
WM_CONTEXTMENU
};

// NB: WM_CONTEXTMENU is generated as a result of pressing the Menu key,
// so eat it if we're discarding keys or commands.

// VCFIXUP - FindMsg could be a template function but VC6 doesn't like
// UINT (&x)[size]
#define FIND(tab) FindMsg(msg, tab, tab+sizeof(tab)/sizeof(tab[0]))

return (keys && FIND(keyMsgTab))
|| (commands && FIND(commandMsgTab))
|| (mice && FIND(miceMsgTab));

#undef FIND
}


--
Doug Harrison
Microsoft MVP - Visual C++

0 new messages