How to cleanly shut down StandardGame?

The StandardGame class confuses me. I can't see how to cleanly shut down the game and know that everything has terminated, as there seems to be no waitable object to that effect.



In fact, there are three synchronization areas that confound me:

  • On start-up it uses sleep in a polling loop to look for being started. Isn't it conceivably possible to race here, letting the GL thread both start AND stop during a scheduling quanta, letting the main thread loop forever?
  • On shutdown, I can't find any synchronization at all. I believe doing a join() on the GL thread might be reasonable, but the structure needs to be carefully examined to avoid deadlock. What's the theory here?
  • Can the futures system lead to deadlock on shutdown? Suppose the main thread is blocking on a future, while the GL thread is shutting itself down and just terminating. What is intended to happen in that case?



    Perhaps I'm just not getting the general design of this class? Here's what it appears to me that it is doing:


  1. You create the StandardApp subclass, and call start() on it.
  2. Start() starts the OpenGL loop running.
  3. The OpenGL loop does both update() and render() on the application (thus those functions are "gl threaded").
  4. The OpenGL loop also updates the GameStateManager. However, I can't find any locking or synchronization calls in GameStateManager or GameStateNode. The samples in the articles discussing StandardGame all create the game states after start(), and happily attaches them in the non-GL thread. Isn't that dangerous?
  5. What is the main thread supposed to be doing in the meanwhile? How can it communicate with the scene graph in a thread-safe way?



    In general, when using a threaded scene graph, I would expect to see some double-buffering of state, or other hand-off between one thread that simulates/updates, and one thread that renders. However, that doesn't appear to be what the current design is.



    So… I'm not getting it. What is the main thread expected to do in StandardGame? And how can it cleanly re-join the GL thread when everything is said and done?

That's what I observed as well - StandardGame does not seem to terminate cleanly (at least the tests using it do not). Maybe darkfrog (the author of that class) can shed some light on this…



Welcome on the jME boards, jwatte - h+, right? - going to use jME Physics? :slight_smile:

something wrong with System.exit(0);?

System.exit() is not always the preferred mechanism.  First of all it's a forced close and ideally you want to terminate "gracefully". Further, if you have anything else going on…like a menu that is outside of the game that you want to appear when StandardGame terminates then you'd kill that as well by calling System.exit().



It used to work…not sure what exactly has changed but I'll try to get a look at it and see what's hanging on.  I know that if you use JMEDesktop with Substance it seems to create a thread that clings to life despite your efforts to tell it you don't want it anymore.  Apart from that I haven't seen this happen though…if someone else gets a chance to look at it before me I'd appreciate the assistance. :slight_smile:

Can you answer the questions from 4) and 5), darkfrog? To me it really seems to be 'dangerous' to modify the scenegraph from the main thread if it's not the gl-thread (as our scenegraph is no generally thread-safe…

The update calls are made inside the OpenGL thread, but attaching GameStates to the GameStateManager can and most commonly does occur in my code outside the OpenGL thread.  I haven't had any problems with this, but to be perfectly secure you can use game.lock() before you do anything that is going to modify the scenegraph and game.unlock() when you're done.  However, it's always best to do something like:



game.lock();
try {
    ... code to work with the scenegraph ...
} finally {
    game.unlock();
}



As otherwise you run the risk of leaving your game locked and essentially unresponsive if an exception is thrown or you call "return" at some point in your code.  This can actually gain you a great deal in performance as you could build your entire GameState and then simply lock() to attach it to the GameStateManager then unlock() when you're done so you aren't bogging down the OpenGL thread with anything it doesn't need to handle.

The primary focus of StandardGame is to only do the minimum amount of work in the OpenGL thread and do everything else in another thread.

Now, it's important to note here as well that the lock and unlock really are only useful for jME specific scenegraph stuff that doesn't touch LWJGL -> OpenGL as there seems to be something very explicitly requiring those calls to ONLY be made from the OpenGL thread.

Hope that answers your questions.
attaching GameStates to the GameStateManager can and most commonly does occur in my code outside the OpenGL thread


How is this threadsafe?

As long as you don't have things concurrently adding or removing another game state at the same time there is little problems associated with this but considering this better it is probably unwise to do it as there is a slight possibility that things will get a little "wacky" as a result if things happen in just the wrong order.

there is a slight possibility that things will get a little "wacky" as a result if things happen in just the wrong order


The very definition of a hard-to-catch threading bug!

Indeed…but what fun is programming if you don't have hard to find bugs in your code? :wink:

I just made some changes to StandardGame that resolves a possible OpenAL error upon shutdown, but I tested the thread not cleanly shutting down issue and have been unable to reproduce it.  I made a slight modification to TestStandardGame that starts up, runs for 10 seconds then invokes shutdown() on StandardGame and it cleanly shutdown without any errors or hanging threads.



The reason Thread.sleep(1) is used by the way is to yield to other threads as Thread.yield() should do but does not always honor that agreement.



I'm unsure your question on the Futures (sorry, just getting around to answering some of your questions)…

The Thread.yield(1) is a race condition, because the only way to communicate whether the GL thread is running is through a boolean variable. Thus, theoretically, the GL thread could start, set the variable, decide to stop, and clear the variable, all within one scheduling quanta. Thus, the main thread will never see the bool being set, and will spin forever. This is why there are various kernel primitives for threading, such as semaphores, monitors, etc. A semaphore, or other primitive that the main thread can wait on, and the GL thread can signal when it starts running, would be the correct way to do it.



I am using the JMEDesktop to get a Swing GUI into the application, btw; that may have something to do with the problem. When I converted to use SimpleGame, the application quits nicely even with the JMEDesktop stuff running. With StandardGame, it does not.



The problem I can see with futures is this:


  1. GL thread decides it's time to quit
  2. main thread queues a Future, and calls get() on it, which means it'll block until that future is complete
  3. GL thread quits without looping back to the part that serves the message queue of futures



    Now, it seems to me as if the main thread will wait in the get() forever, because nothing will service the message queue. There needs to be some mutual exclusion around that message queue lifetime management, and it needs to be possible for that queue to stop accepting futures, before the final number of work items in that queue are completed, before the GL thread stops.



    No offense intended, but in general, threading is very, very hard. I say this, having spent a 20 year career in writing device debuggers, device drivers, OS subsystems, multi-threaded rendering systems, and other such fun stuff. You should not start using a thread without being extremely cautious of how that thread communicates with the other parts of the system, and with very clean initialization and termination conditions. It seems to me as if the StandardGame plays too fast and loose with the rules for my taste, so I'll go back to a single-threaded game and add my own threads where necessary.

First of all, Thread.sleep(int) and Thread.yield() there is no Thread.yield(1) but I'll just assume that was a typo.



Second, I have yet to have any problems with StandardGame and I have been developing with it for a long time now.



You do have a good point about Futures and perhaps there should be a cleanup() method added to GameTaskQueue that will cycle through anything still waiting to execute interrupt them.



I realize threading can be very hard, but no I don't believe in general StandardGame "plays too fast and loose", but rather than giving up because you seem to think there's a problem perhaps it would be better to offer some fixes since you seem to know so much about threading and there's obviously much we can all learn. :slight_smile:

I already suggested the fixes! Unfortunately, I'm not working out of CVS; I'm working out of the 1.0 jars, so it would be hard to work them up and offer patches. However, given the restrictions imposed (all scene updates have to go through the OpenGL thread to be safe), there's really no benefit to my particular use case in using StandardGame. I could just as easily run the jME in the main thread, and farm out another thread for my true asynchronous tasks.