Skip to main content
GameDev.net gamedev.net
🔒 Locked

multithreading and killing processes

Started by okonomiyaki Apr 27, 2003 at 3:19 PM 9 replies 550+ views
Original Post
okonomiyaki
okonomiyaki
My program used to be a single thread (win32 application) that ran an infinite loop :
ZeroMemory(&Msg, sizeof(MSG));
while(Msg.message != WM_QUIT) {
	if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	else {
			if(mymain.DrawFrame() == FALSE) break;
	}

}
 
with the message handling:

long FAR PASCAL WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
	switch(uMsg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hWnd, uMsg, wParam, lParam);

};

 
When I pressed "esc" or clicked the "X" button it closed the program and killed the process. I''ve added multithreading to my program though. Basically the program instanstiates a thread that loads all of the data, and while that thread is running it shows a title screen and a progress bar. Now my infinite loop looks like this:

ZeroMemory(&Msg, sizeof(MSG));
while(Msg.message != WM_QUIT) {
	if(PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE)) {
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}
	else {
		if(mymain.IsRunning()) mymain.RenderTitleScreen();
		else {
			if(mymain.DrawFrame() == FALSE) break;
		}
	}
}

 
IsRunning() is a function that returns true if the loading thread is still running. So that''s it, when the thread is done it calls CloseHandle(d_threadhandle) and the game continues normally. But now whenever I close the program, the window closes but the process does not terminate!!! This is VERY annoying as every time I run my program I have to go into the task manager and close the process myself. Any ideas why the process isn''t killing itself when it was before? Thanks.
Toolmaker
Toolmaker
I don't know exactly what you mean. Are you saying, that when you close the program while running the loading thread the thread doesn't not terminate? Inside the WM_CLOSE, do this:


          
case WM_CLOSE:
if (mymain.IsRunning)
mymain.Terminate(); // assuming mymain is the thread class

return (0);



If you don't use a thread class, I have one for you if you like. It consists from 2 classes, CThread and CMutex. The CMutex class holds a CRITICAL_SECTION so you can lock data. You can download both classes here. It is a RAR file and contains the source to both classes. Usage below:


  
CThread Thread;
CMutex Mutex;

// Create thread

Thread.Create(ThrFunc, (void *)Data);

// Lock data, change it, and unlock

Mutex.Lock();
Data->SomeVariable = X;
Mutex.Unlock();

// Kill thread

Thread.KillThread();


You can also suspend the thread, wait till it ends and get it's ID. Hope it helps.

Toolmaker

EDIT: Messed up some HTML/source tags



-Earth is 98% full. Please delete anybody you can.

[edited by - toolmaker on April 27, 2003 5:01:45 PM]
 
CWizard
CWizard
Ehm, do not use TerminateThread() to end the thread, it causes leaks.

How does your thread end itself? Usually you have some way to signal the thread to close from the main thread, then wait for it to exit and then close its handle. You should not Terminate() it, unless as a last resort if it isn''t exiting itself in a certain amount of time.
okonomiyaki
okonomiyaki
quote:
Original post by Toolmaker
I don''t know exactly what you mean. Are you saying, that when you close the program while running the loading thread the thread doesn''t not terminate? Inside the WM_CLOSE, do this:




It doesn''t kill itself whenever I close it, before or after the thread ends.

quote:



If you don''t use a thread class, I have one for you if you like. It consists from 2 classes, CThread and CMutex. The CMutex class holds a CRITICAL_SECTION so you can lock data. You can download both classes here. It is a RAR file and contains the source to both classes. Usage below:



I did wrap it up in my own thread class. Thanks though, it''s pretty similar to the one you posted.

quote:

You can also suspend the thread, wait till it ends and get it''s ID. Hope it helps.



That''s kind of what I do. If I call end thread before it ends, it waits for it to finish, then closes the handle.

quote:

How does your thread end itself? Usually you have some way to signal the thread to close from the main thread, then wait for it to exit and then close its handle. You should not Terminate() it, unless as a last resort if it isn''t exiting itself in a certain amount of time.



This is the end() function in my thread class:

  

void cThread::End()
{

if( d_threadHandle != NULL )
{
d_bIsRunning = false;
WaitForSingleObject( d_threadHandle, INFINITE );
CloseHandle( d_threadHandle );
d_threadHandle = NULL;
}

}


