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 }