Mercurial > projects > qtd
comparison qt/d2/qt/Signal.d @ 276:501128ac7a2c
signals cleaned up correctly on receiver destruction
author | maxter |
---|---|
date | Tue, 29 Sep 2009 12:00:45 +0000 |
parents | cf6a4cd0e3f2 |
children | 5df570e79cfc |
comparison
equal
deleted
inserted
replaced
275:bb0f228c27cd | 276:501128ac7a2c |
---|---|
33 a = (cast(T*)crealloc(a.ptr, length * T.sizeof))[0..length]; | 33 a = (cast(T*)crealloc(a.ptr, length * T.sizeof))[0..length]; |
34 if (!a.ptr) | 34 if (!a.ptr) |
35 new OutOfMemoryError(__FILE__, __LINE__); | 35 new OutOfMemoryError(__FILE__, __LINE__); |
36 } | 36 } |
37 | 37 |
38 unittest | 38 |
39 { | 39 void append(T)(ref T[] a, T element) |
40 int[] a; | 40 { |
41 realloc(a, 16); | 41 auto newLen = a.length + 1; |
42 assert(a.length == 16); | 42 a = (cast(T*)crealloc(a.ptr, newLen * T.sizeof))[0..newLen]; |
43 foreach (i, ref e; a) | 43 if (!a.ptr) |
44 e = i; | 44 new OutOfMemoryError(__FILE__, __LINE__); |
45 realloc(a, 4096); | 45 a[newLen - 1] = element; |
46 assert(a.length == 4096); | 46 } |
47 foreach (i, e; a[0..16]) | 47 |
48 assert(e == i); | 48 void move(T)(ref T[] a, size_t src, size_t dest, size_t length) |
49 cfree(a.ptr); | 49 { |
50 } | 50 if (a.length > 1) |
51 | 51 memmove(a.ptr + dest, a.ptr + src, length * T.sizeof); |
52 } | |
53 | |
54 // COMPILER BUG: Though this is private cannot name it 'remove' because of conflicts | |
55 // with Array.remove | |
56 void erase(T)(ref T[] a, size_t i) | |
57 { | |
58 auto newLen = a.length - 1; | |
59 move(a, i + 1, i, newLen); | |
60 realloc(a, newLen); | |
61 } | |
62 | |
63 version (QtdUnittest) | |
64 { | |
65 unittest | |
66 { | |
67 int[] a; | |
68 realloc(a, 16); | |
69 assert(a.length == 16); | |
70 foreach (i, ref e; a) | |
71 e = i; | |
72 realloc(a, 4096); | |
73 assert(a.length == 4096); | |
74 foreach (i, e; a[0..16]) | |
75 assert(e == i); | |
76 cfree(a.ptr); | |
77 } | |
78 } | |
52 | 79 |
53 //TODO: should be in the standard library | 80 //TODO: should be in the standard library |
54 struct STuple(A...) | 81 struct STuple(A...) |
55 { | 82 { |
56 static string genSTuple() | 83 static string genSTuple() |
61 return r; | 88 return r; |
62 } | 89 } |
63 | 90 |
64 mixin (genSTuple); | 91 mixin (genSTuple); |
65 template at(size_t i) { mixin("alias _" ~ ToString!(i) ~ " at;"); }; | 92 template at(size_t i) { mixin("alias _" ~ ToString!(i) ~ " at;"); }; |
66 } | |
67 | |
68 void move(T)(ref T[] a, size_t src, size_t dest, size_t length) | |
69 { | |
70 if (a.length > 1) | |
71 memmove(a.ptr + dest, a.ptr + src, length * T.sizeof); | |
72 } | 93 } |
73 | 94 |
74 enum SignalEventId | 95 enum SignalEventId |
75 { | 96 { |
76 firstSlotConnected, | 97 firstSlotConnected, |
150 { | 171 { |
151 alias R Receiver; | 172 alias R Receiver; |
152 | 173 |
153 Receiver receiver; | 174 Receiver receiver; |
154 Dg invoker; | 175 Dg invoker; |
155 | 176 ConnectionFlags flags; |
177 | |
156 static if (is(Receiver == Dg)) | 178 static if (is(Receiver == Dg)) |
157 { | 179 { |
158 static const isDelegate = true; | 180 static const isDelegate = true; |
159 | 181 |
160 void onReceiverDisposed(Object o) | |
161 { | |
162 assert (lock !is null); | |
163 synchronized(lock) | |
164 { | |
165 receiver.context = null; | |
166 receiver.funcptr = null; | |
167 } | |
168 } | |
169 | |
170 // null if receiver doesn't point to a disposable object | |
171 Object lock; | |
172 | |
173 bool isDisposed() | 182 bool isDisposed() |
174 { | 183 { |
175 return !receiver.funcptr; | 184 return !receiver.funcptr; |
176 } | 185 } |
186 | |
187 void dispose() | |
188 { | |
189 receiver.funcptr = null; | |
190 receiver.context = null; | |
191 } | |
177 | 192 |
178 Object getObject() | 193 Object getObject() |
179 { | 194 { |
180 return lock ? _d_toObject(receiver.context) : null; | 195 return flags & ConnectionFlags.NoObject || !receiver.context |
196 ? null : _d_toObject(receiver.context); | |
181 } | 197 } |
182 } | 198 } |
183 else | 199 else |
184 static const isDelegate = false; | 200 static const isDelegate = false; |
185 } | 201 } |
256 s.changed.emit; // bar is called. | 272 s.changed.emit; // bar is called. |
257 s.changed.disconnect(&bar); // must be explicitly disconnected. | 273 s.changed.disconnect(&bar); // must be explicitly disconnected. |
258 } | 274 } |
259 ---- | 275 ---- |
260 */ | 276 */ |
261 public enum ConnectionFlags | 277 public enum ConnectionFlags : ubyte |
262 { | 278 { |
263 /// | 279 /// |
264 None, | 280 None, |
265 /** | 281 /** |
266 The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified). | 282 The receiver will be stored as weak reference (implied if ConnectionFlags.NoObject is not specified). |
321 return data.length; | 337 return data.length; |
322 } | 338 } |
323 | 339 |
324 void free() | 340 void free() |
325 { | 341 { |
326 static if (SlotType.isDelegate) | |
327 { | |
328 foreach (ref slot; data) | |
329 { | |
330 if (auto obj = slot.getObject) | |
331 rt_detachDisposeEvent(obj, &slot.onReceiverDisposed); | |
332 } | |
333 } | |
334 static if (!strong) | 342 static if (!strong) |
335 cfree(data.ptr); | 343 cfree(data.ptr); |
336 } | 344 } |
337 } | 345 } |
338 | 346 |
339 public alias void delegate(int signalId, SignalEventId event) SignalEvent; | 347 public alias void delegate(int signalId, SignalEventId event) SignalEvent; |
348 | |
349 struct Receivers | |
350 { | |
351 struct Data | |
352 { | |
353 Object object; | |
354 int refs; | |
355 } | |
356 | |
357 Data[] data; | |
358 void add(Object receiver, DEvent disposeEvent) | |
359 { | |
360 foreach (ref d; data) | |
361 { | |
362 if (d.object is receiver) | |
363 { | |
364 d.refs++; | |
365 return; | |
366 } | |
367 } | |
368 | |
369 append(data, Data(receiver, 1)); | |
370 rt_attachDisposeEvent(receiver, disposeEvent); | |
371 } | |
372 | |
373 void remove(Object receiver, DEvent disposeEvent) | |
374 { | |
375 foreach (i, ref d; data) | |
376 { | |
377 if (d.object is receiver) | |
378 { | |
379 assert (d.refs); | |
380 d.refs--; | |
381 if (!d.refs) | |
382 { | |
383 .erase(data, i); | |
384 rt_detachDisposeEvent(receiver, disposeEvent); | |
385 } | |
386 return; | |
387 } | |
388 } | |
389 | |
390 assert (false); | |
391 } | |
392 | |
393 // remove all refarences for receiver, receiver has been disposed | |
394 void removeAll(Object receiver) | |
395 { | |
396 foreach (i, ref d; data) | |
397 { | |
398 if (d.object is receiver) | |
399 { | |
400 .erase(data, i); | |
401 return; | |
402 } | |
403 } | |
404 } | |
405 | |
406 // remove all references for all receivers, detaching dispose events | |
407 void free(DEvent disposeEvent) | |
408 { | |
409 foreach (i, ref d; data) | |
410 rt_detachDisposeEvent(d.object, disposeEvent); | |
411 cfree(data.ptr); | |
412 data = null; | |
413 } | |
414 } | |
340 | 415 |
341 struct SignalConnections | 416 struct SignalConnections |
342 { | 417 { |
343 bool isInUse; | 418 bool isInUse; |
344 | 419 |
406 { | 481 { |
407 foreach(i, e; slotLists.tupleof) | 482 foreach(i, e; slotLists.tupleof) |
408 { | 483 { |
409 static if (is(typeof(slotLists.at!(i).free))) | 484 static if (is(typeof(slotLists.at!(i).free))) |
410 slotLists.at!(i).free; | 485 slotLists.at!(i).free; |
486 } | |
487 } | |
488 | |
489 void onReceiverDisposed(Object receiver) | |
490 { | |
491 foreach (i, e; slotLists.tupleof) | |
492 { | |
493 static if (slotLists.at!(i).SlotType.isDelegate) | |
494 { | |
495 foreach (ref slot; slotLists.at!(i).data) | |
496 { | |
497 if (slot.getObject is receiver) | |
498 slot.dispose; | |
499 } | |
500 } | |
411 } | 501 } |
412 } | 502 } |
413 | 503 |
414 template SlotListType(int slotListId) | 504 template SlotListType(int slotListId) |
415 { | 505 { |
438 */ | 528 */ |
439 public Object signalSender() { | 529 public Object signalSender() { |
440 return signalSender_; | 530 return signalSender_; |
441 } | 531 } |
442 | 532 |
443 public class SignalHandler | 533 public final class SignalHandler |
444 { | 534 { |
445 SignalConnections[] connections; | 535 SignalConnections[] connections; |
536 Receivers receivers; | |
446 Object owner; | 537 Object owner; |
447 int blocked; | 538 int blocked; |
448 | 539 |
449 SignalEvent signalEvent; | 540 SignalEvent signalEvent; |
450 | 541 |
461 return &connections[signalId]; | 552 return &connections[signalId]; |
462 return null; | 553 return null; |
463 } | 554 } |
464 | 555 |
465 private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver, | 556 private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver, |
466 Dg invoker) | 557 Dg invoker, ConnectionFlags flags) |
467 { | 558 { |
468 if (signalId >= connections.length) | 559 if (signalId >= connections.length) |
469 connections.length = signalId + 1; | 560 connections.length = signalId + 1; |
470 auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker)); | 561 auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker)); |
471 | 562 |
472 if (signalEvent && connections[signalId].slotCount == 1) | 563 static if (slot.isDelegate) |
564 { | |
565 if (!(flags & ConnectionFlags.NoObject)) | |
566 receivers.add(_d_toObject(receiver.context), &onReceiverDisposed); | |
567 } | |
568 | |
569 if (signalEvent && connections[signalId].slotCount == 1) | |
473 signalEvent(signalId, SignalEventId.firstSlotConnected); | 570 signalEvent(signalId, SignalEventId.firstSlotConnected); |
474 | 571 |
475 return slot; | 572 return slot; |
476 } | 573 } |
574 | |
575 void onReceiverDisposed(Object receiver) | |
576 { | |
577 synchronized(this) | |
578 { | |
579 foreach(ref c; connections) | |
580 c.onReceiverDisposed(receiver); | |
581 receivers.removeAll(receiver); | |
582 } | |
583 } | |
477 | 584 |
478 private void removeSlot(int slotListId)(int signalId, int slotId) | 585 private void removeSlot(int slotListId)(int signalId, int slotId) |
479 { | 586 { |
587 auto slot = connections[signalId].getSlotList!(slotListId).get(slotId); | |
588 static if (slot.isDelegate) | |
589 { | |
590 if (auto obj = slot.getObject) | |
591 receivers.remove(obj, &onReceiverDisposed); | |
592 } | |
593 | |
480 connections[signalId].removeSlot!(slotListId)(slotId); | 594 connections[signalId].removeSlot!(slotListId)(slotId); |
481 | 595 |
482 if (signalEvent && !connections[signalId].slotCount) | 596 if (signalEvent && !connections[signalId].slotCount) |
483 signalEvent(signalId, SignalEventId.lastSlotDisconnected); | 597 signalEvent(signalId, SignalEventId.lastSlotDisconnected); |
484 } | |
485 | |
486 private SlotType!(slotListId)* addObjectSlot(int slotListId)(size_t signalId, Object obj, Dg receiver, | |
487 Dg invoker) | |
488 { | |
489 auto slot = addSlot!(slotListId)(signalId, receiver, invoker); | |
490 slot.lock = this; | |
491 rt_attachDisposeEvent(obj, &slot.onReceiverDisposed); | |
492 return slot; | |
493 } | 598 } |
494 | 599 |
495 size_t slotCount(int signalId) | 600 size_t slotCount(int signalId) |
496 { | 601 { |
497 synchronized(this) | 602 synchronized(this) |
501 return con.slotCount; | 606 return con.slotCount; |
502 return 0; | 607 return 0; |
503 } | 608 } |
504 } | 609 } |
505 | 610 |
506 void connect(Receiver)(size_t signalId, Receiver receiver, | 611 void connect(Receiver)(int signalId, Receiver receiver, |
507 Dg invoker, ConnectionFlags flags) | 612 Dg invoker, ConnectionFlags flags) |
508 { | 613 { |
509 synchronized(this) | 614 synchronized(this) |
510 { | 615 { |
511 static if (is(typeof(receiver.context))) | 616 static if (is(typeof(receiver.context))) |
512 { | 617 { |
513 Object obj; | 618 Object obj; |
514 if ((flags & ConnectionFlags.NoObject) || (obj = _d_toObject(receiver.context)) is null) | 619 if ((flags & ConnectionFlags.NoObject)) |
515 { | 620 { |
516 // strong by default | 621 // strong by default |
517 if (flags & ConnectionFlags.Weak) | 622 if (flags & ConnectionFlags.Weak) |
518 addSlot!(SlotListId.Weak)(signalId, receiver, invoker); | 623 addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags); |
519 else | 624 else |
520 addSlot!(SlotListId.Strong)(signalId, receiver, invoker); | 625 addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags); |
521 } | 626 } |
522 else | 627 else |
523 { | 628 { |
524 // weak by default | 629 // weak by default |
525 if (flags & ConnectionFlags.Strong) | 630 if (flags & ConnectionFlags.Strong) |
526 addObjectSlot!(SlotListId.Strong)(signalId, obj, receiver, invoker); | 631 addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags); |
527 else | 632 else |
528 addObjectSlot!(SlotListId.Weak)(signalId, obj, receiver, invoker); | 633 addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags); |
529 } | 634 } |
530 } | 635 } |
531 else | 636 else |
532 addSlot!(SlotListId.Func)(signalId, receiver, invoker); | 637 addSlot!(SlotListId.Func)(signalId, receiver, invoker, flags); |
533 } | 638 } |
534 } | 639 } |
535 | 640 |
536 void disconnect(Receiver)(int signalId, Receiver receiver) | 641 void disconnect(Receiver)(int signalId, Receiver receiver) |
537 { | 642 { |
568 } | 673 } |
569 } | 674 } |
570 | 675 |
571 if (slot.receiver == receiver) | 676 if (slot.receiver == receiver) |
572 { | 677 { |
573 static if (slot.isDelegate) | |
574 { | |
575 if (auto obj = slot.getObject) | |
576 rt_detachDisposeEvent(obj, &slot.onReceiverDisposed); | |
577 } | |
578 removeSlot!(slotListId)(signalId, slotId); | 678 removeSlot!(slotListId)(signalId, slotId); |
579 break TOP; | 679 break TOP; |
580 } | 680 } |
581 | 681 |
582 slotId++; | 682 slotId++; |
671 } | 771 } |
672 } | 772 } |
673 | 773 |
674 ~this() | 774 ~this() |
675 { | 775 { |
776 receivers.free(&onReceiverDisposed); | |
676 foreach(ref c; connections) | 777 foreach(ref c; connections) |
677 c.free; | 778 c.free; |
678 } | 779 } |
679 } | 780 } |
680 | 781 |