🔒 Locked
Multi-threading Question
Advertisement
Original Post
Ok, I''ve searched around this forum (and a few others) and gone through the tutorials I''ve found on the net. I haven''t quite found what I''m looking for, though.
I''m doing a test with multi-threading (so no "don''t use multi-threading" messages, please...I can''t believe HOW many I''ve read tonight already) to separate the message pump and the "game" loop. I read the tutorial here on Gamedev on how to do it, but it doesn''t seem to work with my implementation. I''m hoping someone here can either tell me what I''m doing wrong or confirm my suspicions.
I start off by creating a thread that runs my RunGame function and then exits. The RunGame function just does a loop where it does stuff until the ESC key is pressed. The OpenGL window is created in that second thread (if I create it in the main thread, I can''t render to it). It will loop through and render properly, but it''s happily going along while ignoring any windows messages that come to it. This problem goes away if I put the message processing routines (PeekMessage, etc.) into RunGame''s loop. That isn''t what I''m going for.
I''ve tried using the OpenGL window''s hWnd as a parameter for the main thread''s GetMessage or PeekMessage (tried both, only difference seems to be rendering performance). It still doesn''t get the messages, so I''m thinking messages can''t be gotten across threads (even with the proper window handle).
After reading a lot (that search is HANDY!) of posts about message pumps and multi-threading, I''ve noticed some posts from people who are able to separate the message pump out and have it work.
Anyone know what I might be missing? Or, if I can''t get messages from windows in other threads, anyone have a workaround that might work?
Thanks in advance!
The first thread should create the window and start the pump (it''s a good idea to have the thread that creates a window, pump it, and have that thread be the first one).
In one of the first message handlers (after WM_CREATE), create the second thread. You want to make sure the window is alive and valid before handing it off to the GL renderer.
Make a simple window app first, make sure it works, then add the second thread. In the first thread you can now hang in the message pump using GetMessage (this will keep that thread idle until it has work to do as well).
GL may need to be told it''s being used in a MT app in the initialization functions.
Render to the window in the second thread - this needs to work this way.
You have mild syncronization issues for window resizing, moving, and GL blitting. You can worry about those later, it shouldn''t crash, but it might ''bleed'' over the window''s border.
Also put a small sleep in the render thread, Sleep(0) or Sleep(1) is good. You might be starving the message pump thread... you can also try increasing the priority of the pump thread and decreasing the priority of the render thread.
Magmai Kai Holmlor
"Oh, like you''ve never written buggy code" - Lee
"What I see is a system that _could do anything - but currently does nothing !" - Anonymous CEO
In one of the first message handlers (after WM_CREATE), create the second thread. You want to make sure the window is alive and valid before handing it off to the GL renderer.
Make a simple window app first, make sure it works, then add the second thread. In the first thread you can now hang in the message pump using GetMessage (this will keep that thread idle until it has work to do as well).
GL may need to be told it''s being used in a MT app in the initialization functions.
Render to the window in the second thread - this needs to work this way.
You have mild syncronization issues for window resizing, moving, and GL blitting. You can worry about those later, it shouldn''t crash, but it might ''bleed'' over the window''s border.
Also put a small sleep in the render thread, Sleep(0) or Sleep(1) is good. You might be starving the message pump thread... you can also try increasing the priority of the pump thread and decreasing the priority of the render thread.
Magmai Kai Holmlor
"Oh, like you''ve never written buggy code" - Lee
"What I see is a system that _could do anything - but currently does nothing !" - Anonymous CEO
Like Magmai said, wait till you catch your WM_CREATE message then, create your thread.
Althought there afew other solutions also, am assumin you want to run OGL in its own thread and when windows executes a certain message you want to check for it and take appropriate action?
If you know C++ well enough you can use whats called a listener patern and have ogl listen for certain events and take action from there... It not that important to have high performance on catching windows events, more important catching input events, playing sound and plastering billions and billions of polygons on screen
Althought there afew other solutions also, am assumin you want to run OGL in its own thread and when windows executes a certain message you want to check for it and take appropriate action?
If you know C++ well enough you can use whats called a listener patern and have ogl listen for certain events and take action from there... It not that important to have high performance on catching windows events, more important catching input events, playing sound and plastering billions and billions of polygons on screen
Also, you can create your rendering context on the main thread, but in order to render to the window from the worker/rendering thread, you must call wglMakeCurrent() with the render context associated with the window you created.
If you weren''t calling wglMakeCurrent() from the second thread, then that''s why when the window is created on the first thread, you can''t render to it.
-Brannon
If you weren''t calling wglMakeCurrent() from the second thread, then that''s why when the window is created on the first thread, you can''t render to it.
-Brannon
Thanks for the responses. I''m working on implementing some changes now.
This was a simple, working app before I started playing around with it.
I''ll get back to you all with the results of my typing.
This was a simple, working app before I started playing around with it.
I''ll get back to you all with the results of my typing.
Ok, now THAT part''s running. I can''t get GetMessage to work yet, though. I''m still using PeekMessage because GetMessage won''t let my loop finish to get to where I''m checking to see if the thread is done. I''ve tried moving the checking code, but it doesn''t make a difference. Any ideas on how to get around that?
The only other problem I have is that going from fullscreen to windowed and back isn''t going to be nearly as easy. I''ll work on a way of synchronizing that sometime tomorrow night.
Thanks all!!
The only other problem I have is that going from fullscreen to windowed and back isn''t going to be nearly as easy. I''ll work on a way of synchronizing that sometime tomorrow night.
Thanks all!!
I''ve got two threads, one for the window, one for OpenGL rendering as well. I plan on adding a third and fourth threads for user input and background loading of expected resources (models, maps, etc...) No problems except for resizing the window, but that''s not very crucial and I should have it fixed soon.
At first I had problems exiting the program. What I did was a made a global boolean g_Exit, and when g_Exit is true, it exits both threads. What I found VERY strange is that even though I created the window in the main thread, I had to destroy it in the rendering thread.
Currently I''m calling CreateThread directly after I make the window, I might need to change where I''m calling it from. All options in CreateThread are set to 0 except for the mandatory ones.
At first I had problems exiting the program. What I did was a made a global boolean g_Exit, and when g_Exit is true, it exits both threads. What I found VERY strange is that even though I created the window in the main thread, I had to destroy it in the rendering thread.
Currently I''m calling CreateThread directly after I make the window, I might need to change where I''m calling it from. All options in CreateThread are set to 0 except for the mandatory ones.
Well, I''m thinking my problem has something to do with the close message getting sent to the wrong thread (I''ll try PostThreadMessage when I get home). I use a variable to tell me when the thread is done, which worked in an earlier test that I tried.
As for destroying the window in the second thread, I tried it but it wouldn''t unregister the class. I tried destroying it in the main thread and it destroyed it without any problems.
I''m at work for another hour so I won''t be able to try anything until then, though. I''m hoping that''s all it is.
As for destroying the window in the second thread, I tried it but it wouldn''t unregister the class. I tried destroying it in the main thread and it destroyed it without any problems.
I''m at work for another hour so I won''t be able to try anything until then, though. I''m hoping that''s all it is.
Ok this is good, my code works perfectly now. It suspends and resumes the rendering thread exactly the way I want it to, shutdowns just fine and everything.
Just to make sure it was all perfect, I searched in the docs for threads, and found the ExitThread() function. Do I actually have to call this function to properly shutdown my program? Currently it just calls CloseHandle() on the thread with no problems, and when I call ExitThread() and I close the program, it is still running in the background without a window! (I have to close it with task manager). The docs aren''t very clear on how to manage threads, it just says that ExitThread() "exits the thread" =/.
Just to make sure it was all perfect, I searched in the docs for threads, and found the ExitThread() function. Do I actually have to call this function to properly shutdown my program? Currently it just calls CloseHandle() on the thread with no problems, and when I call ExitThread() and I close the program, it is still running in the background without a window! (I have to close it with task manager). The docs aren''t very clear on how to manage threads, it just says that ExitThread() "exits the thread" =/.
Well, I got my close button working. I had ESC jump out of the main loop, and it would destroy the window properly, but the close button wouldn''t (because WM_CLOSE was posting a quit message instead of telling the thread to close). So, I moved my "game loop keep running" variable into the message handler and made my ESC key checker just post a WM_CLOSE message instead of setting the variable itself. I figure it''s MORE threadsafe that way.
Also, I think I''ll look into that wglMakeCurrent, as Brannon suggested. To make the window and the contexts independently I had to split up my Init and End functions, and I''d like to pull those back together if I can.
Also, I think I''ll look into that wglMakeCurrent, as Brannon suggested. To make the window and the contexts independently I had to split up my Init and End functions, and I''d like to pull those back together if I can.
The worker thread should should poll either a flag or an event to determine when to exit each loop (or every nth loop). The main message pumping thread should set the flag/signal the event to tell the worker thread to exit (in say the WM_CLOSE message handler). After the primary thread sets this flag/event it should WaitForSingleObject(m_WorkerThreadHandle, 5000); If the wait succeeds, it means the worker thread has terminated, and it''s safe to exit the primary thread. If the wait fails, it''s time for baby-seal technology - TerminateThread(m_WorkerThreadHandle); Then exit the primary thread/message pump.
If you want it to exit when you hit ESC, have the input thread PostQuitMessage(hWnd, 0), and everything will come down like a ton of bricks.
If you want it to exit when you hit ESC, have the input thread PostQuitMessage(hWnd, 0), and everything will come down like a ton of bricks.
I played around with wglMakeCurrent a bit but it didn''t want to work as I had my functions set up. It returned with a "BUSY" error, so I''m thinking it''s because I needed to release it before I tried making it current.
So, rather than try playing with that even more (putting a couple of my init functions BACK together started giving weird errors, so that contributed to my decision) I''ll stick with creating the windows separately from the device contexts.
I did get my fullscreen stuff working again, too (after a lot of quick CTRL-ALT-DEL to get to my task manager
). I ended up going with a toggle-fullscreen flag that I set to true in the game loop. I then post a close message so the game loop exits out and destroys the window. I put the main while loop inside a do-while loop that will check for the toggle-fullscreen flag. I had to throw in some extra checks and such (to keep it from looping indefinitely, giving error message boxes all the time, and to keep it from exiting because the GameThreadDone value was already true...)
It''s been VERY interesting. Thanks for your help, everyone. Special kudos to you, Magmai!
I got my Mutex class completed a little while ago, so I''m going through and protecting some of the stuff my wndproc hits.
Later all!
So, rather than try playing with that even more (putting a couple of my init functions BACK together started giving weird errors, so that contributed to my decision) I''ll stick with creating the windows separately from the device contexts.
I did get my fullscreen stuff working again, too (after a lot of quick CTRL-ALT-DEL to get to my task manager
It''s been VERY interesting. Thanks for your help, everyone. Special kudos to you, Magmai!
I got my Mutex class completed a little while ago, so I''m going through and protecting some of the stuff my wndproc hits.
Later all!
quote:
Original post by Dragonskin
It''s been VERY interesting. Thanks for your help, everyone. Special kudos to you, Magmai!
I got my Mutex class completed a little while ago, so I''m going through and protecting some of the stuff my wndproc hits.
Later all!
Welcome
One last thing, Win32 Mutexes are for inter-processes synchronization and Critical Sections are for inter-thread synchronization (they''re an order of magnitude faster than the mutex, so they''re preferable). Both work like traffic lights, just have different scopes.
Magmai Kai Holmlor
"Oh, like you''ve never written buggy code" - Lee
"What I see is a system that _could do anything - but currently does nothing !" - Anonymous CEO
Ok. Cool. I''m gonna have to play with those as well 
You really shouldn''t ever call TerminateThread(). If your thread is hanging on exit, then fix what is causing the hang. Don''t hide it by killing the thread. TerminateThread() kills the thread "with extreme prejudice". That means that DllMain(DLL_THREAD_DETACH) doesn''t get called, etc. Rather than kill the thread that way, you can just let it sit there. When you go to exit the process the system will take care of destroying the thread.
Calling ExitThread() will exit the current thread. If you call this on your main thread, your process will shutdown. You don''t actually need to call ExitThread(). Just ''return'' from the thread function, and the system call (in kernel32.dll) that started your thread will take care of shutting it down. Also, if you use any CRT functions in your threads, use _beginthreadex() (not CreateThread).
If you are processing messages in your thread function and you want to be able to wait on a shutdown event, then use MsgWaitForMutlipleObjects(), and specify that you want to be woken up on all messages (then use PeekMessage() to retrieve the message). If your are woken up because the event is signalled, then you can just exit the thread.
-Brannon
Calling ExitThread() will exit the current thread. If you call this on your main thread, your process will shutdown. You don''t actually need to call ExitThread(). Just ''return'' from the thread function, and the system call (in kernel32.dll) that started your thread will take care of shutting it down. Also, if you use any CRT functions in your threads, use _beginthreadex() (not CreateThread).
If you are processing messages in your thread function and you want to be able to wait on a shutdown event, then use MsgWaitForMutlipleObjects(), and specify that you want to be woken up on all messages (then use PeekMessage() to retrieve the message). If your are woken up because the event is signalled, then you can just exit the thread.
-Brannon
It was kind of interesting last night trying to figure out why I suddenly started having problems going between fullscreen and windowed mode (occasionally, it would just exit the program). Finally traced it back to the window handle not being valid in time for the second thread to start again (and that''s AFTER the WM_CREATE has been sent, interestingly enough). I decided to put the second thread to sleep for 5 seconds and then try doing the init again. If it fails the second time it''ll give me a message box.
As for the GetMessage/PeekMessage problem I was having...well, I still am. I did come up with a work-around for the moment. Since I have an "Active" variable for my program, I just check for that in my main loop. If the program isn''t active it''ll call WaitMessage. That way at least when I minimize my CPU usage goes down to almost nothing. Anyone see a problem with this method, or should I be ok?
And I got critical sections put in. Lucky me I designed the Mutex class with a simple on/off interface that worked well with critical sections as well. Change the class type on all of the mutexes and clear sailing afterwards.
As for the GetMessage/PeekMessage problem I was having...well, I still am. I did come up with a work-around for the moment. Since I have an "Active" variable for my program, I just check for that in my main loop. If the program isn''t active it''ll call WaitMessage. That way at least when I minimize my CPU usage goes down to almost nothing. Anyone see a problem with this method, or should I be ok?
And I got critical sections put in. Lucky me I designed the Mutex class with a simple on/off interface that worked well with critical sections as well. Change the class type on all of the mutexes and clear sailing afterwards.
What problem are you having with GetMessage/PeekMessage?
WaitMessage is essentially doing the same thing as GetMessage, except that I don''t think WaitMessage retrieves the message for you, it just wakes up the thread.
If you don''t want your message loop to chew up all of your resources, then don''t use PeekMessage. Use GetMessage instead. If there are no messages pending, then GetMessage will block (put the thread to sleep).
-Brannon
WaitMessage is essentially doing the same thing as GetMessage, except that I don''t think WaitMessage retrieves the message for you, it just wakes up the thread.
If you don''t want your message loop to chew up all of your resources, then don''t use PeekMessage. Use GetMessage instead. If there are no messages pending, then GetMessage will block (put the thread to sleep).
-Brannon
Ok, the problem I''m having with GetMessage is that it is preventing my main thread from closing because it never gets the last message.
I do a check on a GameThreadDone variable which is set to true when the game thread is done (and then the thread returns 0). I''m having the thread post a close message as well, which worked fine when I was doing testing before. If I use GetMessage, it never gets down to where it''ll post the quit message because the thread is done. Peekmessage allows me to get down further in the loop.
And yes, I realize that WaitMessage doesn''t get a message, just waits for it. That''s why I''m using it if the program isn''t "Active"...it''ll wait until a message comes, and then my message handler loops through and takes care of it.
It''s not that it doesn''t WORK, I''m just trying to figure out why GetMessage doesn''t get my last close message.
I do a check on a GameThreadDone variable which is set to true when the game thread is done (and then the thread returns 0). I''m having the thread post a close message as well, which worked fine when I was doing testing before. If I use GetMessage, it never gets down to where it''ll post the quit message because the thread is done. Peekmessage allows me to get down further in the loop.
And yes, I realize that WaitMessage doesn''t get a message, just waits for it. That''s why I''m using it if the program isn''t "Active"...it''ll wait until a message comes, and then my message handler loops through and takes care of it.
It''s not that it doesn''t WORK, I''m just trying to figure out why GetMessage doesn''t get my last close message.
That''s very odd... let''s see some code (snipped please
)
I usually add an ASSERT if I need to resort to baby-seal technology, but it''s more desirable to TerminateThread than hang the program, in a worse-case-scenario. (Fixing the root problem as soon as it''s discovered is a very good idea though.)
Magmai Kai Holmlor
"Oh, like you''ve never written buggy code" - Lee
"What I see is a system that _could do anything - but currently does nothing !" - Anonymous CEO
I usually add an ASSERT if I need to resort to baby-seal technology, but it''s more desirable to TerminateThread than hang the program, in a worse-case-scenario. (Fixing the root problem as soon as it''s discovered is a very good idea though.)
Magmai Kai Holmlor
"Oh, like you''ve never written buggy code" - Lee
"What I see is a system that _could do anything - but currently does nothing !" - Anonymous CEO
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement