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