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.defs; 25 import dakka.base.impl.defs; 26 27 private { 28 static if (__traits(compiles, import("BuildIdentifier.txt"))) { 29 __gshared string buildText = import("BuildIdentifier.txt"); 30 } else { 31 __gshared string buildText = "DakkaBuild#" ~ __TIMESTAMP__; 32 } 33 } 34 35 void assignBuildTitle(string text) { 36 buildText = text; 37 } 38 39 string getBuildTitle() { 40 return buildText; 41 } 42 43 class Actor { 44 import vibe.d : runTask; 45 import cerealed; 46 47 this(Actor supervisor = null, bool isLocalInstance = true) { 48 this.supervisor_ = supervisor; 49 this.isLocalInstance_ = isLocalInstance; 50 } 51 52 private { 53 // stores a list of all the children that this actor created. 54 // they may be null (have been killed). 55 Actor[] _children; 56 Actor supervisor_; 57 bool isAlive_ = true; 58 bool isDying_ = false; 59 60 string identifier_; 61 string remoteAddressIdentifier_; 62 bool isLocalInstance_; 63 } 64 65 final { 66 /** 67 * Creates a new actor instance or finds one that is already available. 68 */ 69 T actorOf(T : Actor)() { 70 if (__ctfe && is(typeof(this) == Actor)) { 71 pragma(msg, "You sure you want to CTFE actorOf " ~ typeText!T ~ "?"); 72 return null; 73 } else { 74 foreach(child; _children) { 75 if (child.classinfo == T.classinfo) { 76 return cast(T)child; 77 } 78 } 79 80 auto ret = new ActorRef!T(this); 81 _children ~= ret; 82 return ret; 83 } 84 } 85 86 Actor referenceOfActor() { 87 return new ActorRef!(typeof(this)); 88 } 89 } 90 91 @property { 92 Actor[] children() { return _children; } 93 Actor supervisor() { return supervisor_; } 94 bool isLocalInstance() { return isLocalInstance_; } 95 bool isAlive() { return isAlive_; } 96 string identifier() { return identifier_; } 97 string remoteAddressIdentifier() { return remoteAddressIdentifier_; } 98 } 99 100 /** 101 * Lets go die. 102 */ 103 void die(bool informSupervisor = true) { 104 import dakka.base.remotes.defs; 105 import dakka.base.registration.actors : destoreActor; 106 107 // seems silly to do anything if its a remote instance. 108 // we don't know what to do with it. 109 // leave it for the ActorRef 110 if (!isDying_ && isAlive_ && isLocalInstance_) { 111 // stop any more errors being received. 112 isDying_ = true; 113 auto director = getDirector(); 114 115 if (informSupervisor) { 116 if (supervisor !is null) { 117 if (supervisor.isLocalInstance || director.validAddressIdentifier((cast(ActorRef!(Actor))supervisor).remoteAddressIdentifier)) { 118 supervisor.kill(this); 119 } 120 } 121 } 122 123 onStop(); 124 125 // this will enable calling of methods that may be wrapped, by it being set after onstop. 126 isAlive_ = false; 127 isDying_ = false; 128 129 foreach(child; _children) { 130 child.die(); 131 } 132 } 133 } 134 135 /** 136 * Kills off a specific child 137 */ 138 void kill(Actor actor) { 139 Actor[] newChildren; 140 141 foreach(child; _children) { 142 if (cast(Actor)child == cast(Actor)actor) { 143 child.die(false); 144 } else { 145 newChildren ~= child; 146 } 147 } 148 149 _children = newChildren; 150 } 151 152 void onStart() {} 153 void onStop() {} 154 void onChildError(Actor actor, string message) {} 155 } 156 157 class ActorRef(T : Actor) : T { 158 import dakka.base.remotes.defs : getDirector, DakkaActorRefWrapper; 159 import dakka.base.registration.actors : canLocalCreate, storeActor, createLocalActorNonRef; 160 161 this(string identifier, string remoteAddress) { 162 identifier_ = identifier; 163 remoteAddressIdentifier_ = remoteAddress; 164 isLocalInstance_ = false; 165 } 166 167 this(Actor supervisor = null, bool isActualInstance = false) { 168 import vibe.d : runTask; 169 auto director = getDirector(); 170 enum type = typeText!T; 171 172 if (isActualInstance) { 173 localRef = cast(T)supervisor; 174 identifier_ = supervisor.identifier_; 175 } else { 176 bool createRemotely = director.canCreateRemotely!T && director.preferablyCreateRemotely!T; 177 178 if (createRemotely) { 179 // hey director, you think you could you know find a node to create it upon? 180 string addr = director.preferableRemoteNodeCreation(type); 181 if (addr !is null) { 182 // then go send a request to create it? 183 string identifier = director.createClass(addr, type, supervisor is null ? null : supervisor.identifier_); 184 // lastly I'll store that info. 185 if (identifier !is null) { 186 identifier_ = identifier; 187 remoteAddressIdentifier_ = addr; 188 isLocalInstance_ = false; 189 } else { 190 // hey supervisor... we couldn't create this reference. What do you want to do now? 191 assert(0, "no identifier"); 192 } 193 } else { 194 // hey supervisor... we couldn't create this reference. What do you want to do now? 195 assert(0, "no address"); 196 } 197 } else { 198 // well this is easy. 199 // register it with out director as our current instance. 200 // also don't forget that could be a singleton. have it handled centurally. 201 localRef = cast(T)createLocalActorNonRef(type); 202 if (localRef.identifier_ is null) { 203 identifier_ = director.localActorCreate(typeText!T); 204 localRef.identifier_ = identifier_; 205 storeActor!T(localRef); 206 207 // new thread for on start. Yes its evil. But it'll work. 208 runTask({ (cast(T)localRef).onStart(); }); 209 } 210 } 211 } 212 } 213 214 private { 215 T localRef; 216 } 217 218 override { 219 void die(bool informSupervisor = true) { 220 import dakka.base.registration.actors : destoreActor; 221 222 auto director = getDirector(); 223 enum type = typeText!T; 224 225 if (isLocalInstance_ && !isDying_ && isAlive_) { 226 super.die(informSupervisor); 227 228 // no point in it being in destructor. 229 // As it is also stored within the actor registration 230 // (so won't have that called till the reference in the actor registration goes bye bye). 231 director.localActorDies(type, identifier); 232 destoreActor(identifier_); 233 } else { 234 director.killClass(remoteAddressIdentifier, type, identifier); 235 } 236 } 237 238 void onChildError(Actor actor, string message) { 239 if (isLocalInstance_) { 240 super.onChildError(actor, message); 241 } else { 242 // assuming the remote node will know about this actor. After all, it contains the supervisor for it! 243 getDirector().actorError(remoteAddressIdentifier, identifier_, (actor !is null ? actor.identifier_ : ""), message); 244 } 245 } 246 } 247 248 mixin(getActorImplComs!T); 249 } 250 251 class AllActorRefs(T : Actor) if (isASingleton!T) { 252 import dakka.base.impl.wrap; 253 import dakka.base.registration.actors : createLocalActorNonRef; 254 import dakka.base.remotes.defs : getDirector; 255 import cerealed.decerealizer; 256 257 private { 258 T localRef; 259 ActorRef!T[string] remoteRefs; // actor[remoteAddressIdentifier] 260 } 261 262 this() { 263 localRef = cast(T)createLocalActorNonRef(typeText!T); // because singleton and local 264 } 265 266 // implement the methods overloading 267 268 void checkForUpdatesToAddresses() { 269 import std.algorithm : equal; 270 271 auto director = getDirector(); 272 273 // grab all of the current ones 274 string[] allRemoteAddresses = director.allRemoteAddresses(); 275 276 // does remoteRefs.keys != addrs 277 if (!equal(remoteRefs.keys, allRemoteAddresses)) { 278 279 // remoteRefs = [] 280 remoteRefs = remoteRefs.init; 281 282 // foreach addr in addrs 283 foreach(addr; allRemoteAddresses) { 284 // remoteRefs[addr] = director.createClass(addr, typeText!T); 285 remoteRefs[addr] = new ActorRef!T(director.createClass(addr, typeText!T, null), addr); 286 } 287 } 288 } 289 290 mixin(getActorWrapImpl!T); 291 } 292 293 pure string typeText(T)() { 294 import std.traits : moduleName; 295 static if (is(T == class) || is(T == struct) || is(T == union)) { 296 return moduleName!T ~ "." ~ T.stringof; 297 } else { 298 return T.stringof; 299 } 300 } 301 302 enum DakkaNodeType { 303 Local, 304 Remote 305 } 306 307 struct DakkaCapability { 308 string name; 309 } 310 311 struct DakkaSingleton {} 312 313 pure bool isASingleton(T : Actor)() { 314 foreach(uda; __traits(getAttributes, T)) { 315 static if (is(uda == DakkaSingleton)) { 316 return true; 317 } 318 } 319 320 return false; 321 } 322 323 struct DakkaLocalOnly {} 324 pure bool isMethodLocalOnly(T : Actor, string m)() { 325 enum T t = T.init; 326 foreach(uda; __traits(getAttributes, mixin("t." ~ m))) { 327 static if (is(uda == DakkaLocalOnly)) { 328 return true; 329 } 330 } 331 332 return false; 333 } 334 335 enum DakkaCallStrategy { 336 Sequentially, 337 Until 338 } 339 340 struct DakkaCall_ { 341 DakkaCallStrategy strategy; 342 ubyte[] data; 343 } 344 345 pure DakkaCall_ DakkaCall(DakkaCallStrategy strategy) { 346 return DakkaCall_(strategy); 347 } 348 349 pure DakkaCall_ DakkaCall(T)(DakkaCallStrategy strategy, T t) { 350 import cerealed.cerealizer; 351 Cerealizer c; 352 c ~= t; 353 354 return DakkaCall_(strategy, cast(ubyte[])c.bytes); 355 }