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