1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2014 Richard Andrew Cattermole
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 module dakka.base.registration.actors;
25 import dakka.base.registration.capabilities;
26 import dakka.base.remotes.messages : ActorInformation;
27 import dakka.base.defs;
28 
29 __gshared private {
30 	string[][string] capabilityClasses;
31 	ActorInformation[string] classesInfo;
32 
33 	shared(Actor)[string] localInstances; // instance[instanceIdentifier]
34 	string[][string] remoteInstances; // instanceIdentifier[][adder]
35 	shared(Actor)delegate() [string] createLocalReferenceInstance;
36 	shared(Actor)delegate() [string] createLocalReferenceInstanceNonRef;
37 	ubyte[]delegate(string, ubyte[], string=null)[string] localCallInstance;
38 }
39 
40 void registerActor(T : Actor)() {
41 	synchronized {
42 		enum type = typeText!T;
43 
44 		capabilityClasses[type] = [];
45 		foreach(UDA; __traits(getAttributes, T)) {
46 			static if (__traits(compiles, {DakkaCapability c = UDA;})) {
47 				capabilityClasses[type] ~= UDA.name;
48 			}
49 		}
50 		classesInfo[type] = extractActorInfo!T;
51 
52 		createLocalReferenceInstance[type] = {
53 			static if (isASingleton!T) {
54 				if (type !in localInstances)
55 					localInstances[type] = cast(shared)new ActorRef!T;
56 				return cast(shared)localInstances[type];
57 			} else
58 				return cast(shared)new ActorRef!T;
59 		};
60 
61 		createLocalReferenceInstanceNonRef[type] = {
62 			static if (isASingleton!T) {
63 				if (type !in localInstances)
64 					localInstances[type] = cast(shared)new T;
65 				return cast(shared(T))localInstances[type];
66 			} else
67 				return cast(shared)new T;
68 		};
69 	}
70 }
71 
72 bool canLocalCreate(T : Actor)() {
73 	return canLocalCreate(typeText!T);
74 }
75 
76 bool canLocalCreate(string type) {
77 	synchronized {
78 		if (!hasCapabilities()) return true;
79 		if (type !in capabilityClasses) return true;
80 
81 		foreach(c; capabilityClasses[type]) {
82 			if (!hasCapability(c)) return false;
83 		}
84 		
85 		return true;
86 	}
87 }
88 
89 ActorInformation[] possibleActors() {
90 	synchronized {
91 		if (!hasCapabilities()) return classesInfo.values;
92 
93 		ActorInformation[] ret;
94 
95 	L1: foreach(k, v; classesInfo) {
96 			foreach(c; capabilityClasses[k])
97 				if (!hasCapability(c)) continue L1;
98 			ret ~= v;
99 		}
100 		
101 		return ret;
102 	}
103 }
104 
105 string[] possibleActorNames() {
106 	synchronized {
107 		if (!hasCapabilities()) return classesInfo.keys;
108 
109 		string[] ret;
110 		
111 	F1: foreach(k, v; classesInfo) {
112 			if (k in capabilityClasses)
113 				foreach(c; capabilityClasses[k])
114 					if (!hasCapability(c)) continue F1;
115 			ret ~= k;
116 		}
117 
118 		return ret;
119 	}
120 }
121 
122 ActorInformation getActorInformation(string name) {
123 	synchronized {
124 		assert(name in classesInfo, "Class " ~ name ~ " has not been registered.");
125 		return classesInfo[name];
126 	}
127 }
128 
129 string[] capabilitiesRequired(T : Actor)() {
130 	synchronized {
131 		if (typeText!T !in capabilityClasses) return [];
132 		return capabilityClasses[typeText!T];
133 	}
134 }
135 
136 string[] capabilitiesRequired(string identifier) {
137 	synchronized {
138 		return capabilityClasses[identifier];
139 	}
140 }
141 
142 void storeActor(T : Actor)(T actor) {
143 	synchronized {
144 		localInstances[actor.identifier] = cast(shared)actor;
145 		localCallInstance[actor.identifier] = (string method, ubyte[] args, string addr=null) {return handleCallActorMethods!T(actor, method, args, addr);};
146 	}
147 }
148 
149 void destoreActor(string identifier) {
150 	synchronized {
151 		localInstances.remove(identifier);
152 		localCallInstance.remove(identifier);
153 	}
154 }
155 
156 Actor getInstance(string identifier) {
157 	synchronized {
158 		return cast()localInstances[identifier];
159 	}
160 }
161 
162 bool hasInstance(string identifier) {
163 	synchronized {
164 		return identifier in localInstances ? true : false;
165 	}
166 }
167 
168 Actor createLocalActor(string type) {
169 	synchronized {
170 		assert(type in classesInfo, "Class " ~ type ~ " has not been registered.");
171 		return cast()createLocalReferenceInstance[type]();
172 	}
173 }
174 
175 Actor createLocalActorNonRef(string type) {
176 	synchronized {
177 		assert(type in classesInfo, "Class " ~ type ~ " has not been registered.");
178 		return cast()createLocalReferenceInstanceNonRef[type]();
179 	}
180 }
181 
182 ubyte[] callMethodOnActor(string identifier, string method, ubyte[] data, string addr=null) {
183 	synchronized {
184 		assert(identifier in localCallInstance, "Class " ~ identifier ~ " has not been created.");
185 		return localCallInstance[identifier](method, data, addr);
186 	}
187 }
188 
189 
190 /**
191  * Dyanmic stuff for remotes
192  */
193 
194 private {
195 	import dakka.base.impl.defs;
196 	import dakka.base.remotes.defs : grabActorFromData;
197 	import cerealed;
198 
199 	pure {
200 		import dakka.base.remotes.messages;
201 		import std.traits;
202 
203 		ActorInformation extractActorInfo(T : Actor)() {
204 			ActorInformation ret;
205 			ret.name = typeText!T;
206 			ret.methods = extractActorMethods!T;
207 			return ret;
208 		}
209 
210 		ActorMethod[] extractActorMethods(T : Actor)() {
211 			ActorMethod[] ret;
212 			T t;
213 
214 			foreach(m; __traits(allMembers, T)) {
215 				static if (__traits(compiles, mixin("t." ~ m))) {
216 					static if (isCallable!(mixin("t." ~ m))) {
217 						static if (!isMethodLocalOnly!(T, m))
218 							ret ~= extractActorMethod!(T, m);
219 					}
220 				}
221 			}
222 
223 			return ret;
224 		}
225 
226 		ActorMethod extractActorMethod(T : Actor, string m)() {
227 			ActorMethod ret;
228 			ret.name = m;
229 			ret.return_type = typeText!(ReturnType!(__traits(getMember, T, m)));
230 
231 			enum argStorage = ParameterStorageClassTuple!(__traits(getMember, T, m));
232 			foreach(i, arg; ParameterTypeTuple!(__traits(getMember, T, m))) {
233 				ActorMethodArgument argument;
234 				argument.type = typeText!(arg);
235 
236 				if (argStorage[i] == ParameterStorageClass.out_)
237 					argument.usage = ActorMethodArgumentUsage.Out;
238 				else if (argStorage[i] == ParameterStorageClass.ref_)
239 					argument.usage = ActorMethodArgumentUsage.Ref;
240 				else
241 					argument.usage = ActorMethodArgumentUsage.In;
242 
243 				ret.arguments ~= argument;
244 			}
245 
246 			return ret;
247 		}
248 
249 		string getValuesFromDeserializer(T : Actor, string m)(T t = T.init) {
250 			string ret;
251 
252 			//Decerealizer
253 			foreach(i, n; ParameterTypeTuple!(mixin("t." ~ m))) {
254 				static if (is(n == class) && is(n : Actor)) {
255 					ret ~= "grabActorFromData!(" ~ n.stringof ~ ")(d, addr), ";
256 				} else {
257 					ret ~= "d.value!(" ~ n.stringof ~ "), ";
258 				}
259 			}
260 
261 			if (ret.length > 0)
262 				ret.length -= 2;
263 
264 			return ret;
265 		}
266 
267 		string grabImportsForDeserializer(T : Actor, string m)(T t = T.init) {
268 			string ret;
269 			foreach(n; ParameterTypeTuple!(mixin("t." ~ m))) {
270 				static if (is(n == class) || is(n == struct) || is(n == union)) {
271 					ret ~= "import " ~ moduleName!n ~ " : " ~ n.stringof ~ ";";
272 				}
273 			}
274 			return ret;
275 		}
276 	}
277 	
278 	ubyte[] handleCallActorMethods(T : Actor)(T t, string method, ubyte[] data, string addr=null) {
279 		foreach(m; __traits(allMembers, T)) {
280 			static if (__traits(compiles, __traits(getMember, t, m)) && __traits(getProtection, __traits(getMember, t, m)) == "public" && !hasMember!(Actor, m)) {
281 				static if (__traits(isVirtualFunction, __traits(getMember, t, m))) {
282 					if (m == method) {
283 						static if (!isMethodLocalOnly!(T, m))
284 							return handleCallActorMethod!(T, m)(t, data, addr);
285 					}
286 				}
287 			}
288 		}
289 		
290 		return null;
291 	}
292 
293 	ubyte[] handleCallActorMethod(T : Actor, string m)(T t, ubyte[] data, string addr=null) {
294 		auto d = Decerealizer(data);
295 		
296 		static if (!hasReturnValue!(T, m)) {
297 			// call
298 			doCallActorMethod!(T, m)(t, d, data, addr);
299 			return null;
300 		} else {
301 			// store ret during call
302 			auto ret = doCallActorMethod!(T, m)(t, d, data, addr);
303 			
304 			// serialize ret
305 			auto c = Cerealiser();
306 			static if (is(typeof(ret) == class) && is(typeof(ret) : Actor)) {
307 				c.write(DakkaActorRefWrapper(ret.identifier, ret.isLocalInstance ? null : (ret.remoteAddressIdentifier == remoteAddressIdentifier ? null : ret.remoteAddressIdentifier)));
308 			} else {
309 				c ~= ret;
310 			}
311 			
312 			// return ret
313 			return cast(ubyte[])c.bytes;
314 		}
315 	}
316 
317 	U doCallActorMethod(T : Actor, string m, T tt = T.init, U=ReturnType!(mixin("tt." ~ m)))(T t, Decerealizer d, ubyte[] data, string addr=null) {
318 		import std.traits : moduleName;
319 		import dakka.base.remotes.defs : getDirector;
320 		mixin(grabImportsForDeserializer!(T, m));
321 
322 		try {
323 			static if (!hasReturnValue!(T, m)) {
324 				mixin("t." ~ m ~ "(" ~ getValuesFromDeserializer!(T, m) ~ ");");
325 			} else {
326 				mixin("return t." ~ m ~ "(" ~ getValuesFromDeserializer!(T, m) ~ ");");
327 			}
328 		} catch(Exception e) {
329           	if(t.supervisor !is null) {
330             	if (t.supervisor.isLocalInstance || (!t.supervisor.isLocalInstance && getDirector().validAddressIdentifier((cast(ActorRef!(Actor))t.supervisor).remoteAddressIdentifier))) {
331                		(cast()t.supervisor).onChildError(t, e.toString());
332 				} else {
333                 	t.die();
334 				}
335 			}
336 			static if (hasReturnValue!(T, m))
337             	return U.init;
338 		}
339 	}
340 }
341