view mde/init.d @ 6:dcb24afa0dce

Some fixes from mde/text/format.d unittests plus a few more fixes. committer: Diggory Hardy <diggory.hardy@gmail.com>
author Diggory Hardy <diggory.hardy@gmail.com>
date Thu, 10 Jan 2008 18:33:24 +0000
parents 9a990644948c
children f63f4f41a2dc
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;

import mde.exception;

import mde.input.input;

// tango imports
import tango.core.Thread;
import tango.util.log.Log : Log, Logger;
import tango.util.log.ConsoleAppender : ConsoleAppender;

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
{
    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 this fails by throwing an exception, 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()
    {
        // Start all threads
        ThreadGroup tg = new ThreadGroup;
        tg.create(&initSDL);
        
        // Do some initialisation in the main thread
        Input.instance.loadConfig (0);
        
        // Wait for all threads to complete
        try {
            tg.joinAll (true);		// rethrows any exceptions
        }
        catch (initException e) {	// Any problems?
            // All cleanup-on-failure must be done here.
            runCleanupFcts();
            throw e;	// Rethrow. Warning: if multiple threads throw exceptions, only one gets returned.
        }
        
    }
    
    /* Initialisation functions.
    *
    * These should each handle a separate area of initialisation such that these functions could
    * be run simultaneously in separate threads. */
    void initSDL () {
        try {
            // SDL Joystick, used by mde.input
            DerelictSDL.load();
        } catch (DerelictException de) {
            throw new DynamicLibraryLoadException (de.msg);
        }
        logger.info ("Derelict: loaded SDL");
        
        SDL_Init (SDL_INIT_TIMER | SDL_INIT_JOYSTICK);
        addCleanupFct (&cleanupSDL);
        logger.info ("SDL initialised");
    }
    
    ~this()
    {
        runCleanupFcts();
    }
    
    /* 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);
}