Mercurial > projects > qtd
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 |