diff mde/setup/Init.d @ 88:01f4f5f1acc9

Changes to init and to allow compiling with gdc. Tweaked init code to allow using circular iterators (disabled until my patch makes it into tango). Changes to allow compiling with gdc. Building is successful and unittests complete, but in my experience a SIGSEGV occurs within SDL.
author Diggory Hardy <diggory.hardy@gmail.com>
date Mon, 29 Sep 2008 12:09:44 +0100
parents 79d816b3e2d2
children 97e6dce08037
line wrap: on
line diff
--- a/mde/setup/Init.d	Tue Sep 23 12:31:48 2008 +0100
+++ b/mde/setup/Init.d	Mon Sep 29 12:09:44 2008 +0100
@@ -51,7 +51,7 @@
 import tango.core.Thread;
 import tango.core.sync.Condition;
 import tango.core.Exception;
-import tango.util.container.LinkedList;
+import tango.util.container.CircularList;
 
 //import tango.stdc.stringz : fromStringz;
 import tango.io.Console;	// for printing command-line usage
@@ -235,7 +235,7 @@
     
     // run init stages or cleanup if startup is false
     private static void runStages(bool startup) () {
-        LinkedList!(InitStage*) toRun = new LinkedList!(InitStage*);
+        CircularList!(InitStage*) toRun = new CircularList!(InitStage*);
         foreach (v; stages) {
             // Filter only stages with the relevant delegate. It is not checked later that the
             // delegate exists!
@@ -257,8 +257,10 @@
                 }
             }
         }
+        auto toRunIt = toRun.iterator;
         int numWorking = miscOpts.numThreads;
-        bool abortInit = false;
+        enum STATE {    WORKING = 0,    DONE = 1,       ABORT = 2 }
+        STATE doneInit = STATE.WORKING;
         Mutex toRunM = new Mutex;       // synchronization on toRun, numWorking
         Condition toRunC = new Condition(toRunM);       // used by threads waiting for remaining stages' dependencies to be met
         
