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

WaitForSingleObject is blocking program

8 views
Skip to first unread message

PS

unread,
Mar 19, 2004, 7:46:45 AM3/19/04
to
Hello,

I'm having a problem with a thread construction.

I have 2 buttons in a dialog box.
Pressing 'start' will activate the thread.
Pressing 'stop' has to terminate the thread.
I'm using an event (m_Finished) to indicate that the action is
started.
When the thread returns, I set that event, so I know the action is
done.
Pressing the 'close' button, the application must quit, BUT the thread
must be finished first. I'm using the 'm_Finished' event to see is the
action is done. If it's still busy, I want the thread to be terminated
before closing, so the OnClose() function has to wait until the thread
is done.


// ************************************************
// Simple thread that does ... nothing, except wait
// for an 'ending' command (passed by an event).
// ************************************************
UINT TestProc(LPVOID pParam)
{
HANDLE hEvent = *(HANDLE*)pParam;

bool bThreadDone = false;
while (!bThreadDone)
{
DWORD dwResult = ::WaitForSingleObject(hEvent, 500);
if (dwResult == WAIT_OBJECT_0)
bThreadDone = true;
}
return 0;
}


// ************************************************
// Thread that has to block execution in OnClose()
// until 'm_Finished' event is set.
// ************************************************
UINT WaitUntilFinished(LPVOID pParam)
{
HANDLE hEvent = *(HANDLE*)pParam;

bool bLoop = true;
while (bLoop == true)
{
DWORD dwResult = WaitForSingleObject(hEvent, 500);
if (dwResult == WAIT_OBJECT_0)
bLoop = false;
}

return 0;
}


