Game Design, Programming and running a one-man games business…

Dumb multithreading question (how to safely quit a multithreaded app)

I’m not a multi-threading expert. my main process trundles along nicely then when loading the game it calls CreateThread() to do some loading stuff. Then, while the main thread is rendering my lovely loading screen, and the new thread is loading in data, some evil player hits the top right X button, or alt+F4s the game…

Instant crash!

Unless I add code which calls my thread manager and then calls TerminateThread(). This works…but then the app does not actually properly quit, so it can’t work right. Also… I now read that TerminateThread() is not the way to exit a thread anyway, and I’ve been using it all this time… arghhh!

How do you handle quitting a multithreaded app when the player arbitrarily exist it? Nobody seems to have a definitive answer…

13 thoughts on Dumb multithreading question (how to safely quit a multithreaded app)

  1. In C++ in a custom engine? I don’t know. In Unity (using C#) there’s a method Unity will call when the game is closing, and we have that call Thread.Abort() on AIW’s AI thread. The AI thread will often have already thrown an exception due to other shutdown stuff (causing nulls where nulls do not normally happen, etc) and/or can throw an exception due to its thread getting disposed or whatever, but the AI thread is basically running in a loop so the main “gracefully terminate” solution for us is:

    Wrap the logic of the other thread entirely within a try/catch which catches either everything or, if you can narrow it down, just the exceptions the shutdown process is likely to cause.

    This would generally avoid a crash or error message that would reach your user. Of course, if your other thread is responsible for writing stuff to disk or you otherwise care about how it terminates other than “I don’t care how much it hits the fan in there, don’t bug the user”, that’s more complicated.

  2. Both Alt+F4 and the × button cause a WM_CLOSE to be sent to your main window. Typically, from your message loop, you’d call PostQuitMessage() in response. No forceful termination of anything is going on; it’s all under your applications control. See:

    For thread termination, you’d send some kind of message to the background threads (if they have a message queue), or set a boolean flag to signal exit, that the threads check each time through their main loop (in Java it would need to be volatile, not sure about C++). Then, before starting cleanup in the main thread, you’d wait until all the worker threads have exited gracefully, using WaitForSingleObject().

    (BTW, shouldn’t the form labels on your blog’s comment form be above the fields rather than below them?)

  3. Hmmm, My other threads don’t have loops (they are code to start a new game, load a game or go to the next turn). Something weird must be going on, because normally alt+f4 would quit harmlessly anywhere, so maybe I’ve screwed up something more fundamental. I guess worst case, I could kill the window, and then just wait until all my threads have finished before really exiting…?

  4. soo…indeed it looks like adding
    after PostQuitMessage() does the trick, although it doesn’t quit *immediately*, it does actually quit without error, and this is only for those crazy people who quit the app mid level load anyway :D

  5. Preferably, you’re using lightweight tasks with a threadpool. In your example, instead of loading everything in one really heavy thread, spawn half a dozen threads and tell them all “these are the things to load, but do them one at a time”. When that queue of things is empty, those threads can then shut down gracefully, if you don’t plan on re-using them for your actual gameplay.

    An easy way to handle this early termination is just have your master thread clear out that queue of items. As each worker thread finishes up its current task, it will then shut down and you can exit cleanly.

    If you’re using C++11, the standard library has a really nice implementation of asynchronous tasks with async and future.

  6. It’s been a while since I did any C++ windows coding, but you should be able to intercept the windows close message and handle all your shutdown, including safely closing all your threads.

    If you intercept the windows message you can either process your shutdown and return the correct value to complete the window close, or return a value that causes windows to ignore the close message completely.

    Personally, in a game even in windowed mode, it may be better to remove the close box completely and force the user to follow your own exit process. There’s nothing you can do if they use the task manager to quit the process, but then that is their own problem and if they lose data, so be it.

  7. I’m working with an engine heavily concurrent so I might help:

    0. Which c++ version are you using? 03? 11? Do you have access to boost? (I know you don’t like this kind of library)

    1. Did you try releasing all data that HAVE TO be released no matter what in one of the standard C++ termination handlers? See in particular “terminate()”. I use a terminate handler and it seem to work well but I fixed the case you’re describing in a different way.

    2. In boost.thread they implemented a (controversial) feature called “interruptions” which is basically objects you put at several places in the thread code. Outside, a call to a function in the thread interface allow to trigger ending the thread at one of these interruption points. Maybe you can build something like that, which would look like updating the state of a loading bar. Just have some points where the thread can either continue or stop if a flag is set.

    3. In my case I use TBB’s task scheduler, so most of the work to be done is cut in several tasks. The main thread own a prioritized task queue and schedule/push tasks the task scheduler. However, there is also a specific graphic thread which handle all graphic work (you can push tasks in the graphic system so it is executed in sync with graphic work, mostly to make sure OpenGL work is done in sync).
    With tasks it’s easy to end the work: just don’t reschedule any. With the graphic thread, which initialization can be long (and I suspect it is something like that that you want to interrupt), it is actually cut into several tasks. The graphic system which manage the thread will execute all the tasks one by one but between each execution if will check it is time to stop (an atomic boolean). If it is, then the graphic system will not execute the next task, will just release the data it hold and return the thread. The firs task in the graphic thread is creating the basic graphic engine and the window. As soon as the window is created, another task is executed which setup inputs, which mean the user can press exit. But even if he presses exist immediately, the current tasks have to be finished before the game can exit. As most of my tasks are fine-grained, it is mostly quick to exit even while loading at the beginning, but it’s not always immediate (because of the graphic init which takes time). It’s ok in my case.
    I don’t think you the same setup than me but I’m explaining this so that you might be inspired by how I did solve this problem (having work fine grained in tasks which can not be executed if the game have to exit). Obviously, in doubt, it is coupled whith error handling functions I talked about in 1). Note that my game is not finished so maybe I will encounter other problems with this sytem but I don’t have any so far (but it took me a while to figure how to set it up correctly).

  8. Although my problem is effectively fixed, I find all multithreading discussion very interesting, especially in terms of how I might use threads better in my next game, which will have more need for them. D3 only really uses threads for pretty loading screens.

  9. It’s a subject that interest me too a lot because I want to be able to setup games which performance scale as much as possible with current and future hardware. Part of why I have setup my game this way is for concrete experience with concurrency in games, part is because I need this game to do a lot of things in parallel and graphics is not the bottleneck.

  10. Well in my opinion when the house is about to be destroyed I don’t tidy up the kitchen. Usually when a process is about to terminate I just let it terminate. I’ve never found it worth the effort to try to “cleanly” terminate since none of it matters, although I guess if you want to prevent a crash message then you could just wrap a handler around everything that swallowed any exceptions that bubble up. Just my $.02, or whatever the British equivalent would be.

  11. Just my $0.02, but any calls such as TerminateThread() should never be used unless you don’t care about a graceful exit.

    Each thread should have a transition from its running state through cleanup and finally to its exit state. During that transition, it properly closes any opened resources. Obviously, some threads have no opened resources of their own, so the clean-up code can be empty.

    When the program is terminating (via File-Exit, Closing Window, etc.) it should signal all threads of that intention, then wait for them all to exit gracefully.

    One use case is the “Save-and-exit” action, where you need to save the current progress, and then exit. The instant-exit action should be just like that, without the saving part.

  12. Every platform I’ve ever worked on sends you a signal before exiting, to give you a chance to clean up your mess first. Since you’ve indicated you’ve pretty much solved the issue, however, I won’t say more than that about the subject.

    I find multi-threading to be a highly interesting topic, and I’ve been playing around a lot with multi-threaded code in C++. The details of multi-threading tends to differ depending on the platform, and I like my software to be cross-platform, so I try to stick to cross-platform threading libraries.

    I’ve been using boost threads a lot, but I recently stumbled across the Poco C++ libraries. They have a pretty good multi-threading implementation and I like that they have a good thread pool implementation as well. I’m considering trying that out to see if I like it better than boost threads.

    I took a class back when I was still in college that went into detail regarding multi-threading and multiple processes working together, so I got a good overview of the issues that can appear. So although I was well warned about issues that can appear like deadlocks and race conditions, I still found that in practice multi-threading can be tricky to get right. I’ve found out through debugging my multi-threaded code that all sorts of interesting things can happen that I hadn’t anticipated.

    It’s definitely tricky to get right, so make sure you read up on the issues before starting on any serious multi-threaded code and then prepare to learn from experience.

  13. yes its true that effective debugging is the real nightmare of developing multi-threaded stuff. this is why I am easing into it with relatively petty stuff like loading and turn-based processing code, before attempting to sue it in a more general way in real-time for any future games.
    I also like to clean up my code nicely one xit, and now all that TerminateThread stuff is gone :D

Comments are currently closed.