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