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