Re: Getting thread to stop in CDialog type class

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance



See below...
On Mon, 16 Feb 2009 02:59:56 -0800 (PST), andreas.zetterstrom@xxxxxxxxx wrote:

On 15 Feb, 04:03, Joseph M. Newcomer <newco...@xxxxxxxxxxxx> wrote:
(1) presumably there *is* a reason to force the update.  Please explain
(2) presumably it does this via a PostMessage, and does not manipulate the labels directly

Read my essay on worker threads on my MVP Tips site.  

You are doing a cross-thread SendMessage, which should always be thought of as a Really
Bad Idea To Be Avoided At All Costs.  Don't touch a window owned by one thread from any
other thread, period.  Otherwise, the failure you see is inevitable.

Waiting in OnDestroy is a particularly poor way to deal with this, and must not be used.
You CANNOT make it work, so don't even bother trying.  It is the wrong place, FAR too
late, to discover that the thread should be shut down.  See my essay on worker threads.
You have to initation shutdown as a two-phase process
        OnClose/OnOK/OnCancel:  
                if a thread is running
                        Tell the thread to stop
                        Set a flag saying you are in shutdown mode
                        return without calling the CDialog superclass method
        thread:
                When it sees it should stop, it exits its loop, then does a
                PostMessage a user-defined completion message to the main thread
        main thread OnThreadCompleted handler
                indicate the thread has finished
                if the shutdown mode flag is set, PostMessage(WM_CLOSE) to
                        your window to close
                        (Note that since there is no thread running, it will
                        shut down as expected)

The failure here is that you are trying to impose sequential semantics in an asynchronous
world.  Generally, treat most WaitForSingleObject calls in the main thread as a design
error.  You must not block the message pump.  So make everything be asynchronous in the
first place, don't think about forcing waits, and the problem goes away.  And NEVER
manipulate windows from another thread!
                        joe



On Fri, 13 Feb 2009 08:01:30 -0800 (PST), andreas.zetterst...@xxxxxxxxx wrote:
Let's see if I can explain this...

I have a class based on a standard CDialog, in it I have a thread
which loops every 100 ms. This thread has two purposes;
1) it invalidates a painting area, forcing the dialog to redraw even
if windows don't see it as nessesary
2) it updates values in various labels and such

My problem is with #2, and specifically when the app is about to exit.

At OnDestroy I have to stop the thread, as after OnDestroy has
finished the hWnds to all child labels are not usable anymore. To do
this I wait in OnDestroy for the thread to finish its current cycle
(using a semaphore), and then stop it. The problem is that writing
data to the labels is essentially sending a message to the message
queue, and the message queue is at the moment stopped in OnDestroyed
(I think), thus a deadlock! I can't for the life of me figure out how
to reliably stop the thread without this deadlock occuring.

Joseph M. Newcomer [MVP]
email: newco...@xxxxxxxxxxxx
Web:http://www.flounder.com
MVP Tips:http://www.flounder.com/mvp_tips.htm

1) I'm showing simple moving graphics with related data shown in
various CLabels and CListCtrls, which is why I need to update the
window more often than Windows wants me to.
****
What does Windows wanting you to do the update have to do with anything. The normal
mechanism that you invoke some form of InvalidateRect (such as Invalidate()) whenever
*you* want to update something. Windows attitude on update does not get involved in this
at all. So presumably the reason you call Invalidate() is that *you* have something that
changed, which is not stated in your original description (you just said that it
invalidated every 100 milliseconds, but not that there was a reason for doing so)
****
2) I'm calling functions such as CListCtrl::InsertItem and
CStatic::SetWindowText which I assume at some level posts or sends a
message to the queue, waits for it to complete and hangs the app due
to me incorrectly waiting in OnDestroy.
****
Right. Which is why you would never, ever call CListCtrl::InsertItem or
CStatic::SetWindowText from a thread. Because of what you saw, it is dangerous to do so.
Deadlock is the usual error encountered. If you want to modify something, you PostMessage
to the main GUI thread and ask *it* to do the updates for you. You NEVER modify a
window's contents or take other actions on it from a thread that does not own it.
****

Your method of stopping the thread from OnClose seems like a perfectly
good solution, and in fact I myself tried to do that, but by some
wierd reason OnClose is never called in my app. I have traced it down
to the main CFormView, and as it is part of the base of the MFC
project I don't know how I can get its OnClose to activate. That is
why I was using OnDestroy as it was the only message that actually was
reliably called. So I guess this is another obstacle I have to
overcome.
****
Note that this applies only to top-level dialogs. You do not have a top-level dialog, so
instead it becomes an issue that when your top-level view receives an OnClose it has to
notify any child windows (such as your dialog in the tab control) that they are to shut
down. Because this is is in the same thread, you can use SendMessage, and SendMessage can
return a BOOL indicating if deferred shutdown is required.
****

Just to clarify: My app is an MFC application, single document,
created by the wizard. In the main CFormView I create a CTabView
derived class, which itself then creates a number of tabs which are
realized by CDialog derived classes where the OnOk and OnCancel bodies
have been emptied, the buttons deleted and the class being defined as
a child (as I can see you yourself do in your examples). In OnClose in
CFormView I call CTabView::CloseWindow and in CTabView's OnClose I
call CloseWindow for the CDialogs. But as I never get the OnClose from
CFormView of course none of the others are called either.
****
Good clarification. But see above. The CFormView, when it receives a notification to
close, must notify the child windows that it is closing. Otherwise, the CFormView has to
"own" the thread so it knows how to shut it down and receives the shutdown notification.
****

About the closing of the thread (or timer as I guess it should be
instead); why do I have to post a user defined message and then when
receiveing that message posting WM_CLOSE? can't I post WM_CLOSE
directly from the thread/timer as it finishes for the last time?
****
Because you should not be controlling windows from other threads. Essentially, if it
involves anything but PostMessage, it can only be done from the thread that owns the
window.

Note that this presumes that your thread knows it is the only thread involved, and that
shutting down always implies a close of the app is required. The thread should not know
this. If you add another thread, each thread handles its shutdown by notifying its
"owner" that it has shut down; it is only the owner that decides that this implies a
postponed WM_CLOSE should be continued. This is because a thread might terminate for
other reasons, which would NOT require that the application close.
joe
****

------------

****
And your point is? So what? First, OnTimer does *not* "send" a message to the queue; nor
does it "post" a message. What happens is that if the timer goes off, the next time
GetMessage discovers that there is nothing in the queue it simulates the receipt of a
timer message.
*****
What I meant was, if OnTimer is run by the same thread that handles
the messages, won't it deadlock itself if I somewhere in the OnTimer
function tries to post a new message? as through CListCtrl::InsertItem
or CStatic::SetWindowText?

You said you had a dialog. So either you have a dialog, or you have a property page. If
you had a property page, you should have said so.
I don't really know what a property page is, but looking at this link
(http://msdn.microsoft.com/en-us/library/675f1588(VS.80).aspx) I'm
pretty sure a CDialog derived class with the OnOk and OnCancel bodies
emptied is much closer to a CDialog than a property page.

Thanks for the input. I'm definitely going to bookmark your tips page.
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.



Relevant Pages