comparison qt/d1/qt/Signal.d @ 276:501128ac7a2c

signals cleaned up correctly on receiver destruction
author maxter
date Tue, 29 Sep 2009 12:00:45 +0000
parents 073b9153ed8a
children 5df570e79cfc
comparison
equal deleted inserted replaced
275:bb0f228c27cd 276:501128ac7a2c
31 a = (cast(T*)crealloc(a.ptr, length * T.sizeof))[0..length]; 31 a = (cast(T*)crealloc(a.ptr, length * T.sizeof))[0..length];
32 if (!a.ptr) 32 if (!a.ptr)
33 new OutOfMemoryException(__FILE__, __LINE__); 33 new OutOfMemoryException(__FILE__, __LINE__);
34 } 34 }
35 35
36 unittest 36 void append(T)(ref T[] a, T element)
37 { 37 {
38 int[] a; 38 auto newLen = a.length + 1;
39 realloc(a, 16); 39 a = (cast(T*)crealloc(a.ptr, newLen * T.sizeof))[0..newLen];
40 assert(a.length == 16); 40 if (!a.ptr)
41 foreach (i, ref e; a) 41 new OutOfMemoryException(__FILE__, __LINE__);
42 e = i; 42 a[newLen - 1] = element;
43 realloc(a, 4096); 43 }
44 assert(a.length == 4096); 44
45 foreach (i, e; a[0..16]) 45 void move(T)(ref T[] a, size_t src, size_t dest, size_t length)
46 assert(e == i); 46 {
47 cfree(a.ptr); 47 if (a.length > 1)
48 memmove(a.ptr + dest, a.ptr + src, length * T.sizeof);
49 }
50
51 // COMPILER BUG: Though this is private cannot name it 'remove' because of conflicts
52 // with Array.remove
53 void erase(T)(ref T[] a, size_t i)
54 {
55 auto newLen = a.length - 1;
56 move(a, i + 1, i, newLen);
57 realloc(a, newLen);
58 }
59
60 version (QtdUnittest)
61 {
62 unittest
63 {
64 int[] a;
65 realloc(a, 16);
66 assert(a.length == 16);
67 foreach (i, ref e; a)
68 e = i;
69 realloc(a, 4096);
70 assert(a.length == 4096);
71 foreach (i, e; a[0..16])
72 assert(e == i);
73 cfree(a.ptr);
74 }
48 } 75 }
49 76
50 // TODO: This one should be replaced with an appropriate library function 77 // TODO: This one should be replaced with an appropriate library function
51 char[] __toString(long v) 78 char[] __toString(long v)
52 { 79 {
90 return r; 117 return r;
91 } 118 }
92 119
93 mixin (genSTuple); 120 mixin (genSTuple);
94 template at(size_t i) { mixin("alias _" ~ ToString!(i) ~ " at;"); }; 121 template at(size_t i) { mixin("alias _" ~ ToString!(i) ~ " at;"); };
95 }
96
97 void move(T)(ref T[] a, size_t src, size_t dest, size_t length)
98 {
99 if (a.length > 1)
100 memmove(a.ptr + dest, a.ptr + src, length * T.sizeof);
101 } 122 }
102 123
103 enum SignalEventId 124 enum SignalEventId
104 { 125 {
105 firstSlotConnected, 126 firstSlotConnected,
179 { 200 {
180 alias R Receiver; 201 alias R Receiver;
181 202
182 Receiver receiver; 203 Receiver receiver;
183 Dg invoker; 204 Dg invoker;
205 ConnectionFlags flags;
184 206
185 static if (is(Receiver == Dg)) 207 static if (is(Receiver == Dg))
186 { 208 {
187 static const isDelegate = true; 209 static const isDelegate = true;
188 210
189 void onReceiverDisposed(Object o)
190 {
191 assert (lock !is null);
192 synchronized(lock)
193 {
194 receiver.context = null;
195 receiver.funcptr = null;
196 }
197 }
198
199 // null if receiver doesn't point to a disposable object
200 Object lock;
201
202 bool isDisposed() 211 bool isDisposed()
203 { 212 {
204 return !receiver.funcptr; 213 return !receiver.funcptr;
205 } 214 }
215
216 void dispose()
217 {
218 receiver.funcptr = null;
219 receiver.context = null;
220 }
206 221
207 Object getObject() 222 Object getObject()
208 { 223 {
209 return lock ? _d_toObject(receiver.context) : null; 224 return flags & ConnectionFlags.NoObject || !receiver.context
225 ? null : _d_toObject(receiver.context);
210 } 226 }
211 } 227 }
212 else 228 else
213 static const isDelegate = false; 229 static const isDelegate = false;
214
215 static typeof(*this) opCall(Receiver r, Dg c)
216 {
217 typeof(*this) ret;
218 ret.receiver = r;
219 ret.invoker = c;
220 return ret;
221 }
222 } 230 }
223 231
224 enum SlotListId 232 enum SlotListId
225 { 233 {
226 Func, // function pointers 234 Func, // function pointers
293 s.changed.emit; // bar is called. 301 s.changed.emit; // bar is called.
294 s.changed.disconnect(&bar); // must be explicitly disconnected. 302 s.changed.disconnect(&bar); // must be explicitly disconnected.
295 } 303 }
296 ---- 304 ----
297 */ 305 */
298 public enum ConnectionFlags 306 public enum ConnectionFlags : ubyte
299 { 307 {
300 /// 308 ///
301 None, 309 None,
302 /** 310 /**
303 The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified). 311 The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified).
322 330
323 struct SlotList(SlotT, bool strong = false) 331 struct SlotList(SlotT, bool strong = false)
324 { 332 {
325 alias SlotT SlotType; 333 alias SlotT SlotType;
326 SlotType[] data; 334 SlotType[] data;
327 335
328 void length(size_t length) 336 void length(size_t length)
329 { 337 {
330 static if (strong) 338 static if (strong)
331 data.length = length; 339 data.length = length;
332 else 340 else
358 return data.length; 366 return data.length;
359 } 367 }
360 368
361 void free() 369 void free()
362 { 370 {
363 static if (SlotType.isDelegate)
364 {
365 foreach (ref slot; data)
366 {
367 if (auto obj = slot.getObject)
368 rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
369 }
370 }
371 static if (!strong) 371 static if (!strong)
372 cfree(data.ptr); 372 cfree(data.ptr);
373 } 373 }
374 } 374 }
375 375
376 public alias void delegate(int signalId, SignalEventId event) SignalEvent; 376 public alias void delegate(int signalId, SignalEventId event) SignalEvent;
377
378 struct Receivers
379 {
380 struct Data
381 {
382 Object object;
383 int refs;
384 }
385
386 Data[] data;
387 void add(Object receiver, DEvent disposeEvent)
388 {
389 foreach (ref d; data)
390 {
391 if (d.object is receiver)
392 {
393 d.refs++;
394 return;
395 }
396 }
397
398 append(data, Data(receiver, 1));
399 rt_attachDisposeEvent(receiver, disposeEvent);
400 }
401
402 void remove(Object receiver, DEvent disposeEvent)
403 {
404 foreach (i, ref d; data)
405 {
406 if (d.object is receiver)
407 {
408 assert (d.refs);
409 d.refs--;
410 if (!d.refs)
411 {
412 .erase(data, i);
413 rt_detachDisposeEvent(receiver, disposeEvent);
414 }
415 return;
416 }
417 }
418
419 assert (false);
420 }
421
422 // remove all refarences for receiver, receiver has been disposed
423 void removeAll(Object receiver)
424 {
425 foreach (i, ref d; data)
426 {
427 if (d.object is receiver)
428 {
429 .erase(data, i);
430 return;
431 }
432 }
433 }
434
435 // remove all references for all receivers, detaching dispose events
436 void free(DEvent disposeEvent)
437 {
438 foreach (i, ref d; data)
439 rt_detachDisposeEvent(d.object, disposeEvent);
440 cfree(data.ptr);
441 data = null;
442 }
443 }
377 444
378 struct SignalConnections 445 struct SignalConnections
379 { 446 {
380 bool isInUse; 447 bool isInUse;
381 448
428 foreach(i, e; slotLists.tupleof) 495 foreach(i, e; slotLists.tupleof)
429 lengths[i] = slotLists.at!(i).length; 496 lengths[i] = slotLists.at!(i).length;
430 } 497 }
431 498
432 SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot) 499 SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot)
433 { 500 {
434 return getSlotList!(slotListId).add(slot); 501 return getSlotList!(slotListId).add(slot);
435 } 502 }
436 503
437 void removeSlot(int slotListId)(int slotId) 504 void removeSlot(int slotListId)(int slotId)
438 { 505 {
439 slotLists.at!(slotListId).remove(slotId); 506 slotLists.at!(slotListId).remove(slotId);
440 } 507 }
441 508
442 void free() 509 void free()
443 { 510 {
444 foreach(i, e; slotLists.tupleof) 511 foreach(i, e; slotLists.tupleof)
445 { 512 {
446 static if (is(typeof(slotLists.at!(i).free))) 513 static if (is(typeof(slotLists.at!(i).free)))
447 slotLists.at!(i).free; 514 slotLists.at!(i).free;
515 }
516 }
517
518 void onReceiverDisposed(Object receiver)
519 {
520 foreach (i, e; slotLists.tupleof)
521 {
522 static if (slotLists.at!(i).SlotType.isDelegate)
523 {
524 foreach (ref slot; slotLists.at!(i).data)
525 {
526 if (slot.getObject is receiver)
527 slot.dispose;
528 }
529 }
448 } 530 }
449 } 531 }
450 532
451 template SlotListType(int slotListId) 533 template SlotListType(int slotListId)
452 { 534 {
479 */ 561 */
480 public Object signalSender() { 562 public Object signalSender() {
481 return signalSender_.val; 563 return signalSender_.val;
482 } 564 }
483 565
484 public class SignalHandler 566 public final class SignalHandler
485 { 567 {
486 SignalConnections[] connections; 568 SignalConnections[] connections;
569 Receivers receivers;
487 Object owner; 570 Object owner;
488 int blocked; 571 int blocked;
489 572
490 SignalEvent signalEvent; 573 SignalEvent signalEvent;
491 574
492 alias SignalConnections.SlotType SlotType; 575 alias SignalConnections.SlotType SlotType;
493 alias SignalConnections.ReceiverType ReceiverType; 576 alias SignalConnections.ReceiverType ReceiverType;
494 577
495 public this(Object owner_) { 578 public this(Object owner_) {
496 owner = owner_; 579 owner = owner_;
497 } 580 }
498 581
499 private SignalConnections* getConnections(int signalId) 582 private SignalConnections* getConnections(int signalId)
500 { 583 {
501 if (signalId < connections.length) 584 if (signalId < connections.length)
502 return &connections[signalId]; 585 return &connections[signalId];
503 return null; 586 return null;
504 } 587 }
505 588
506 private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver, 589 private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver,
507 Dg invoker) 590 Dg invoker, ConnectionFlags flags)
508 { 591 {
509 if (signalId >= connections.length) 592 if (signalId >= connections.length)
510 connections.length = signalId + 1; 593 connections.length = signalId + 1;
511 auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker)); 594 auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker, flags));
595
596 static if (slot.isDelegate)
597 {
598 if (!(flags & ConnectionFlags.NoObject))
599 receivers.add(_d_toObject(receiver.context), &onReceiverDisposed);
600 }
512 601
513 if (signalEvent && connections[signalId].slotCount == 1) 602 if (signalEvent && connections[signalId].slotCount == 1)
514 signalEvent(signalId, SignalEventId.firstSlotConnected); 603 signalEvent(signalId, SignalEventId.firstSlotConnected);
515 604
516 return slot; 605 return slot;
517 } 606 }
518 607
608 void onReceiverDisposed(Object receiver)
609 {
610 synchronized(this)
611 {
612 foreach(ref c; connections)
613 c.onReceiverDisposed(receiver);
614 receivers.removeAll(receiver);
615 }
616 }
617
519 private void removeSlot(int slotListId)(int signalId, int slotId) 618 private void removeSlot(int slotListId)(int signalId, int slotId)
520 { 619 {
620 auto slot = connections[signalId].getSlotList!(slotListId).get(slotId);
621 static if (slot.isDelegate)
622 {
623 if (auto obj = slot.getObject)
624 receivers.remove(obj, &onReceiverDisposed);
625 }
626
521 connections[signalId].removeSlot!(slotListId)(slotId); 627 connections[signalId].removeSlot!(slotListId)(slotId);
522 628
523 if (signalEvent && !connections[signalId].slotCount) 629 if (signalEvent && !connections[signalId].slotCount)
524 signalEvent(signalId, SignalEventId.lastSlotDisconnected); 630 signalEvent(signalId, SignalEventId.lastSlotDisconnected);
525 } 631 }
526 632
527 private SlotType!(slotListId)* addObjectSlot(int slotListId)(size_t signalId, Object obj, Dg receiver, 633
528 Dg invoker)
529 {
530 auto slot = addSlot!(slotListId)(signalId, receiver, invoker);
531 slot.lock = this;
532 rt_attachDisposeEvent(obj, &slot.onReceiverDisposed);
533 return slot;
534 }
535
536 size_t slotCount(int signalId) 634 size_t slotCount(int signalId)
537 { 635 {
538 synchronized(this) 636 synchronized(this)
539 { 637 {
540 auto con = getConnections(signalId); 638 auto con = getConnections(signalId);
542 return con.slotCount; 640 return con.slotCount;
543 return 0; 641 return 0;
544 } 642 }
545 } 643 }
546 644
547 void connect(Receiver)(size_t signalId, Receiver receiver, 645 void connect(Receiver)(int signalId, Receiver receiver,
548 Dg invoker, ConnectionFlags flags) 646 Dg invoker, ConnectionFlags flags)
549 { 647 {
550 synchronized(this) 648 synchronized(this)
551 { 649 {
552 static if (is(typeof(receiver.context))) 650 static if (is(typeof(receiver.context)))
553 { 651 {
554 Object obj; 652 Object obj;
555 if ((flags & ConnectionFlags.NoObject) || (obj = _d_toObject(receiver.context)) is null) 653 if ((flags & ConnectionFlags.NoObject))
556 { 654 {
557 // strong by default 655 // strong by default
558 if (flags & ConnectionFlags.Weak) 656 if (flags & ConnectionFlags.Weak)
559 addSlot!(SlotListId.Weak)(signalId, receiver, invoker); 657 addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags);
560 else 658 else
561 addSlot!(SlotListId.Strong)(signalId, receiver, invoker); 659 addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags);
562 } 660 }
563 else 661 else
564 { 662 {
565 // weak by default 663 // weak by default
566 if (flags & ConnectionFlags.Strong) 664 if (flags & ConnectionFlags.Strong)
567 addObjectSlot!(SlotListId.Strong)(signalId, obj, receiver, invoker); 665 addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags);
568 else 666 else
569 addObjectSlot!(SlotListId.Weak)(signalId, obj, receiver, invoker); 667 addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags);
570 } 668 }
571 } 669 }
572 else 670 else
573 addSlot!(SlotListId.Func)(signalId, receiver, invoker); 671 {
672 flags |= ConnectionFlags.NoObject;
673 addSlot!(SlotListId.Func)(signalId, receiver, invoker, flags);
674 }
574 } 675 }
575 } 676 }
576 677
577 void disconnect(Receiver)(int signalId, Receiver receiver) 678 void disconnect(Receiver)(int signalId, Receiver receiver)
578 { 679 {
609 } 710 }
610 } 711 }
611 712
612 if (slot.receiver == receiver) 713 if (slot.receiver == receiver)
613 { 714 {
614 static if (slot.isDelegate)
615 {
616 if (auto obj = slot.getObject)
617 rt_detachDisposeEvent(obj, &slot.onReceiverDisposed);
618 }
619 removeSlot!(slotListId)(signalId, slotId); 715 removeSlot!(slotListId)(signalId, slotId);
620 break TOP; 716 break TOP;
621 } 717 }
622 718
623 slotId++; 719 slotId++;
712 } 808 }
713 } 809 }
714 810
715 ~this() 811 ~this()
716 { 812 {
813 receivers.free(&onReceiverDisposed);
717 foreach(ref c; connections) 814 foreach(ref c; connections)
718 c.free; 815 c.free;
719 } 816 }
720 } 817 }
721 818