signals.d 37 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423
  1. // Written in the D programming language.
  2. /**
  3. * Signals and Slots are an implementation of the $(LINK2 http://en.wikipedia.org/wiki/Observer_pattern, Observer pattern)$(BR)
  4. * Essentially, when a Signal is emitted, a list of connected Observers
  5. * (called slots) are called.
  6. *
  7. * This implementation supersedes the former std.signals.
  8. *
  9. * Copyright: Copyright Robert Klotzner 2012 - 2013.
  10. * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
  11. * Authors: Robert Klotzner
  12. */
  13. /* Copyright Robert Klotzner 2012 - 2013.
  14. * Distributed under the Boost Software License, Version 1.0.
  15. * (See accompanying file LICENSE_1_0.txt or copy at
  16. * http://www.boost.org/LICENSE_1_0.txt)
  17. *
  18. * Based on the original implementation written by Walter Bright. (std.signals)
  19. * I shamelessly stole some ideas of: http://forum.dlang.org/thread/jjote0$1cql$1@digitalmars.com
  20. * written by Alex Rønne Petersen.
  21. *
  22. * Also thanks to Denis Shelomovskij who made me aware of some
  23. * deficiencies in the concurrent part of WeakRef.
  24. */
  25. module stdx.signals;
  26. import core.atomic;
  27. import core.memory;
  28. // Hook into the GC to get informed about object deletions.
  29. private alias void delegate(Object) DisposeEvt;
  30. private extern (C) void rt_attachDisposeEvent( Object obj, DisposeEvt evt );
  31. private extern (C) void rt_detachDisposeEvent( Object obj, DisposeEvt evt );
  32. /**
  33. * string mixin for creating a signal.
  34. *
  35. * It creates a Signal instance named "_name", where name is given
  36. * as first parameter with given protection and an accessor method
  37. * with the current context protection named "name" returning either a
  38. * ref RestrictedSignal or ref Signal depending on the given
  39. * protection.
  40. *
  41. * Bugs:
  42. * This mixin generator does not work with templated types right now because of:
  43. * $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=10502, 10502)$(BR)
  44. * You might want to use the Signal struct directly in this
  45. * case. Ideally you write the code, the mixin would generate, manually
  46. * to ensure an easy upgrade path when the above bug gets fixed:
  47. ---
  48. * ref RestrictedSignal!(SomeTemplate!int) mysig() { return _mysig;}
  49. * private Signal!(SomeTemplate!int) _mysig;
  50. ---
  51. *
  52. * Params:
  53. * name = How the signal should be named. The ref returning function
  54. * will be named like this, the actual struct instance will have an
  55. * underscore prefixed.
  56. *
  57. * protection = Specifies how the full functionality (emit) of the
  58. * signal should be protected. Default is private. If
  59. * Protection.none is given, private is used for the Signal member
  60. * variable and the ref returning accessor method will return a
  61. * Signal instead of a RestrictedSignal. The protection of the
  62. * accessor method is specified by the surrounding protection scope:
  63. ---
  64. * public: // Everyone can access mysig now:
  65. * // Result of mixin(signal!int("mysig", Protection.none))
  66. * ref Signal!int mysig() { return _mysig;}
  67. * private Signal!int _mysig;
  68. ---
  69. *
  70. * Example:
  71. ---
  72. import std.stdio;
  73. class MyObject
  74. {
  75. // Mixin signal named valueChanged, with default "private" protection.
  76. // (Only MyObject is allowed to emit the signal)
  77. mixin(signal!(string, int)("valueChanged"));
  78. int value() @property { return _value; }
  79. int value(int v) @property
  80. {
  81. if (v != _value)
  82. {
  83. _value = v;
  84. // call all the connected slots with the two parameters
  85. _valueChanged.emit("setting new value", v);
  86. }
  87. return v;
  88. }
  89. private:
  90. int _value;
  91. }
  92. class Observer
  93. { // our slot
  94. void watch(string msg, int i)
  95. {
  96. writefln("Observed msg '%s' and value %s", msg, i);
  97. }
  98. }
  99. void watch(string msg, int i)
  100. {
  101. writefln("Globally observed msg '%s' and value %s", msg, i);
  102. }
  103. void main()
  104. {
  105. auto a = new MyObject;
  106. Observer o = new Observer;
  107. a.value = 3; // should not call o.watch()
  108. a.valueChanged.connect!"watch"(o); // o.watch is the slot
  109. a.value = 4; // should call o.watch()
  110. a.valueChanged.disconnect!"watch"(o); // o.watch is no longer a slot
  111. a.value = 5; // so should not call o.watch()
  112. a.valueChanged.connect!"watch"(o); // connect again
  113. // Do some fancy stuff:
  114. a.valueChanged.connect!Observer(o, (obj, msg, i) => obj.watch("Some other text I made up", i+1));
  115. a.valueChanged.strongConnect(&watch);
  116. a.value = 6; // should call o.watch()
  117. destroy(o); // destroying o should automatically disconnect it
  118. a.value = 7; // should not call o.watch()
  119. }
  120. ---
  121. * which should print:
  122. * <pre>
  123. * Observed msg 'setting new value' and value 4
  124. * Observed msg 'setting new value' and value 6
  125. * Observed msg 'Some other text I made up' and value 7
  126. * Globally observed msg 'setting new value' and value 6
  127. * Globally observed msg 'setting new value' and value 7
  128. * </pre>
  129. */
  130. string signal(Args...)(string name, Protection protection=Protection.private_) @trusted // trusted necessary because of to!string
  131. {
  132. import std.conv;
  133. string argList="(";
  134. import std.traits : fullyQualifiedName;
  135. foreach (arg; Args)
  136. {
  137. argList~=fullyQualifiedName!(arg)~", ";
  138. }
  139. if (argList.length>"(".length)
  140. argList = argList[0 .. $-2];
  141. argList ~= ")";
  142. string output = (protection == Protection.none ? "private" : to!string(protection)[0..$-1]) ~
  143. " Signal!" ~ argList ~ " _" ~ name ~ ";\n";
  144. string rType = protection == Protection.none ? "Signal!" : "RestrictedSignal!";
  145. output ~= "ref " ~ rType ~ argList ~ " " ~ name ~ "() { return _" ~ name ~ ";}\n";
  146. return output;
  147. }
  148. /**
  149. * Protection to use for the signal string mixin.
  150. */
  151. enum Protection
  152. {
  153. none, /// No protection at all, the wrapping function will return a ref Signal instead of a ref RestrictedSignal
  154. private_, /// The Signal member variable will be private.
  155. protected_, /// The signal member variable will be protected.
  156. package_ /// The signal member variable will have package protection.
  157. }
  158. /**
  159. * Full signal implementation.
  160. *
  161. * It implements the emit function for all other functionality it has
  162. * this aliased to RestrictedSignal.
  163. *
  164. * A signal is a way to couple components together in a very loose
  165. * way. The receiver does not need to know anything about the sender
  166. * and the sender does not need to know anything about the
  167. * receivers. The sender will just call emit when something happens,
  168. * the signal takes care of notifing all interested parties. By using
  169. * wrapper delegates/functions, not even the function signature of
  170. * sender/receiver need to match.
  171. *
  172. * Another consequence of this very loose coupling is, that a
  173. * connected object will be freed by the GC if all references to it
  174. * are dropped, even if it was still connected to a signal, the
  175. * connection will simply be dropped. This way the developer is freed of
  176. * manually keeping track of connections.
  177. *
  178. * If in your application the connections made by a signal are not
  179. * that loose you can use strongConnect(), in this case the GC won't
  180. * free your object until it was disconnected from the signal or the
  181. * signal got itself destroyed.
  182. *
  183. * This struct is not thread-safe in general, it just handles the
  184. * concurrent parts of the GC.
  185. *
  186. * Bugs: The code probably won't compile with -profile because of bug:
  187. * $(LINK2 http://d.puremagic.com/issues/show_bug.cgi?id=10260, 10260)
  188. */
  189. struct Signal(Args...)
  190. {
  191. alias restricted this;
  192. /**
  193. * Emit the signal.
  194. *
  195. * All connected slots which are still alive will be called. If
  196. * any of the slots throws an exception, the other slots will
  197. * still be called. You'll receive a chained exception with all
  198. * exceptions that were thrown. Thus slots won't influence each
  199. * others execution.
  200. *
  201. * The slots are called in the same sequence as they were registered.
  202. *
  203. * emit also takes care of actually removing dead connections. For
  204. * concurrency reasons they are set just to an invalid state by the GC.
  205. *
  206. * If you remove a slot during emit() it won't be called in the
  207. * current run if it wasn't already.
  208. *
  209. * If you add a slot during emit() it will be called in the
  210. * current emit() run. Note however Signal is not thread-safe, "called
  211. * during emit" basically means called from within a slot.
  212. */
  213. void emit( Args args ) @trusted
  214. {
  215. _restricted._impl.emit(args);
  216. }
  217. /**
  218. * Get access to the rest of the signals functionality.
  219. */
  220. ref RestrictedSignal!(Args) restricted() @property @trusted
  221. {
  222. return _restricted;
  223. }
  224. private:
  225. RestrictedSignal!(Args) _restricted;
  226. }
  227. /**
  228. * The signal implementation, not providing an emit method.
  229. *
  230. * The idea is to instantiate a Signal privately and provide a
  231. * public accessor method for accessing the contained
  232. * RestrictedSignal. You can use the signal string mixin, which does
  233. * exactly that.
  234. */
  235. struct RestrictedSignal(Args...)
  236. {
  237. /**
  238. * Direct connection to an object.
  239. *
  240. * Use this method if you want to connect directly to an object's
  241. * method matching the signature of this signal. The connection
  242. * will have weak reference semantics, meaning if you drop all
  243. * references to the object the garbage collector will collect it
  244. * and this connection will be removed.
  245. *
  246. * Preconditions: obj must not be null. mixin("&obj."~method)
  247. * must be valid and compatible.
  248. * Params:
  249. * obj = Some object of a class implementing a method
  250. * compatible with this signal.
  251. */
  252. void connect(string method, ClassType)(ClassType obj) @trusted
  253. if (is(ClassType == class) && __traits(compiles, {void delegate(Args) dg = mixin("&obj."~method);}))
  254. in
  255. {
  256. assert(obj);
  257. }
  258. body
  259. {
  260. _impl.addSlot(obj, cast(void delegate())mixin("&obj."~method));
  261. }
  262. /**
  263. * Indirect connection to an object.
  264. *
  265. * Use this overload if you want to connect to an objects method
  266. * which does not match the signal's signature. You can provide
  267. * any delegate to do the parameter adaption, but make sure your
  268. * delegates' context does not contain a reference to the target
  269. * object, instead use the provided obj parameter, where the
  270. * object passed to connect will be passed to your delegate.
  271. * This is to make weak ref semantics possible, if your delegate
  272. * contains a ref to obj, the object won't be freed as long as
  273. * the connection remains.
  274. *
  275. * Preconditions: obj and dg must not be null (dgs context
  276. * may). dg's context must not be equal to obj.
  277. *
  278. * Params:
  279. * obj = The object to connect to. It will be passed to the
  280. * delegate when the signal is emitted.
  281. *
  282. * dg = A wrapper delegate which takes care of calling some
  283. * method of obj. It can do any kind of parameter adjustments
  284. * necessary.
  285. */
  286. void connect(ClassType)(ClassType obj, void delegate(ClassType obj, Args) dg) @trusted
  287. if (is(ClassType == class))
  288. in
  289. {
  290. assert(obj);
  291. assert(dg);
  292. assert(cast(void*)obj !is dg.ptr);
  293. }
  294. body
  295. {
  296. _impl.addSlot(obj, cast(void delegate()) dg);
  297. }
  298. /**
  299. * Connect with strong ref semantics.
  300. *
  301. * Use this overload if you either really, really want strong ref
  302. * semantics for some reason or because you want to connect some
  303. * non-class method delegate. Whatever the delegates' context
  304. * references, will stay in memory as long as the signals
  305. * connection is not removed and the signal gets not destroyed
  306. * itself.
  307. *
  308. * Preconditions: dg must not be null. (Its context may.)
  309. *
  310. * Params:
  311. * dg = The delegate to be connected.
  312. */
  313. void strongConnect(void delegate(Args) dg) @trusted
  314. in
  315. {
  316. assert(dg);
  317. }
  318. body
  319. {
  320. _impl.addSlot(null, cast(void delegate()) dg);
  321. }
  322. /**
  323. * Disconnect a direct connection.
  324. *
  325. * After issuing this call, methods of obj won't be triggered any
  326. * longer when emit is called.
  327. * Preconditions: Same as for direct connect.
  328. */
  329. void disconnect(string method, ClassType)(ClassType obj) @trusted
  330. if (is(ClassType == class) && __traits(compiles, {void delegate(Args) dg = mixin("&obj."~method);}))
  331. in
  332. {
  333. assert(obj);
  334. }
  335. body
  336. {
  337. void delegate(Args) dg = mixin("&obj."~method);
  338. _impl.removeSlot(obj, cast(void delegate()) dg);
  339. }
  340. /**
  341. * Disconnect an indirect connection.
  342. *
  343. * For this to work properly, dg has to be exactly the same as
  344. * the one passed to connect. So if you used a lamda you have to
  345. * keep a reference to it somewhere if you want to disconnect
  346. * the connection later on. If you want to remove all
  347. * connections to a particular object use the overload which only
  348. * takes an object paramter.
  349. */
  350. void disconnect(ClassType)(ClassType obj, void delegate(ClassType, T1) dg) @trusted
  351. if (is(ClassType == class))
  352. in
  353. {
  354. assert(obj);
  355. assert(dg);
  356. }
  357. body
  358. {
  359. _impl.removeSlot(obj, cast(void delegate())dg);
  360. }
  361. /**
  362. * Disconnect all connections to obj.
  363. *
  364. * All connections to obj made with calls to connect are removed.
  365. */
  366. void disconnect(ClassType)(ClassType obj) @trusted if (is(ClassType == class))
  367. in
  368. {
  369. assert(obj);
  370. }
  371. body
  372. {
  373. _impl.removeSlot(obj);
  374. }
  375. /**
  376. * Disconnect a connection made with strongConnect.
  377. *
  378. * Disconnects all connections to dg.
  379. */
  380. void strongDisconnect(void delegate(Args) dg) @trusted
  381. in
  382. {
  383. assert(dg);
  384. }
  385. body
  386. {
  387. _impl.removeSlot(null, cast(void delegate()) dg);
  388. }
  389. private:
  390. SignalImpl _impl;
  391. }
  392. private struct SignalImpl
  393. {
  394. /**
  395. * Forbid copying.
  396. * Unlike the old implementations, it now is theoretically
  397. * possible to copy a signal. Even different semantics are
  398. * possible. But none of the possible semantics are what the user
  399. * intended in all cases, so I believe it is still the safer
  400. * choice to simply disallow copying.
  401. */
  402. @disable this(this);
  403. /// Forbid copying
  404. @disable void opAssign(SignalImpl other);
  405. void emit(Args...)( Args args )
  406. {
  407. int emptyCount = 0;
  408. if (!_slots.emitInProgress)
  409. {
  410. _slots.emitInProgress = true;
  411. scope (exit) _slots.emitInProgress = false;
  412. }
  413. else
  414. emptyCount = -1;
  415. doEmit(0, emptyCount, args);
  416. if (emptyCount > 0)
  417. {
  418. _slots.slots = _slots.slots[0 .. $-emptyCount];
  419. _slots.slots.assumeSafeAppend();
  420. }
  421. }
  422. void addSlot(Object obj, void delegate() dg)
  423. {
  424. auto oldSlots = _slots.slots;
  425. if (oldSlots.capacity <= oldSlots.length)
  426. {
  427. auto buf = new SlotImpl[oldSlots.length+1]; // TODO: This growing strategy might be inefficient.
  428. foreach (i, ref slot ; oldSlots)
  429. buf[i].moveFrom(slot);
  430. oldSlots = buf;
  431. }
  432. else
  433. oldSlots.length = oldSlots.length + 1;
  434. oldSlots[$-1].construct(obj, dg);
  435. _slots.slots = oldSlots;
  436. }
  437. void removeSlot(Object obj, void delegate() dg)
  438. {
  439. removeSlot((ref SlotImpl item) => item.wasConstructedFrom(obj, dg));
  440. }
  441. void removeSlot(Object obj)
  442. {
  443. removeSlot((ref SlotImpl item) => item.obj is obj);
  444. }
  445. ~this()
  446. {
  447. foreach (ref slot; _slots.slots)
  448. {
  449. debug (signal) { import std.stdio; stderr.writefln("Destruction, removing some slot(%s, weakref: %s), signal: ", &slot, &slot._obj, &this); }
  450. slot.reset(); // This is needed because ATM the GC won't trigger struct
  451. // destructors to be run when within a GC managed array.
  452. }
  453. }
  454. /// Little helper functions:
  455. /**
  456. * Find and make invalid any slot for which isRemoved returns true.
  457. */
  458. void removeSlot(bool delegate(ref SlotImpl) isRemoved)
  459. {
  460. if (_slots.emitInProgress)
  461. {
  462. foreach (ref slot; _slots.slots)
  463. if (isRemoved(slot))
  464. slot.reset();
  465. }
  466. else // It is save to do immediate cleanup:
  467. {
  468. int emptyCount = 0;
  469. auto mslots = _slots.slots;
  470. foreach (int i, ref slot; mslots)
  471. {
  472. // We are retrieving obj twice which is quite expensive because of GC lock:
  473. if (!slot.isValid || isRemoved(slot))
  474. {
  475. emptyCount++;
  476. slot.reset();
  477. }
  478. else if (emptyCount)
  479. mslots[i-emptyCount].moveFrom(slot);
  480. }
  481. if (emptyCount > 0)
  482. {
  483. mslots = mslots[0..$-emptyCount];
  484. mslots.assumeSafeAppend();
  485. _slots.slots = mslots;
  486. }
  487. }
  488. }
  489. /**
  490. * Helper method to allow all slots being called even in case of an exception.
  491. * All exceptions that occur will be chained.
  492. * Any invalid slots (GC collected or removed) will be dropped.
  493. */
  494. void doEmit(Args...)(int offset, ref int emptyCount, Args args )
  495. {
  496. int i=offset;
  497. auto myslots = _slots.slots;
  498. scope (exit) if (i+1<myslots.length) doEmit(i+1, emptyCount, args); // Carry on.
  499. if (emptyCount == -1)
  500. {
  501. for (; i<myslots.length; i++)
  502. {
  503. myslots[i](args);
  504. myslots = _slots.slots; // Refresh because addSlot might have been called.
  505. }
  506. }
  507. else
  508. {
  509. for (; i<myslots.length; i++)
  510. {
  511. bool result = myslots[i](args);
  512. myslots = _slots.slots; // Refresh because addSlot might have been called.
  513. if (!result)
  514. emptyCount++;
  515. else if (emptyCount>0)
  516. {
  517. myslots[i-emptyCount].reset();
  518. myslots[i-emptyCount].moveFrom(myslots[i]);
  519. }
  520. }
  521. }
  522. }
  523. SlotArray _slots;
  524. }
  525. // Simple convenience struct for signal implementation.
  526. // Its is inherently unsafe. It is not a template so SignalImpl does
  527. // not need to be one.
  528. private struct SlotImpl
  529. {
  530. @disable this(this);
  531. @disable void opAssign(SlotImpl other);
  532. /// Pass null for o if you have a strong ref delegate.
  533. /// dg.funcptr must not point to heap memory.
  534. void construct(Object o, void delegate() dg)
  535. in { assert(this is SlotImpl.init); }
  536. body
  537. {
  538. _obj.construct(o);
  539. _dataPtr = dg.ptr;
  540. _funcPtr = dg.funcptr;
  541. assert(GC.addrOf(_funcPtr) is null, "Your function is implemented on the heap? Such dirty tricks are not supported with std.signal!");
  542. if (o)
  543. {
  544. if (_dataPtr is cast(void*) o)
  545. _dataPtr = directPtrFlag;
  546. hasObject = true;
  547. }
  548. }
  549. /**
  550. * Check whether this slot was constructed from object o and delegate dg.
  551. */
  552. bool wasConstructedFrom(Object o, void delegate() dg)
  553. {
  554. if ( o && dg.ptr is cast(void*) o)
  555. return obj is o && _dataPtr is directPtrFlag && funcPtr is dg.funcptr;
  556. else
  557. return obj is o && _dataPtr is dg.ptr && funcPtr is dg.funcptr;
  558. }
  559. /**
  560. * Implement proper explict move.
  561. */
  562. void moveFrom(ref SlotImpl other)
  563. in { assert(this is SlotImpl.init); }
  564. body
  565. {
  566. auto o = other.obj;
  567. _obj.construct(o);
  568. _dataPtr = other._dataPtr;
  569. _funcPtr = other._funcPtr;
  570. other.reset(); // Destroy original!
  571. }
  572. @property Object obj()
  573. {
  574. return _obj.obj;
  575. }
  576. /**
  577. * Whether or not _obj should contain a valid object. (We have a weak connection)
  578. */
  579. bool hasObject() @property const
  580. {
  581. return cast(ptrdiff_t) _funcPtr & 1;
  582. }
  583. /**
  584. * Check whether this is a valid slot.
  585. *
  586. * Meaning opCall will call something and return true;
  587. */
  588. bool isValid() @property
  589. {
  590. return funcPtr && (!hasObject || obj !is null);
  591. }
  592. /**
  593. * Call the slot.
  594. *
  595. * Returns: True if the call was successful (the slot was valid).
  596. */
  597. bool opCall(Args...)(Args args)
  598. {
  599. auto o = obj;
  600. void* o_addr = cast(void*)(o);
  601. if (!funcPtr || (hasObject && !o_addr))
  602. return false;
  603. if (_dataPtr is directPtrFlag || !hasObject)
  604. {
  605. void delegate(Args) mdg;
  606. mdg.funcptr=cast(void function(Args)) funcPtr;
  607. debug (signal) { import std.stdio; writefln("hasObject: %s, o_addr: %s, dataPtr: %s", hasObject, o_addr, _dataPtr);}
  608. assert((hasObject && _dataPtr is directPtrFlag) || (!hasObject && _dataPtr !is directPtrFlag));
  609. if (hasObject)
  610. mdg.ptr = o_addr;
  611. else
  612. mdg.ptr = _dataPtr;
  613. mdg(args);
  614. }
  615. else
  616. {
  617. void delegate(Object, Args) mdg;
  618. mdg.ptr = _dataPtr;
  619. mdg.funcptr = cast(void function(Object, Args)) funcPtr;
  620. mdg(o, args);
  621. }
  622. return true;
  623. }
  624. /**
  625. * Reset this instance to its intial value.
  626. */
  627. void reset() {
  628. _funcPtr = SlotImpl.init._funcPtr;
  629. _dataPtr = SlotImpl.init._dataPtr;
  630. _obj.reset();
  631. }
  632. private:
  633. void* funcPtr() @property const
  634. {
  635. return cast(void*)( cast(ptrdiff_t)_funcPtr & ~cast(ptrdiff_t)1);
  636. }
  637. void hasObject(bool yes) @property
  638. {
  639. if (yes)
  640. _funcPtr = cast(void*)(cast(ptrdiff_t) _funcPtr | 1);
  641. else
  642. _funcPtr = cast(void*)(cast(ptrdiff_t) _funcPtr & ~cast(ptrdiff_t)1);
  643. }
  644. void* _funcPtr;
  645. void* _dataPtr;
  646. WeakRef _obj;
  647. enum directPtrFlag = cast(void*)(~0);
  648. }
  649. // Provides a way of holding a reference to an object, without the GC seeing it.
  650. private struct WeakRef
  651. {
  652. /**
  653. * As struct must be relocatable, it is not even possible to
  654. * provide proper copy support for WeakRef. rt_attachDisposeEvent
  655. * is used for registering unhook. D's move semantics assume
  656. * relocatable objects, which results in this(this) being called
  657. * for one instance and the destructor for another, thus the wrong
  658. * handlers are deregistered. D's assumption of relocatable
  659. * objects is not matched, so move() for example will still simply
  660. * swap contents of two structs, resulting in the wrong unhook
  661. * delegates being unregistered.
  662. * Unfortunately the runtime still blindly copies WeakRefs if they
  663. * are in a dynamic array and reallocation is needed. This case
  664. * has to be handled separately.
  665. */
  666. @disable this(this);
  667. @disable void opAssign(WeakRef other);
  668. void construct(Object o)
  669. in { assert(this is WeakRef.init); }
  670. body
  671. {
  672. debug (signal) createdThis=&this;
  673. debug (signal) { import std.stdio; writefln("WeakRef.construct for %s and object: %s", &this, o); }
  674. if (!o)
  675. return;
  676. _obj.construct(cast(void*)o);
  677. rt_attachDisposeEvent(o, &unhook);
  678. }
  679. Object obj() @property
  680. {
  681. return cast(Object) _obj.address;
  682. }
  683. /**
  684. * Reset this instance to its intial value.
  685. */
  686. void reset()
  687. {
  688. auto o = obj;
  689. debug (signal) { import std.stdio; writefln("WeakRef.reset for %s and object: %s", &this, o); }
  690. if (o)
  691. rt_detachDisposeEvent(o, &unhook);
  692. unhook(o); // unhook has to be done unconditionally, because in case the GC
  693. //kicked in during toggleVisibility(), obj would contain -1
  694. //so the assertion of SlotImpl.moveFrom would fail.
  695. debug (signal) createdThis = null;
  696. }
  697. ~this()
  698. {
  699. reset();
  700. }
  701. private:
  702. debug (signal)
  703. {
  704. invariant()
  705. {
  706. import std.conv : text;
  707. assert(createdThis is null || &this is createdThis,
  708. text("We changed address! This should really not happen! Orig address: ",
  709. cast(void*)createdThis, " new address: ", cast(void*)&this));
  710. }
  711. WeakRef* createdThis;
  712. }
  713. void unhook(Object o)
  714. {
  715. _obj.reset();
  716. }
  717. shared(InvisibleAddress) _obj;
  718. }
  719. // Do all the dirty stuff, WeakRef is only a thin wrapper completing
  720. // the functionality by means of rt_ hooks.
  721. private shared struct InvisibleAddress
  722. {
  723. /// Initialize with o, state is set to invisible immediately.
  724. /// No precautions regarding thread safety are necessary because
  725. /// obviously a live reference exists.
  726. void construct(void* o)
  727. {
  728. auto tmp = cast(ptrdiff_t) o;
  729. _addr = makeInvisible(cast(ptrdiff_t) o);
  730. }
  731. void reset()
  732. {
  733. atomicStore(_addr, 0L);
  734. }
  735. void* address() @property
  736. {
  737. makeVisible();
  738. scope (exit) makeInvisible();
  739. GC.addrOf(cast(void*)atomicLoad(_addr)); // Just a dummy call to the GC
  740. // in order to wait for any possible running
  741. // collection to complete (have unhook called).
  742. auto buf = atomicLoad(_addr);
  743. if ( isNull(buf) )
  744. return null;
  745. assert(isVisible(buf));
  746. return cast(void*) buf;
  747. }
  748. debug(signal) string toString()
  749. {
  750. import std.conv : text;
  751. return text(address);
  752. }
  753. private:
  754. long _addr;
  755. void makeVisible()
  756. {
  757. long buf, wbuf;
  758. do
  759. {
  760. buf = atomicLoad(_addr);
  761. wbuf = makeVisible(buf);
  762. }
  763. while(!cas(&_addr, buf, wbuf));
  764. }
  765. void makeInvisible()
  766. {
  767. long buf, wbuf;
  768. do
  769. {
  770. buf = atomicLoad(_addr);
  771. wbuf = makeInvisible(buf);
  772. }
  773. while(!cas(&_addr, buf, wbuf));
  774. }
  775. version(D_LP64)
  776. {
  777. static long makeVisible(long addr)
  778. {
  779. return ~addr;
  780. }
  781. static long makeInvisible(long addr)
  782. {
  783. return ~addr;
  784. }
  785. static bool isVisible(long addr)
  786. {
  787. return !(addr & (1L << (ptrdiff_t.sizeof*8-1)));
  788. }
  789. static bool isNull(long addr)
  790. {
  791. return ( addr == 0 || addr == (~0) );
  792. }
  793. }
  794. else
  795. {
  796. static long makeVisible(long addr)
  797. {
  798. auto addrHigh = (addr >> 32) & 0xffff;
  799. auto addrLow = addr & 0xffff;
  800. return addrHigh << 16 | addrLow;
  801. }
  802. static long makeInvisible(long addr)
  803. {
  804. auto addrHigh = ((addr >> 16) & 0x0000ffff) | 0xffff0000;
  805. auto addrLow = (addr & 0x0000ffff) | 0xffff0000;
  806. return (cast(long)addrHigh << 32) | addrLow;
  807. }
  808. static bool isVisible(long addr)
  809. {
  810. return !((addr >> 32) & 0xffffffff);
  811. }
  812. static bool isNull(long addr)
  813. {
  814. return ( addr == 0 || addr == ((0xffff0000L << 32) | 0xffff0000) );
  815. }
  816. }
  817. }
  818. /**
  819. * Provides a way of storing flags in unused parts of a typical D array.
  820. *
  821. * By unused I mean the highest bits of the length.
  822. * (We don't need to support 4 billion slots per signal with int
  823. * or 10^19 if length gets changed to 64 bits.)
  824. */
  825. private struct SlotArray {
  826. // Choose int for now, this saves 4 bytes on 64 bits.
  827. alias int lengthType;
  828. import std.bitmanip : bitfields;
  829. enum reservedBitsCount = 3;
  830. enum maxSlotCount = lengthType.max >> reservedBitsCount;
  831. SlotImpl[] slots() @property
  832. {
  833. return _ptr[0 .. length];
  834. }
  835. void slots(SlotImpl[] newSlots) @property
  836. {
  837. _ptr = newSlots.ptr;
  838. version(assert)
  839. {
  840. import std.conv : text;
  841. assert(newSlots.length <= maxSlotCount, text("Maximum slots per signal exceeded: ", newSlots.length, "/", maxSlotCount));
  842. }
  843. _blength.length &= ~maxSlotCount;
  844. _blength.length |= newSlots.length;
  845. }
  846. size_t length() @property
  847. {
  848. return _blength.length & maxSlotCount;
  849. }
  850. bool emitInProgress() @property
  851. {
  852. return _blength.emitInProgress;
  853. }
  854. void emitInProgress(bool val) @property
  855. {
  856. _blength.emitInProgress = val;
  857. }
  858. private:
  859. SlotImpl* _ptr;
  860. union BitsLength {
  861. mixin(bitfields!(
  862. bool, "", lengthType.sizeof*8-1,
  863. bool, "emitInProgress", 1
  864. ));
  865. lengthType length;
  866. }
  867. BitsLength _blength;
  868. }
  869. unittest {
  870. SlotArray arr;
  871. auto tmp = new SlotImpl[10];
  872. arr.slots = tmp;
  873. assert(arr.length == 10);
  874. assert(!arr.emitInProgress);
  875. arr.emitInProgress = true;
  876. assert(arr.emitInProgress);
  877. assert(arr.length == 10);
  878. assert(arr.slots is tmp);
  879. arr.slots = tmp;
  880. assert(arr.emitInProgress);
  881. assert(arr.length == 10);
  882. assert(arr.slots is tmp);
  883. debug (signal){ import std.stdio;
  884. writeln("Slot array tests passed!");
  885. }
  886. }
  887. unittest
  888. { // Check that above example really works ...
  889. debug (signal) import std.stdio;
  890. class MyObject
  891. {
  892. mixin(signal!(string, int)("valueChanged"));
  893. int value() @property { return _value; }
  894. int value(int v) @property
  895. {
  896. if (v != _value)
  897. {
  898. _value = v;
  899. // call all the connected slots with the two parameters
  900. _valueChanged.emit("setting new value", v);
  901. }
  902. return v;
  903. }
  904. private:
  905. int _value;
  906. }
  907. class Observer
  908. { // our slot
  909. void watch(string msg, int i)
  910. {
  911. debug (signal) writefln("Observed msg '%s' and value %s", msg, i);
  912. }
  913. }
  914. void watch(string msg, int i)
  915. {
  916. debug (signal) writefln("Globally observed msg '%s' and value %s", msg, i);
  917. }
  918. auto a = new MyObject;
  919. Observer o = new Observer;
  920. a.value = 3; // should not call o.watch()
  921. a.valueChanged.connect!"watch"(o); // o.watch is the slot
  922. a.value = 4; // should call o.watch()
  923. a.valueChanged.disconnect!"watch"(o); // o.watch is no longer a slot
  924. a.value = 5; // so should not call o.watch()
  925. a.valueChanged.connect!"watch"(o); // connect again
  926. // Do some fancy stuff:
  927. a.valueChanged.connect!Observer(o, (obj, msg, i) => obj.watch("Some other text I made up", i+1));
  928. a.valueChanged.strongConnect(&watch);
  929. a.value = 6; // should call o.watch()
  930. destroy(o); // destroying o should automatically disconnect it
  931. a.value = 7; // should not call o.watch()
  932. }
  933. unittest
  934. {
  935. debug (signal) import std.stdio;
  936. class Observer
  937. {
  938. void watch(string msg, int i)
  939. {
  940. //writefln("Observed msg '%s' and value %s", msg, i);
  941. captured_value = i;
  942. captured_msg = msg;
  943. }
  944. int captured_value;
  945. string captured_msg;
  946. }
  947. class SimpleObserver
  948. {
  949. void watchOnlyInt(int i) {
  950. captured_value = i;
  951. }
  952. int captured_value;
  953. }
  954. class Foo
  955. {
  956. @property int value() { return _value; }
  957. @property int value(int v)
  958. {
  959. if (v != _value)
  960. { _value = v;
  961. _extendedSig.emit("setting new value", v);
  962. //_simpleSig.emit(v);
  963. }
  964. return v;
  965. }
  966. mixin(signal!(string, int)("extendedSig"));
  967. //Signal!(int) simpleSig;
  968. private:
  969. int _value;
  970. }
  971. Foo a = new Foo;
  972. Observer o = new Observer;
  973. SimpleObserver so = new SimpleObserver;
  974. // check initial condition
  975. assert(o.captured_value == 0);
  976. assert(o.captured_msg == "");
  977. // set a value while no observation is in place
  978. a.value = 3;
  979. assert(o.captured_value == 0);
  980. assert(o.captured_msg == "");
  981. // connect the watcher and trigger it
  982. a.extendedSig.connect!"watch"(o);
  983. a.value = 4;
  984. assert(o.captured_value == 4);
  985. assert(o.captured_msg == "setting new value");
  986. // disconnect the watcher and make sure it doesn't trigger
  987. a.extendedSig.disconnect!"watch"(o);
  988. a.value = 5;
  989. assert(o.captured_value == 4);
  990. assert(o.captured_msg == "setting new value");
  991. //a.extendedSig.connect!Observer(o, (obj, msg, i) { obj.watch("Hahah", i); });
  992. a.extendedSig.connect!Observer(o, (obj, msg, i) => obj.watch("Hahah", i) );
  993. a.value = 7;
  994. debug (signal) stderr.writeln("After asignment!");
  995. assert(o.captured_value == 7);
  996. assert(o.captured_msg == "Hahah");
  997. a.extendedSig.disconnect(o); // Simply disconnect o, otherwise we would have to store the lamda somewhere if we want to disconnect later on.
  998. // reconnect the watcher and make sure it triggers
  999. a.extendedSig.connect!"watch"(o);
  1000. a.value = 6;
  1001. assert(o.captured_value == 6);
  1002. assert(o.captured_msg == "setting new value");
  1003. // destroy the underlying object and make sure it doesn't cause
  1004. // a crash or other problems
  1005. debug (signal) stderr.writefln("Disposing");
  1006. destroy(o);
  1007. debug (signal) stderr.writefln("Disposed");
  1008. a.value = 7;
  1009. }
  1010. unittest {
  1011. class Observer
  1012. {
  1013. int i;
  1014. long l;
  1015. string str;
  1016. void watchInt(string str, int i)
  1017. {
  1018. this.str = str;
  1019. this.i = i;
  1020. }
  1021. void watchLong(string str, long l)
  1022. {
  1023. this.str = str;
  1024. this.l = l;
  1025. }
  1026. }
  1027. class Bar
  1028. {
  1029. @property void value1(int v) { _s1.emit("str1", v); }
  1030. @property void value2(int v) { _s2.emit("str2", v); }
  1031. @property void value3(long v) { _s3.emit("str3", v); }
  1032. mixin(signal!(string, int) ("s1"));
  1033. mixin(signal!(string, int) ("s2"));
  1034. mixin(signal!(string, long)("s3"));
  1035. }
  1036. void test(T)(T a)
  1037. {
  1038. auto o1 = new Observer;
  1039. auto o2 = new Observer;
  1040. auto o3 = new Observer;
  1041. // connect the watcher and trigger it
  1042. a.s1.connect!"watchInt"(o1);
  1043. a.s2.connect!"watchInt"(o2);
  1044. a.s3.connect!"watchLong"(o3);
  1045. assert(!o1.i && !o1.l && !o1.str);
  1046. assert(!o2.i && !o2.l && !o2.str);
  1047. assert(!o3.i && !o3.l && !o3.str);
  1048. a.value1 = 11;
  1049. assert(o1.i == 11 && !o1.l && o1.str == "str1");
  1050. assert(!o2.i && !o2.l && !o2.str);
  1051. assert(!o3.i && !o3.l && !o3.str);
  1052. o1.i = -11; o1.str = "x1";
  1053. a.value2 = 12;
  1054. assert(o1.i == -11 && !o1.l && o1.str == "x1");
  1055. assert(o2.i == 12 && !o2.l && o2.str == "str2");
  1056. assert(!o3.i && !o3.l && !o3.str);
  1057. o2.i = -12; o2.str = "x2";
  1058. a.value3 = 13;
  1059. assert(o1.i == -11 && !o1.l && o1.str == "x1");
  1060. assert(o2.i == -12 && !o1.l && o2.str == "x2");
  1061. assert(!o3.i && o3.l == 13 && o3.str == "str3");
  1062. o3.l = -13; o3.str = "x3";
  1063. // disconnect the watchers and make sure it doesn't trigger
  1064. a.s1.disconnect!"watchInt"(o1);
  1065. a.s2.disconnect!"watchInt"(o2);
  1066. a.s3.disconnect!"watchLong"(o3);
  1067. a.value1 = 21;
  1068. a.value2 = 22;
  1069. a.value3 = 23;
  1070. assert(o1.i == -11 && !o1.l && o1.str == "x1");
  1071. assert(o2.i == -12 && !o1.l && o2.str == "x2");
  1072. assert(!o3.i && o3.l == -13 && o3.str == "x3");
  1073. // reconnect the watcher and make sure it triggers
  1074. a.s1.connect!"watchInt"(o1);
  1075. a.s2.connect!"watchInt"(o2);
  1076. a.s3.connect!"watchLong"(o3);
  1077. a.value1 = 31;
  1078. a.value2 = 32;
  1079. a.value3 = 33;
  1080. assert(o1.i == 31 && !o1.l && o1.str == "str1");
  1081. assert(o2.i == 32 && !o1.l && o2.str == "str2");
  1082. assert(!o3.i && o3.l == 33 && o3.str == "str3");
  1083. // destroy observers
  1084. destroy(o1);
  1085. destroy(o2);
  1086. destroy(o3);
  1087. a.value1 = 41;
  1088. a.value2 = 42;
  1089. a.value3 = 43;
  1090. }
  1091. test(new Bar);
  1092. class BarDerived: Bar
  1093. {
  1094. @property void value4(int v) { _s4.emit("str4", v); }
  1095. @property void value5(int v) { _s5.emit("str5", v); }
  1096. @property void value6(long v) { _s6.emit("str6", v); }
  1097. mixin(signal!(string, int) ("s4"));
  1098. mixin(signal!(string, int) ("s5"));
  1099. mixin(signal!(string, long)("s6"));
  1100. }
  1101. auto a = new BarDerived;
  1102. test!Bar(a);
  1103. test!BarDerived(a);
  1104. auto o4 = new Observer;
  1105. auto o5 = new Observer;
  1106. auto o6 = new Observer;
  1107. // connect the watcher and trigger it
  1108. a.s4.connect!"watchInt"(o4);
  1109. a.s5.connect!"watchInt"(o5);
  1110. a.s6.connect!"watchLong"(o6);
  1111. assert(!o4.i && !o4.l && !o4.str);
  1112. assert(!o5.i && !o5.l && !o5.str);
  1113. assert(!o6.i && !o6.l && !o6.str);
  1114. a.value4 = 44;
  1115. assert(o4.i == 44 && !o4.l && o4.str == "str4");
  1116. assert(!o5.i && !o5.l && !o5.str);
  1117. assert(!o6.i && !o6.l && !o6.str);
  1118. o4.i = -44; o4.str = "x4";
  1119. a.value5 = 45;
  1120. assert(o4.i == -44 && !o4.l && o4.str == "x4");
  1121. assert(o5.i == 45 && !o5.l && o5.str == "str5");
  1122. assert(!o6.i && !o6.l && !o6.str);
  1123. o5.i = -45; o5.str = "x5";
  1124. a.value6 = 46;
  1125. assert(o4.i == -44 && !o4.l && o4.str == "x4");
  1126. assert(o5.i == -45 && !o4.l && o5.str == "x5");
  1127. assert(!o6.i && o6.l == 46 && o6.str == "str6");
  1128. o6.l = -46; o6.str = "x6";
  1129. // disconnect the watchers and make sure it doesn't trigger
  1130. a.s4.disconnect!"watchInt"(o4);
  1131. a.s5.disconnect!"watchInt"(o5);
  1132. a.s6.disconnect!"watchLong"(o6);
  1133. a.value4 = 54;
  1134. a.value5 = 55;
  1135. a.value6 = 56;
  1136. assert(o4.i == -44 && !o4.l && o4.str == "x4");
  1137. assert(o5.i == -45 && !o4.l && o5.str == "x5");
  1138. assert(!o6.i && o6.l == -46 && o6.str == "x6");
  1139. // reconnect the watcher and make sure it triggers
  1140. a.s4.connect!"watchInt"(o4);
  1141. a.s5.connect!"watchInt"(o5);
  1142. a.s6.connect!"watchLong"(o6);
  1143. a.value4 = 64;
  1144. a.value5 = 65;
  1145. a.value6 = 66;
  1146. assert(o4.i == 64 && !o4.l && o4.str == "str4");
  1147. assert(o5.i == 65 && !o4.l && o5.str == "str5");
  1148. assert(!o6.i && o6.l == 66 && o6.str == "str6");
  1149. // destroy observers
  1150. destroy(o4);
  1151. destroy(o5);
  1152. destroy(o6);
  1153. a.value4 = 44;
  1154. a.value5 = 45;
  1155. a.value6 = 46;
  1156. }
  1157. unittest
  1158. {
  1159. import std.stdio;
  1160. struct Property
  1161. {
  1162. alias value this;
  1163. mixin(signal!(int)("signal"));
  1164. @property int value()
  1165. {
  1166. return value_;
  1167. }
  1168. ref Property opAssign(int val)
  1169. {
  1170. debug (signal) writeln("Assigning int to property with signal: ", &this);
  1171. value_ = val;
  1172. _signal.emit(val);
  1173. return this;
  1174. }
  1175. private:
  1176. int value_;
  1177. }
  1178. void observe(int val)
  1179. {
  1180. debug (signal) writefln("observe: Wow! The value changed: %s", val);
  1181. }
  1182. class Observer
  1183. {
  1184. void observe(int val)
  1185. {
  1186. debug (signal) writefln("Observer: Wow! The value changed: %s", val);
  1187. debug (signal) writefln("Really! I must know I am an observer (old value was: %s)!", observed);
  1188. observed = val;
  1189. count++;
  1190. }
  1191. int observed;
  1192. int count;
  1193. }
  1194. Property prop;
  1195. void delegate(int) dg = (val) => observe(val);
  1196. prop.signal.strongConnect(dg);
  1197. assert(prop.signal._impl._slots.length==1);
  1198. Observer o=new Observer;
  1199. prop.signal.connect!"observe"(o);
  1200. assert(prop.signal._impl._slots.length==2);
  1201. debug (signal) writeln("Triggering on original property with value 8 ...");
  1202. prop=8;
  1203. assert(o.count==1);
  1204. assert(o.observed==prop);
  1205. }
  1206. unittest
  1207. {
  1208. debug (signal) import std.stdio;
  1209. import std.conv;
  1210. Signal!() s1;
  1211. void testfunc(int id)
  1212. {
  1213. throw new Exception(to!string(id));
  1214. }
  1215. s1.strongConnect(() => testfunc(0));
  1216. s1.strongConnect(() => testfunc(1));
  1217. s1.strongConnect(() => testfunc(2));
  1218. try s1.emit();
  1219. catch(Exception e)
  1220. {
  1221. Throwable t=e;
  1222. int i=0;
  1223. while (t)
  1224. {
  1225. debug (signal) stderr.writefln("Caught exception (this is fine)");
  1226. assert(to!int(t.msg)==i);
  1227. t=t.next;
  1228. i++;
  1229. }
  1230. assert(i==3);
  1231. }
  1232. }
  1233. unittest
  1234. {
  1235. class A
  1236. {
  1237. mixin(signal!(string, int)("s1"));
  1238. }
  1239. class B : A
  1240. {
  1241. mixin(signal!(string, int)("s2"));
  1242. }
  1243. }
  1244. unittest
  1245. {
  1246. struct Test
  1247. {
  1248. mixin(signal!int("a", Protection.package_));
  1249. mixin(signal!int("ap", Protection.private_));
  1250. mixin(signal!int("app", Protection.protected_));
  1251. mixin(signal!int("an", Protection.none));
  1252. }
  1253. static assert(signal!int("a", Protection.package_)=="package Signal!(int) _a;\nref RestrictedSignal!(int) a() { return _a;}\n");
  1254. static assert(signal!int("a", Protection.protected_)=="protected Signal!(int) _a;\nref RestrictedSignal!(int) a() { return _a;}\n");
  1255. static assert(signal!int("a", Protection.private_)=="private Signal!(int) _a;\nref RestrictedSignal!(int) a() { return _a;}\n");
  1256. static assert(signal!int("a", Protection.none)=="private Signal!(int) _a;\nref Signal!(int) a() { return _a;}\n");
  1257. debug (signal)
  1258. {
  1259. pragma(msg, signal!int("a", Protection.package_));
  1260. pragma(msg, signal!(int, string, int[int])("a", Protection.private_));
  1261. pragma(msg, signal!(int, string, int[int], float, double)("a", Protection.protected_));
  1262. pragma(msg, signal!(int, string, int[int], float, double, long)("a", Protection.none));
  1263. }
  1264. }
  1265. unittest // Test nested emit/removal/addition ...
  1266. {
  1267. Signal!() sig;
  1268. bool doEmit = true;
  1269. int counter = 0;
  1270. int slot3called = 0;
  1271. int slot3shouldcalled = 0;
  1272. void slot1()
  1273. {
  1274. doEmit = !doEmit;
  1275. if (!doEmit)
  1276. sig.emit();
  1277. }
  1278. void slot3()
  1279. {
  1280. slot3called++;
  1281. }
  1282. void slot2()
  1283. {
  1284. debug (signal) { import std.stdio; writefln("\nCALLED: %s, should called: %s", slot3called, slot3shouldcalled);}
  1285. assert (slot3called == slot3shouldcalled);
  1286. if ( ++counter < 100)
  1287. slot3shouldcalled += counter;
  1288. if ( counter < 100 )
  1289. sig.strongConnect(&slot3);
  1290. }
  1291. void slot4()
  1292. {
  1293. if ( counter == 100 )
  1294. sig.strongDisconnect(&slot3); // All connections dropped
  1295. }
  1296. sig.strongConnect(&slot1);
  1297. sig.strongConnect(&slot2);
  1298. sig.strongConnect(&slot4);
  1299. for (int i=0; i<1000; i++)
  1300. sig.emit();
  1301. debug (signal)
  1302. {
  1303. import std.stdio;
  1304. writeln("slot3called: ", slot3called);
  1305. }
  1306. }
  1307. /* vim: set ts=4 sw=4 expandtab : */