Mercurial > projects > mde
view mde/init.d @ 8:f63f4f41a2dc
Big changes to init; got some way towards input event support; changed mergetag ID to char[] from uint.
committer: Diggory Hardy <diggory.hardy@gmail.com>
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Fri, 25 Jan 2008 18:17:38 +0000 |
parents | dcb24afa0dce |
children | 1885a9080f2a |
line wrap: on
line source
/************************************************************************************************** * Initialisation setup and exit cleanup module. * * This module controls most of the initialisation and deinitialisation of the program. *************************************************************************************************/ module mde.init; public import mde.exception; import mde.events; import mde.input.input; import mde.input.joystick; // tango imports import tango.core.Thread; import tango.util.log.Log : Log, Logger; import tango.util.log.ConsoleAppender : ConsoleAppender; import tango.stdc.stringz : fromUtf8z; import derelict.sdl.sdl; import derelict.util.exception; /** * Static CTOR * * This should handle a minimal amount of functionality where useful. For instance, configuring the * logger here and not in Init allows unittests to use the logger. */ static this() { // For now, just log to the console: Logger root = Log.getRootLogger(); root.setLevel(root.Level.Trace); root.addAppender(new ConsoleAppender); } static ~this() { } /** * Init class * * A scope class created at beginning of the program and destroyed at the end; thus the CTOR * handles program initialisation and the DTOR handles program cleanup. */ scope class Init { private static Logger logger; static this() { logger = Log.getLogger ("mde.init.Init"); } /** CTOR − initialisation * * Runs general initialisation code, in a threaded manner where this isn't difficult. * * If any init fails, it must run necessary cleanup first since the DTOR cannot be run. */ /* In a single-threaded function this could be done with: * scope(failure) cleanup; * This won't work with a threaded init function since any threads completing succesfully will * not clean-up, and a fixed list of clean-up functions cannot be used since clean-up functions * must not run where the initialisation functions have failed. * Hence a list of clean-up functions is built similarly to scope(failure) --- see addCleanupFct. */ this() { /* Initialisation functions. * * These should each handle a separate area of initialisation such that these functions could * be run simultaneously in separate threads. */ bool initFailure = false; // Set true if something goes wrong and we need to abort. void setFailure () { // For synchronization, although may be unnecessary synchronized initFailure = true; } void delegate() [] initFuncs = [ delegate void() { // Inits SDL and related stuff (joystick). try { // SDL Joystick, used by mde.input DerelictSDL.load(); } catch (DerelictException de) { logger.fatal ("Loading dynamic library failed:"); logger.fatal (de.msg); setFailure (); // abort return; } logger.info ("Derelict: loaded SDL"); if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_JOYSTICK)) { logger.fatal ("SDL initialisation failed:"); char* msg = SDL_GetError (); logger.fatal (msg ? fromUtf8z(msg) : "no reason available"); setFailure (); // abort return; } SDL_SetVideoMode (800, 600, 32, 0); addCleanupFct (&cleanupSDL); logger.info ("SDL initialised"); openJoysticks (); // after SDL init addCleanupFct (&closeJoysticks); } ]; // Start all threads ThreadGroup tg = new ThreadGroup; // can't fail since it does nothing (tango 0.99.4) (unless out of memory − anyway should be safe to throw at this point) try { // creating/starting threads can fail foreach (func; initFuncs) tg.create(func); } catch (Exception e) { // should be a ThreadException, but catch all Exceptions logger.warn ("Caught exception while trying to create threads:"); logger.warn (e.msg); logger.warn ("Will continue in a non-threaded manner."); foreach (func; initFuncs) func(); } // Do some initialisation in the main thread input.loadConfig (); // (may also create instance) addEventsSchedule (); // Wait for all threads to complete. // If something went wrong, we still need to do this before cleaning up. foreach (t; tg) { try { t.join (true); /+ Will only catch thread exceptions; but even so something still went badly wrong so we want the same functionality. } catch (ThreadException e) { // Any threading exception +/ } catch (Exception e) { // Any other exception, i.e. caught from thread. // Relying on catching exceptions thrown by other threads is a bad idea. // Hence all threads should catch their own exceptions and return safely. logger.fatal ("Exception caught during init:"); logger.fatal (e.msg); logger.fatal ("Please report this; it should NEVER happen."); setFailure (); // abort (but join other threads first) } } if (initFailure) { // All cleanup-on-failure must be done here. runCleanupFcts(); // Throw an exception to signal failure and prevent DTOR from also running. throw new initException ("Initialisation failed due to above exceptions."); } } /** DTOR - runs cleanup. * * Currently unthreaded; probably might as well stay that way. */ ~this() { runCleanupFcts(); // if threading, note not all functions can be called simultaeneously } /* Cleanup Functions. * * These may exist simply as something to add to the cleanup list... */ static void cleanupSDL () { SDL_Quit(); } private static { void function ()[] cleanup; // all functions to be run for cleanup // Adding cleanup functions must be synchronized; use: void addCleanupFct (void function () fct) { synchronized cleanup ~= fct; } // Clean-up fcts are run in reverse order to how they're added: void runCleanupFcts () { foreach_reverse (fct; cleanup) fct(); } } } unittest { /* Fake init and cleanup. This happens before the CTOR runs so the extra Init.runCleanupFcts() * call isn't going to mess things up. The extra function called by runCleanupFcts won't cause * any harm either. */ static bool initialised = false; static void cleanup () { initialised = false; } static void init () { initialised = true; Init.addCleanupFct (&cleanup); } init(); assert (initialised); Init.runCleanupFcts(); assert (!initialised); }