1 module reloaded.reloaded;
2 
3 import reloaded.setjmp;
4 
5 public:
6 
7 mixin template ReloadedCrashReturn()
8 {
9     import core.stdc.signal;
10     import reloaded.setjmp : setjmp, jmp_buf;
11 
12     static jmp_buf __crash_return_buffer;
13 
14     extern(C) @nogc nothrow
15     static void __crash_return_handler(int code)
16     {
17         import core.stdc.stdio;
18     	import core.stdc.signal;
19         import reloaded.setjmp : longjmp;
20 
21         enum ERROR_MSG = 
22         [
23             SIGABRT : "Signal Abort",
24             SIGFPE : "Signal Floating-Point Exception",
25             SIGILL : "Signal Illegal Instruction",
26             SIGINT : "Signal Interrupt",
27             SIGSEGV : "Signal Segmentation Violation",
28             SIGTERM : "Signal Terminate",
29         ];
30 
31         printf("ReloadeD Client crashed with signal :");
32 
33         switch(code)
34         {
35             case SIGABRT: printf("Abort"); break;
36             case SIGFPE: printf("Floating-Point Exception"); break;
37             case SIGILL: printf("Illegal Instruction"); break;
38             case SIGINT: printf("Interrupt"); break;
39             case SIGSEGV: printf("Segmentation Violation"); break;
40             case SIGTERM: printf("Terminate"); break;
41             default:
42                 printf("Unknown (%d) ", code);
43             break;
44         }
45 
46     	printf("\n",);
47     	longjmp(__crash_return_buffer, 1);
48     }
49     version(Windows)
50     {
51         auto __crash_return_code = setjmp(__crash_return_buffer, null);
52     }
53     else
54     {
55         auto __crash_return_code = setjmp(__crash_return_buffer);
56     }
57     auto __crash_return_noop = {signal(SIGSEGV, &__crash_return_handler ); return false; }();
58 }
59 
60 struct Reloaded
61 {
62 	import derelict.util.sharedlib : SharedLib;
63 	import fswatch : FileWatch, FileChangeEventType;
64     import std.typecons : Flag, Yes, No;
65 
66     static void noop(){}
67 
68     alias extern_init   = void function(void*);
69     alias extern_uninit = void function(void*);
70     alias extern_load   = void function(void*);
71     alias extern_unload = void function(void*);
72     alias extern_update = void function();
73 
74 	SharedLib       lib;
75 	extern_update   update_fun;
76 	void*           userdata;
77 	FileWatch       fw;
78 
79     string          lib_path;
80     string[2]       lib_swaps;
81 	bool            lib_swaps_v = 0;
82 
83     
84 
85 	this(T)(string lib_path, auto ref T userdata)
86 	{
87         load( lib_path, userdata );
88 	}
89 
90     void load(T)(string lib_path, auto ref T userdata)
91     {
92         import std.path : stripExtension, extension;
93 
94         this.lib_path = lib_path;
95         this.userdata = cast(void*)&userdata;
96 		fw = FileWatch(lib_path);
97 
98         auto base = lib_path.stripExtension;
99         auto ext  = lib_path.extension;
100 
101         lib_swaps[0] = base ~ "0" ~ ext;
102         lib_swaps[1] = base ~ "1" ~ ext;
103 
104         loadLib!(Yes.FirstTime);
105     }
106 
107     void update()
108     {
109         foreach (event; fw.getEvents())
110 		{
111 			if (event.type == FileChangeEventType.create || event.type == FileChangeEventType.modify  )
112 			{
113 				loadLib!(No.FirstTime);
114 				break;
115 			}
116 		}
117         update_fun();
118     }
119 
120     ~this()
121     {
122         auto unload = getLibFun!"unload";
123         if(unload)
124             unload(userdata);
125 
126         auto uninit = getLibFun!"uninit";
127         if(uninit)
128             uninit(userdata);
129     }
130 
131     private:
132 
133     auto getLibFun(string fun, Args... )(auto ref Args args)
134     {
135         mixin("return cast(extern_" ~ fun ~ " )lib.loadSymbol(fun, false);");
136     }
137 
138     void waitFileUnlock()
139     {
140 		import core.stdc.stdio : FILE, fopen, fclose;
141 
142         FILE* fp;
143 		while( (fp = fopen(lib_path.ptr, "r" )) == null ){}
144 		fclose(fp);
145     }
146 
147 
148     void loadLib(Flag!"FirstTime" first_time = No.FirstTime)()
149     {
150         import std.file : copy, exists, remove;
151         import std.stdio : printf = writefln;
152 
153         if( !lib_path.exists )
154         {
155             printf("Lib not found :'%s'", lib_path );
156             return;
157         }
158 
159         waitFileUnlock;
160 
161         auto lib_tmp = lib_swaps[cast(size_t)lib_swaps_v];
162 		copy(lib_path, lib_tmp);
163 
164         if( lib.isLoaded )
165         {
166 			lib.unload;
167             //DTOR unload;
168         }
169 
170 		lib.load( [lib_tmp] );
171 
172         //TEST ALL FUNCTIONS
173 
174         static foreach(fun ; ["init", "uninit", "load", "unload", "update"])
175         {
176             if( getLibFun!fun() == null )
177             {
178                 printf("Lib failed to load extern function: %s -> '%s'", fun,  mixin( "extern_"~fun~".stringof" ));
179             }
180         }
181 
182         lib_swaps_v = !lib_swaps_v;
183 
184         update_fun = getLibFun!"update";
185         if( !update_fun )
186             update_fun = &noop;
187 
188         static if(first_time)
189         {
190             auto init = getLibFun!"init";
191             if(init)
192                 init(userdata);
193             
194             auto load = getLibFun!"load";
195             if(load)
196                 load(userdata);
197         }
198         else
199         {
200             auto unload = getLibFun!"unload";
201             if(unload)
202                 unload( userdata );
203 
204             auto load = getLibFun!"load";
205             if(load)
206                 load( userdata );
207 
208         }
209     }
210 }