I created a simple dialog based application.
I'm starting a thread like this from the dialog class
when the user clicks a button :
m_pThread = AfxBeginThread(DoDownload, this);
Where 'this' is a CFileDownloadDlg *.
My DoDownload function :
UINT DoDownload(LPVOID pParam){
CFileDownloadDlg *pDlg = (CFileDownloadDlg *)pParam;
pDlg->Download(); // Lengthy operation
return 0;
}
Here is my CFileDownloadDlg::Download
void CFileDownloadDlg::Download()
{
m_dlObject.Download(); // m_dlObject is an object that downloads
// a file from the net and is defined in a static
library of my own.
// It also have a callback mechanism which I initialize when
app. starts
// (I pass a simple pointer to a global function) I think
this is where
// the problem is... see below
}
Finally here is my abort button actions :
[...]
csl.Lock();
m_bAbort = TRUE;
csl.Unlock();
TRACE("Waiting for thread\n");
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
TRACE("Thread returned !\n");
[...]
When I comment out the WaitForSingleObject line,
the worker thread terminate correctly (it don't wait till
it finishes, the thread is really aborting itself, I see it also
in the debug message window)
One strange thing is that if I implement a fake infinite loop
in the DoDownload() function and I return only when
m_bAbort equals TRUE, then WaitForSingleObject does
not freeze my application.... otherwise my application freeze
and even the worker thread locks up ! Maybe it is
my callback function from an object that is in a static lib ?
I can provide my whole project if anyone is interrested !
Thank you
Mike
It's to be expected that WFSO blocks the calling thread until the wait is
satisfied. This is particularly noticeable in UI threads, because it blocks
the UI. Thus, you should not use WFSO(INFINITE) in an UI thread unless
you're certain the wait will be satisfied quickly. In addition, you must not
use WFSO(INFINITE) in any thread if that thread must perform some subsequent
action to satisfy the wait, or you get a deadlock. Because both your threads
hang when your UI thread uses WFSO, I'd guess that your worker thread is
trying to SendMessage to the UI thread. The problem is, inter-thread
SendMessage calls are synchronous and are not dispatched until the target
thread calls GetMessage, PeekMessage, etc, which Windows considers a "safe"
time to allow the message to be processed by the target thread. In the
meantime, the sending thread sits around waiting for the target to call one
of these functions, but if the target thread is blocked in WFSO, it'll wait
forever, and you have a deadlock.
Also, I see that you're protecting m_bAbort with a mutex in your UI thread,
which I hope you realize you must also do in your worker thread.
Alternatives to this that allow you to do away with the mutex include using
a CEvent object instead of the bool, which would allow the worker thread to
WaitForMultipleObjects(INFINITE) on the quit event and any other waitable
objects it needs to wait on, or if you don't mind a non-portable,
x86-specific approach, you could simply declare the bool volatile. For more
on this, see these messages:
http://groups.google.com/groups?selm=7hl7205u4ksejokc9ormufeo02pb9i1stu%404ax.com
http://groups.google.com/groups?selm=k7fa209epgjcn2rep0rqks2ee0f4jcitgu%404ax.com
--
Doug Harrison
Microsoft MVP - Visual C++
I'll look at the MTRECALC sample to
implement a correct solution with CEvents since
this is the exact same behavior I need. Its my
first steps with multithreaded apps so I'll try
to read more on it...
Thank you !
Mike
"Doug Harrison [MVP]" <d...@mvps.org> wrote in message
news:6v28f0d5aadcafkfp...@4ax.com...
Idea, write own WaitForSingleObjectInUIThread(...), and implement own timeout watcher.
Yep, just be careful to respect the note in the documentation:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/msgwaitformultipleobjects.asp
<q>
Note MsgWaitForMultipleObjects does not return if there is unread input of
the specified type in the message queue after the thread has called a
function to check the queue. This is because functions such as PeekMessage,
GetMessage, GetQueueStatus, and WaitMessage check the queue and then change
the state information for the queue so that the input is no longer
considered new. A subsequent call to MsgWaitForMultipleObjects will not
return until new input of the specified type arrives. The existing unread
input (received prior to the last time the thread checked the queue) is
ignored.
</q>
In a nutshell, unless you're certain of everything that preceded the MWFMO
call, you need to process any queued messages in a PeekMessage loop before
dropping into MWFMO, e.g.
LONG idleCount = 0;
for (;;)
{
MSG msg;
if (PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
theApp.PumpMessage();
else if (!theApp.OnIdle(idleCount++))
{
idleCount = 0;
if (!PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
{
HANDLE hdls[2] = { evQuit, hWhatever };
DWORD res = MsgWaitForMultipleObjects(2, hdls,
false, INFINITE, QS_ALLINPUT);
if (res == WAIT_OBJECT_0)
break;
else ...
}
}
}
If you were concerned about being flooded with messages or idle-time
processing, such that there might be a significant delay before MWFMO was
called, you could "poll the handles" by injecting some WFMO calls with zero
timeout and thus give the handles priority.
"Doug Harrison [MVP]" <d...@mvps.org> wrote in message
news:gq79f0tf209iivgo5...@4ax.com...
>Use MsgWaitForMultipleObjectsEx/MWMO_INPUTAVAILABLE get around that unread
>input problem.
Thanks for the note. Restrictions appear to be:
That flag is not available on Windows NT.
That function is not available on Windows 95.
There's yet another bug in your code. When you create threads using
AfxBeginThread and a worker function, the returned thread will be
deleted as soon as the thread ends. In the worst case the thread
terminates (and destroys *m_pThread) just between m_bAbort=TRUE and the
call of WFSO.
UINT AFX_CDECL Thread(LPVOID)
{
Sleep(100);
return 0;
}
CWinThread* pThread = AfxBeginThread(Thread, 0);
Sleep(1000); // Maybe the scheduler decided to do a task switch here.
// If Thread() finishes, *pThread becomes invalid!
WaitForSingleObject(pThread->m_hThread, INFINITE); // Invalid handle!
You should prevent MFC from automatically deleting the thread:
m_pThread = AfxBeginThread(DoDownload, this,
THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED);
m_pThread->m_bAutoDelete = FALSE;
m_pThread->ResumeThread();
// ...
m_bAbort = TRUE;
// ...
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
delete m_pThread;
Regards,
Tobias