// *****************************
// if "START"-button is pressed
// --> start thread
// *****************************
void CtestDlg::OnBnClickedStart()
{
ResetEvent(m_Finished); // <--- m_Finished in nonsignaled state

CWinThread* pThread = AfxBeginThread(TestProc, &m_TerminateAsked);

DWORD dwRet;
do
{
dwRet = WaitForSingleObject(pThread->m_hThread, 100);
if (dwRet != WAIT_OBJECT_0)
{
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} while ((dwRet != WAIT_OBJECT_0) && (dwRet != WAIT_FAILED));

SetEvent(m_Finished); // <--- m_Finished in signaled state
}


// *****************************
// if "STOP"-button is pressed
// *****************************
void CtestDlg::OnBnClickedStop()
{
SetEvent(m_TerminateAsked);
}


// *****************************
// if "CLOSE"-button is pressed
// *****************************
void CtestDlg::OnClose()
{
SetEvent(m_TerminateAsked);

// before closing the dialog box, all action must be done
// ---> m_Finished event must have been set

CWinThread* pThread = AfxBeginThread(WaitUntilFinished,
&m_Finished);
WaitForSingleObject(pThread->m_hThread, INFINITE);

CloseHandle(m_Finished);
CloseHandle(m_TerminateAsked);

CDialog::OnClose();
}


Using the sequence 'start' -> 'stop' -> 'close' works.

Using the sequence 'start' -> 'close' should also be working, but it
doesn't !

For some reason, I believe WaitForSingleObject() in the OnClose()
function is
blocking the whole thing cause the 'm_Finished' event is never set.
It is waiting until the m_Finished event gets in the signaled state.
That's exactly what I want, BUT ... for some reason, the
WaitForSingleObject()
in the OnBnClickedStart() function is blocked until the wait function
in the
WaitUntilFinished() is done. And that isn't possible since that
function is
waiting on the m_Finished event.

So ... I would like the WaitUntilFinished()-thread to block the
processing of
the OnClose()-function until the m_Finished event is set, but in the
meanwhile,
the other processing must continue (e.g. setting the m_Finished event
in OnBnClickedStart),
otherwise, the system will hang.

Any idea's ?

Jase

unread,
Mar 19, 2004, 8:23:58 AM3/19/04
to
Instead of calling WaitForSingleObject on your event, you can simply call it
on the thread handle.

Jase

"PS" <abc_3...@hotmail.com> wrote in message
news:57a6d9b.04031...@posting.google.com...

Do you reckon, if you tried, could you make it any more complicated? You've
really confused the issue by putting a message loop in a command handler.
It's actually *heaps* simpler than that. You only need 3 lines of code to
start and stop the thread.

In the start button handler:
m_pThread = AfxBeginThread(TestProc, &m_TerminateAsked);
where m_pThread is a CWinThread * member variable.

In the stop button handler::
SetEvent(m_TerminateAsked);
WaitForSingleObject(m_pThread->m_hThread, INFINITE);

If you use a thread proc that might terminate itself, then you need 3 more
lines:

Add this to the start button handler:
m_pThread->m_bAutoDelete = false;

In the stop button handler, wrap the calls in
if (GetExitCodeThread(m_pThread->m_hThread) == STILL_ACTIVE) {
...
}

Jase


rsr

unread,
Mar 19, 2004, 8:34:19 AM3/19/04
to
Just a simple thoughts...
WaitUntilFinished can be replaced with single WaitForSingleObject with
infinite arg.
OnBnClickedStart should only start the thread and disable the button.
OnBnClickedStop...hmm i would use a global bool variable, like g_bRun. When
u press start thread pops up, g_bRun sets to true and thats all.
If you want to terminate the thread, you will have to set g_bRun to false
and add special code to the thread procedure
OnClose - you want to stop the thread manually or wait till its done? I
would use an event informing about finishing the job, and the g_bRun var if
you dont wanna wait. Make it as simple as it is possible.


Peter Simons

unread,
Mar 19, 2004, 2:13:30 PM3/19/04
to
Thanks to some useful tips, I significantly simplified my code.

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

// ********
// function
// ********
void CtestDlg::ExecFunction()
{
m_pThread = AfxBeginThread(TestProc, &m_TerminateAsked);
}

// ********
// Start
// ********
void CtestDlg::OnBnClickedStart()
{
ExecFunction();
}

// ********
// Stop
// ********
void CtestDlg::OnBnClickedStop()
{
DWORD ExitCode;
GetExitCodeThread(m_pThread->m_hThread, &ExitCode);
if (ExitCode == STILL_ACTIVE)
{
SetEvent(m_TerminateAsked);
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
}
}

// ********
// Close
// ********
void CtestDlg::OnClose()
{
DWORD ExitCode;
GetExitCodeThread(m_pThread->m_hThread, &ExitCode);
if (ExitCode == STILL_ACTIVE)
{
SetEvent(m_TerminateAsked);
WaitForSingleObject(m_pThread->m_hThread, INFINITE);
}

CloseHandle(m_TerminateAsked);

CDialog::OnClose();
}

// ********
// Thread
// ********


UINT TestProc(LPVOID pParam)
{
HANDLE hEvent = *(HANDLE*)pParam;

int ctr = 0;

bool bThreadDone = false;
while (!bThreadDone)
{
DWORD dwResult = WaitForSingleObject(hEvent, 500);
if (dwResult == WAIT_OBJECT_0)
bThreadDone = true;

ctr++;
if (ctr > 10)
bThreadDone = true;
}

return 0;
}
--------------------------------------------------------

But now I have another question, If you're experienced with threads you may know
the answer.

Somehow, it must be possible to 'schedule' multiple functions.
note : this is a simplified example, the real program will pass some parameters
with ExecFunction() !


void CtestDlg::OnBnClickedStart()
{
ExecFunction(...); // first
ExecFunction(...); // second
ExecFunction(...); // third
}

void CtestDlg::ExecFunction()
{
// here, a construction must prevent the thread from being started if :
// a) the previous thread is still running
// b) 'm_TerminateAsked' is in signaled state

m_pThread = AfxBeginThread(TestProc, &m_TerminateAsked);
}


The ExecFunction(...)'s in OnBnClickedStart() must be executed sequentially
(after each other, not at the same time !).
A new ExecFunction() is allowed to be started after the previous one has been
terminated.
If -during a thread execution- the m_TerminateAsked gets in the signaled state,
the active thread must be terminated, and the threads that were waiting to be
started must be canceled.

I don't succeed in creating a software construction that doesn't make the system
hang.

Can anyone help me please ?

Scott McPhillips [MVP]

unread,
Mar 19, 2004, 6:42:25 PM3/19/04
to

You need a different approcah to avoid hanging in the GUI thread. The
GUI thread must keep processing messages. It shouldn't block in a
WaitFor... call. So the whole program, and especially the three calls
to execute things sequentially, should be event-driven, not three calls
in series.

You can set up logic to launch the first thread. When it has finished
it can cause the second to start, and etc. There are two ways: posting
messages or setting events. Posting messages is the only way to do this
if the GUI thread is involved. (Setting events is the only way to do
this if overlapped I/O is involved.) Look here to see how to post
messages to yourself, including between threads:

http://www.mvps.org/vcfaq/mfc/index.htm

Your thread code can sense events (is that what m_TerminateAsked is?) by
calling WaitForSingleObject with a 0 timeout. The return value
indicates whether or not the event is signaled.

--
Scott McPhillips [VC++ MVP]

Jugoslav Dujic

unread,
Mar 20, 2004, 5:21:03 AM3/20/04
to
Scott McPhillips [MVP] wrote:

| Peter Simons wrote:
|
| You need a different approcah to avoid hanging in the GUI thread. The
| GUI thread must keep processing messages. It shouldn't block in a
| WaitFor... call. So the whole program, and especially the three calls
| to execute things sequentially, should be event-driven, not three calls
| in series.
|
| You can set up logic to launch the first thread. When it has finished
| it can cause the second to start, and etc. There are two ways: posting
| messages or setting events. Posting messages is the only way to do this
| if the GUI thread is involved. (Setting events is the only way to do
| this if overlapped I/O is involved.)

If I may butt in (I admit I didn't read the rest of the thread carefully),
there's MsgWaitForMultipleObjects API which can help you construct
a message loop with event waiting; I'm having in mind something like:

HANDLE hEvent[3];

hEvent[0] = CreateEvent(..."EventThread1")
hEvent[1] = CreateEvent(..."EventThread2")
hEvent[2] = CreateEvent(..."EventThread3")

for(;;)
{
iRet = MsgWaitForMultipleObjects(3, hEvent, FALSE, INFINITE, QS_ALLEVENTS);
if (iRet >= WAIT_OBJECT_0 && iRet < WAIT_OBJECT_0 + 2)
{
!An event is signalled
}
else
{
!A message was posted
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != WM_QUIT)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
!Clean up here
break;
}
}
}

Since you want a simpler scheme of sequential execution, waiting on only
*one* (instead of 3) hEvent (and/or a hThread) and reusing it subsequently
could be a simpler scheme than the one above.

(Disclaimer: the code above is untested & written off the top of my head;
I meant only to illustrate the principle rather than to provide a ready
solution).

--
Jugoslav
___________
www.geocities.com/jdujic

Please reply to the newsgroup.
You can find my real e-mail on my home page above.

0 new messages