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