I''m not worrying about terminating it in the middle of it yet. I want to get this working first, so lets just say wait for it to finish then close.
At the end of the function that the thread is executing, it calls this->end(); the while loop in the main program recognizes it''s finished, and continues.
The only thing I do to close the thread is CloseHandle after it''s finished. is there anything else? I don''t have to TerminateThread or something else when it''s already done do I?
CWizard
CWizard
quote:
Original post by okonomiyaki The only thing I do to close the thread is CloseHandle after it''s finished. is there anything else? I don''t have to TerminateThread or something else when it''s already done do I?
CloseHandle() doesn''t do anything unless the thread has ended. The thread itself must either return from the "thread entry function" or call ExitThread() on itself. When it has done that, the thread does still exist, even though it has finnished executing. When you call CloseHandle(), the thread is removed.

What sort of stuff is this "child" thread doing? An endless loop? A finite task? Handle events?
okonomiyaki
okonomiyaki
But if the end() function is called, it waits until it has finished with the waitforsingleobject() method, right? so when it reaches CloseHandle, the thread should be done.
The ''child'' thread is setting everything up for a 3d game. Creating vertex buffers, generating height maps, generating the sky, setting parameters on the Direct3D device, loading in textures, etc. It takes a while, but it is a finite task.
CWizard
CWizard
I'm not sure about the wairforsingleobject() function, I would need to do some research.

In the "child" thread, you should have some flag that can be set by the main thread and cause the "child" thread to abort. The last thing it should do is call ExitThread().

In the main thread you should "poll" to see if the "child" thread has exited, then call CloseHandle(). One way is like this:
unsigned long ec;
if(GetExitCodeThread(thread_handle, &ec) && ec != STILL_ACTIVE) {
CloseHandle(thread_handle);
// Whatever else is to be done when it is done.
}
I don't understand you problem clearly. It is pretty simple: make sure that the child thread has exited, then call CloseHandle() and everything should work fine .

[edited by - CWizard on April 27, 2003 9:16:17 PM]
okonomiyaki
okonomiyaki
Heh, ok, thanks a lot for the help I could use the GetExitCodeThread() function.. I hadn''t researched that before. Thanks!

But if what you said is true, that if the thread if finished and you call CloseHandle() then everything should be fine, then everything should be fine.
I''ll try to clearly state my problem

Lets just assume that my program does what you say it should:
Begin the thread
Finish the thread
CloseHandle(thread);

I''m 100% sure that my program works that way. My problem is that whenever I close the program (no matter where it is) the window closes but the process stays open so when I run task manager I see my program listed in the processes.

It didn''t used to do that before I added that extra thread (that is handled by the 3 steps mentioned above I am sure). That''s the weird part :/
CWizard
CWizard
The problem is most likely due to the spawned thread is still active. That is your main thread has exited, but the child hasn''t, so your process is still alive.

Hmm, I read the original post, where you say "when the thread is done it calls CloseHandle(d_threadhandle)". Do you mean that the thread itself calls CloseHandle()? If so, that would be a problem.

Then, don''t call CloseHandle() on the thread before you know that it has exited, otherwise I think it might useless. At least for testing purposes, call GetExitCodeThread() before CloseHandle() to verify that it really has exited.


okonomiyaki
okonomiyaki
CWizard,
thank you very much Your thought-provoking posts led me to my error, and after a little fiddling around I fixed it.
I had speculated that the process was still open because the thread was still open, but I then I thought that couldn't be because of the simple way I was killing the child thread (wait for it to end, then closehandle it). But here's my error.
At the very end of my thread function, I call thread->end(); so it looks like this:


DWORD func() {

...

cThread->end();

}


guess what? That child thread is calling end(), then going into the inifinite waiting for itself to finish, therefore it never finishes!! Plus, if it did get to the CloseHandle method, you said yourself that the child calling that itself could be a problem.. A little debugging with GetExitCodeThread showed me this. I now use GetExitCodeThread and the main process continually checks this and when it finds it's done, it closes it. Thanks a lot for your help!! You were really smart to see this problem!



[edited by - okonomiyaki on April 27, 2003 11:24:35 PM]
CWizard
CWizard
All well then. Multi-threading is a two edged sword; it can do wonders, but if anything goes wrong it''s hard as hell to track down.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.