1 /**
2 Copyright: 2018 Mark Fisher
3 
4 License:
5 Permission is hereby granted, free of charge, to any person obtaining a copy of
6 this software and associated documentation files (the "Software"), to deal in
7 the Software without restriction, including without limitation the rights to
8 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 of the Software, and to permit persons to whom the Software is furnished to do
10 so, subject to the following conditions:
11 
12 The above copyright notice and this permission notice shall be included in all
13 copies or substantial portions of the Software.
14 
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 SOFTWARE.
22 **/
23 module dxx.sys.loader;
24 
25 private import std.exception;
26 private import std.experimental.logger;
27 
28 private import reloaded : Reloaded, ReloadedCrashReturn;
29 
30 private import dxx.constants;
31 private import dxx.util.notify;
32 private import dxx.util.log;
33 
34 struct ModuleData
35 {
36     const(RTConstants)* hostRuntime;
37     const(RTConstants)* moduleRuntime;
38     void* modData;
39 }
40 
41 class Loader {
42     @property
43     const(string) path;
44 
45     shared(ModuleData) moduleData;
46     Reloaded script;
47 
48     this(const(string) path,void* data) {
49         debug(Loader) {
50             MsgLog.info("Loader " ~ path);
51         }
52         //this.moduleData.libVersion = packageVersion;
53         this.moduleData.hostRuntime = &RTConstants.runtimeConstants;
54         this.moduleData.modData = cast(shared(void*))data;
55         this.path = path;
56         script = Reloaded();
57     }
58     void load() {
59         debug(Loader) {
60             sharedLog.info("load " ~ path);
61         }
62         script.load(path, moduleData);
63         mixin ReloadedCrashReturn;
64     }
65     void update() {
66         debug(Loader) {
67             sharedLog.info("update " ~ path);
68         }
69         script.update;
70     }
71     void update(void* data) {
72         debug(Loader) {
73             sharedLog.info("update");
74         }
75         this.moduleData.modData = cast(shared(void*))data;
76         update;
77     }
78     static auto loadModule(const(string) path,void* data) {
79         debug(Loader) {
80             sharedLog.info("loadModule " ~ path);
81         }
82         auto l = new Loader(path,data);
83         l.load;
84         debug(Loader) {
85             sharedLog.info("loaded " ~ path);
86         }
87         l.validate;
88         return l;
89     }
90     void validate() {
91         debug(Loader) {
92             sharedLog.info("validate " ~ path);
93             sharedLog.info(moduleData.hostRuntime.libVersions);
94         }
95         enforce(moduleData.moduleRuntime);
96         enforce(moduleData.moduleRuntime.checkVersion(RTConstants.constants.semVer));
97     }
98 }
99 
100 final class Module : SyncNotificationSource {
101     struct ModuleEvent {
102         enum Type {
103             Init,
104             Deinit,
105             Load,
106             Unload,
107             Update
108         }
109         Type eventType;
110         shared(Module) mod;
111     };
112     ModuleData* moduleData;
113 
114     private static __gshared shared(Module) INSTANCE;
115     static bool instantiated = false;
116 
117     static auto getInstance() {
118         if(!instantiated) {
119             synchronized(Module.classinfo) {
120                 if(!INSTANCE) {
121                     INSTANCE = new shared(Module);
122                     debug(Module) {
123                         sharedLog.info("new instance.");
124                     }
125                 }
126             }
127             instantiated = true;
128         }
129         return INSTANCE;
130     }
131 
132     template data(alias T) {
133         //alias data = moduleData.data!T;
134         auto ref shared data() {
135             return cast(T*)moduleData.modData;
136         }
137     }
138 
139     private shared this() {}
140 
141     private nothrow shared void sendModuleEvent(alias T)() {
142         auto m = ModuleEvent(T,this);
143         this.send!ModuleEvent(&m);
144     }
145     shared void init() {
146         //checkModuleVersion;
147         debug(Module) { sharedLog.info("init"); }
148         sendModuleEvent!(ModuleEvent.Type.Init);
149     }
150     shared void deinit() {
151         debug(Module) { sharedLog.info("deinit"); }
152         sendModuleEvent!(ModuleEvent.Type.Deinit);
153     }
154     shared void load() {
155         debug(Module) { sharedLog.info("load"); }
156         sendModuleEvent!(ModuleEvent.Type.Load);
157     }
158     shared void unload() {
159         debug(Module) { sharedLog.info("unload"); }
160         sendModuleEvent!(ModuleEvent.Type.Unload);
161     }
162     shared void update() {
163         debug(Module) { sharedLog.info("update"); }
164         sendModuleEvent!(ModuleEvent.Type.Update);
165     }
166 }
167 
168 
169 class ModuleNotificationListener : NotificationListener {
170     override shared void handleNotification(void* t) {
171         Module.ModuleEvent* event = cast(Module.ModuleEvent*)t;
172         debug(Module) {
173             import std.conv;
174             sharedLog.info("Notification:",event.eventType);
175         }
176         final switch(event.eventType) {
177             case Module.ModuleEvent.Type.Init:
178             onInit(event);
179             break;
180             case Module.ModuleEvent.Type.Deinit:
181             onDeinit(event);
182             unregister;
183             break;
184             case Module.ModuleEvent.Type.Load:
185             onLoad(event);
186             break;
187             case Module.ModuleEvent.Type.Unload:
188             onUnload(event);
189             break;
190             case Module.ModuleEvent.Type.Update:
191             onUpdate(event);
192             break;
193         }
194     }
195     shared void onInit(Module.ModuleEvent* event){}
196     shared void onDeinit(Module.ModuleEvent* event){}
197     shared void onLoad(Module.ModuleEvent* event){}
198     shared void onUnload(Module.ModuleEvent* event){}
199     shared void onUpdate(Module.ModuleEvent* event){}
200     void register() {
201         version(DXX_Module) {
202             Module.getInstance.addNotificationListener(cast(shared(NotificationListener))this);
203         }
204     }
205     shared void unregister() {
206         version(DXX_Module) {
207             Module.getInstance.removeNotificationListener(cast(shared(NotificationListener))this);
208         }
209     }
210 }
211 version(DXX_Module) {
212   extern(C):
213   void load( void* userdata );
214   void unload(void* userdata);
215   void init(void* data);
216   void uninit(void* userdata);
217   void update();
218 }