@@ -275,74 +277,84 @@
          * When notified, threads start at 1. */
         void initThreadFct () {
             try {       // created as a thread - must not throw exceptions
-            InitStage* stage;
-            
-            threadLoop: while (true) {      // thread loops until a problem occurs or nothing else can be done
-            // Look for a job:
-            synchronized (toRunM) {
-                --numWorking;           // stopped working: looking/waiting for a job
-                if (abortInit) break threadLoop;  // something went wrong in another thread
-                static if (startup)
-                    int num_rdepends = (stage is null) ? 0 : stage.rdepends.length;
-                else
-                    int num_rdepends = (stage is null) ? 0 : stage.depends.length;
+                InitStage* stage;
                 
-                getStage: while (true) {
-                    auto it = toRun.iterator;   // asserts if toRun is empty
-                    itStages: while (it.next (stage)) {   // get next element of toRun
-                        static if (startup) {
-                            foreach (d; stage.depends)
-                                if (stages[d].state != StageState.ACTIVE)
-                                    continue itStages;  // dependency isn't met (yet)
-                        } else {
-                            foreach (d; stage.rdepends)
-                                if (stages[d].state != StageState.INACTIVE)
-                                    continue itStages;  // reverse dependency isn't unmet (yet)
+                threadLoop:
+                while (true) {      // thread loops until a problem occurs or nothing else can be done
+                    // Look for a job:
+                    synchronized (toRunM) {
+                    --numWorking;           // stopped working: looking/waiting for a job
+                    if (doneInit) break threadLoop;  // something went wrong in another thread
+                    
+                    static if (startup)
+                        int num_rdepends = (stage is null) ? 0 : stage.rdepends.length;
+                    else
+                        int num_rdepends = (stage is null) ? 0 : stage.depends.length;
+                    
+                    getStage:
+                    while (true) {
+                        static if (false)       // An addition to CircularList to allow continuous circular iteration; not yet in tango.
+                            toRunIt.start;                  // start circling from here
+                        else
+                            toRunIt = toRun.iterator;       // new iterator
+                        itStages:
+                        while (toRunIt.next (stage)) {   // get next element of toRun
+                            debug logger.trace ("Iterating: {}", stage);
+                            static if (startup) {
+                                foreach (d; stage.depends)
+                                    if (stages[d].state != StageState.ACTIVE)
+                                        continue itStages;  // dependency isn't met (yet)
+                            } else {
+                                foreach (d; stage.rdepends)
+                                    if (stages[d].state != StageState.INACTIVE)
+                                        continue itStages;  // reverse dependency isn't unmet (yet)
+                            }
+                            
+                            // All dependencies met
+                            debug assert (toRun.size, "toRun is empty (error with iterator)");
+                            toRunIt.remove;
+                            break getStage;
                         }
                         
-                        // All dependencies met
-                        it.remove;
-                        break getStage;
+                        // No stage remaining with all dependencies met
+                        if (toRun.size && numWorking)       // still some working so more dependencies may be met later
+                            toRunC.wait;    // wait until another thread finishes
+                            else                // no thread is working, so none of what's left is doable, or nothing's left
+                                break threadLoop;
+                    }
+                    ++numWorking;           // got a job!
+                    if (num_rdepends > 2)   // how many stages depended on the last one run?
+                        toRunC.notifyAll(); // tell all waiting threads there may be work now
+                    else if (num_rdepends == 2)
+                        toRunC.notify();    // there's potentially work for this thread and one other
+                        // else there won't be additional work so don't notify
                     }
                     
-                    // No stage remaining with all dependencies met
-                    if (!toRun.isEmpty && numWorking)     // still some working so more dependencies may be met later
-                        toRunC.wait;    // wait until another thread finishes
-                    else                // no thread is working, so none of what's left is doable, or nothing's left
+                    // Do a job:
+                    try {
+                        // FIXME - old stage start&finish trace messages - we don't have a name!
+                        debug logger.trace ("InitStage {}: starting", stage);
+                        static if (startup)
+                        stage.state = (*stage).init();  // init is a property of a pointer (oh no!)
+                        else
+                            stage.state = stage.cleanup();
+                        debug logger.trace ("InitStage {}: completed; state: {}", stage, stage.state);
+                    } catch (InitStageException e) {
+                        debug logger.trace ("InitStage {}: failed", stage);
+                        stage.state = e.state;
+                        doneInit = STATE.ABORT;
                         break threadLoop;
+                    } catch (Exception e) {
+                        debug logger.trace ("InitStage {}: failed", stage);
+                        doneInit = STATE.ABORT;
+                        break threadLoop;
+                    }
                 }
-                ++numWorking;           // got a job!
-                if (num_rdepends > 2)   // how many stages depended on the last one run?
-                    toRunC.notifyAll(); // tell all waiting threads there may be work now
-                else if (num_rdepends == 2)
-                    toRunC.notify();    // there's potentially work for this thread and one other
-                // else there won't be additional work so don't notify
-            }
-            
-            // Do a job:
-            try {
-                // FIXME - old stage start&finish trace messages - we don't have a name!
-                debug logger.trace ("InitStage {}: starting", stage);
-                static if (startup)
-                    stage.state = (*stage).init();  // init is a property of a pointer (oh no!)
-                else
-                    stage.state = stage.cleanup();
-                debug logger.trace ("InitStage {}: completed; state: {}", stage, stage.state);
-            } catch (InitStageException e) {
-                debug logger.trace ("InitStage {}: failed", stage);
-                stage.state = e.state;
-                abortInit = true;
-                break threadLoop;
-            } catch (Exception e) {
-                debug logger.trace ("InitStage {}: failed", stage);
-                abortInit = true;
-                break threadLoop;
-            }
-            }
             } catch (Exception e) {
                 logger.fatal ("Exception in initThreadFct: "~e.msg);
-                abortInit = true;
+                doneInit = STATE.ABORT;
             }
+            doneInit |= STATE.DONE;     // allow other threads a faster exit
             toRunC.notifyAll(); // Most likely if we're exiting, we should make sure others aren't waiting.
             return;
         }
@@ -361,11 +373,12 @@
             initThreadFct();                            // try with just this thread
         }       // any other exception will be caught in main() and abort program
         
-        if (abortInit)
+        if (doneInit & STATE.ABORT)
             throw new InitException ("An init/cleanup function failed (see above message(s))");
         
-        foreach (stage; toRun)
-            logger.warn ("InitStage {}: was not run due to unmet dependencies", stage);
+        if (toRun.size)
+            foreach (stage; toRun)
+                logger.warn ("InitStage {}: was not run due to unmet dependencies", stage);
     }
     
     private static {
@@ -397,7 +410,6 @@
         stages = new typeof(stages);    // an empty test-bed
         
         bool init1, init2, init3 = true;
-        bool failed = false;    // set if anything goes wrong
         StageState s1InitReturns = StageState.ACTIVE;
         addInitStage ("stg1", delegate StageState() {
             init1 = true;
@@ -407,11 +419,11 @@
             return StageState.INACTIVE;
         });
         addInitStage ("stg2", delegate StageState() {
-            if (!init1) failed = true;
+            assert (init1);
             init2 = true;
             return StageState.ACTIVE;
         }, delegate StageState() {
-            if (!init1) failed = true;
+            assert (init1);
             init2 = false;
             return StageState.INACTIVE;
         }, ["stg1"]);
@@ -421,7 +433,7 @@
             return StageState.ERROR;
         };
         s3.cleanup = delegate StageState() {
-            if (!init1) failed = true;
+            assert (init1);
             init3 = false;
             return StageState.INACTIVE;
         };
@@ -442,7 +454,6 @@
         runStages!(true);
         assert (init1);
         assert (init2);
-        assert (!failed);
         foreach (s; stages)
             assert (s.state == StageState.ACTIVE);
         
@@ -450,7 +461,6 @@
         assert (!init1);
         assert (!init2);
         assert (!init3);
-        assert (!failed);
         foreach (s; stages)
             assert (s.state == StageState.INACTIVE);
         
@@ -460,18 +470,19 @@
         assert (init1);
         assert (!init2);
         assert (!init3);
-        assert (!failed);
         runStages!(false);
         assert (init1); // S1 cleanup won't run
         
         stages[toStageName("stg1")].state = StageState.INACTIVE;     // hack it back so we can still test
         s1InitReturns = StageState.ACTIVE;
         init1 = false;
+        bool a1 = false;
         try {
             runStages!(true);
-            assert (false);             // runStages should throw because s3.init runs now
-        } catch (Exception) {}
-        assert (init1); // s1.init should run first
+            a1 = true;
+        } catch (Exception e) {}
+        assert (!a1, "runStages didn't throw");
+        assert (init1); // s1.init should run first; s2.init may or may not get run
         assert (stages[toStageName("stg3")].state == cast(StageState)7);        // set by the exception
         
         stages = realInit;      // restore the real init stages