1 /// Mecca main entrypoint
2 module mecca.runtime.main;
3 
4 // Licensed under the Boost license. Full copyright information in the AUTHORS file
5 
6 import std.stdio;
7 import std..string;
8 import mecca.reactor: theReactor;
9 import mecca.runtime.services;
10 
11 
12 struct ServiceManager {
13     bool isRunning;
14     string[] args;
15     private string[] _serviceStack;
16     private string[] _orderOfInitialization;
17     string[] servicesToInit;
18 
19     void main(string[] args) {
20         assert (!isRunning);
21         isRunning = true;
22         scope(exit) isRunning = false;
23         this.args = args;
24 
25         scope(exit) runAtExitCallbacks();
26 
27         theReactor.setup();
28         scope(success) theReactor.teardown();
29 
30         setupServices();
31         scope(success) teardownServices();
32 
33         // this is the mainloop
34         theReactor.start();
35     }
36 
37     version(unittest) void utMain(string[] args, string[] servicesToInit = null) {
38         this.servicesToInit = servicesToInit;
39         main(args);
40     }
41 
42     private void _recursiveSetup(string name) {
43         auto svc = &_registeredServices[name];
44 
45         final switch (svc.state) with (ServiceInterface.State) {
46             case DOWN:
47                 _serviceStack ~= name;
48                 scope(exit) _serviceStack.length--;
49 
50                 svc.state = SETUP;
51                 foreach(depName; svc.deps) {
52                     _recursiveSetup(depName);
53                 }
54                 if (svc.setup()) {
55                     _orderOfInitialization ~= name;
56                     svc.state = UP;
57                 }
58                 else {
59                     svc.state = DISABLED;
60                 }
61                 break;
62 
63             case SETUP:
64                 assert (false, "Cycle detected: %s".format(_serviceStack));
65 
66             case UP:
67                 break;
68 
69             case DISABLED:
70                 if (_serviceStack.length == 0) {
71                     // invoked from root, just skip
72                 }
73                 else {
74                     assert (false, "Service %s requires disabled service %s (%s)".format(_serviceStack[$-1], name));
75                 }
76                 break;
77 
78             case TEARDOWN:
79                 assert (false, "Teardown during setup: %s".format(_serviceStack));
80         }
81     }
82 
83     private void setupServices() {
84         _orderOfInitialization.length = 0;
85         _serviceStack.length = 0;
86         foreach(_, ref svc; _registeredServices) {
87             svc.state = ServiceInterface.State.DOWN;
88         }
89         scope(exit) servicesToInit = null;
90         if (servicesToInit is null) {
91             servicesToInit = _registeredServices.keys;
92         }
93         foreach(name; servicesToInit) {
94             assert (_serviceStack.length == 0);
95             _recursiveSetup(name);
96             assert (_serviceStack.length == 0);
97         }
98     }
99 
100     private void teardownServices() {
101         foreach_reverse(name; _orderOfInitialization) {
102             auto svc = &_registeredServices[name];
103             assert (svc.state == ServiceInterface.State.UP);
104             svc.teardown();
105             svc.state = ServiceInterface.State.DOWN;
106         }
107         _orderOfInitialization.length = 0;
108     }
109 
110     public void stop() {
111         // graceful termination
112         //theReactor.stop();  -- this asserts on a context switch while in a critical section
113         import mecca.lib.time: Timeout;
114 
115         static void stopWrapper() {
116             theReactor.stop();
117         }
118 
119         theReactor.registerTimer(Timeout.elapsed, &stopWrapper);
120     }
121 }
122 
123 __gshared ServiceManager serviceManager;
124 
125 /**
126  * main entrance function for the mecca reactor
127  */
128 int meccaMain(string[] argv) {
129     serviceManager.main(argv);
130     return 0;
131 }