Mercurial > projects > qtd
diff 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 |
line wrap: on
line diff
--- a/qt/d2/qt/Signal.d Mon Sep 28 05:53:47 2009 +0000 +++ b/qt/d2/qt/Signal.d Tue Sep 29 12:00:45 2009 +0000 @@ -35,20 +35,47 @@ new OutOfMemoryError(__FILE__, __LINE__); } -unittest + +void append(T)(ref T[] a, T element) { - int[] a; - realloc(a, 16); - assert(a.length == 16); - foreach (i, ref e; a) - e = i; - realloc(a, 4096); - assert(a.length == 4096); - foreach (i, e; a[0..16]) - assert(e == i); - cfree(a.ptr); + auto newLen = a.length + 1; + a = (cast(T*)crealloc(a.ptr, newLen * T.sizeof))[0..newLen]; + if (!a.ptr) + new OutOfMemoryError(__FILE__, __LINE__); + a[newLen - 1] = element; +} + +void move(T)(ref T[] a, size_t src, size_t dest, size_t length) +{ + if (a.length > 1) + memmove(a.ptr + dest, a.ptr + src, length * T.sizeof); } +// COMPILER BUG: Though this is private cannot name it 'remove' because of conflicts +// with Array.remove +void erase(T)(ref T[] a, size_t i) +{ + auto newLen = a.length - 1; + move(a, i + 1, i, newLen); + realloc(a, newLen); +} + +version (QtdUnittest) +{ + unittest + { + int[] a; + realloc(a, 16); + assert(a.length == 16); + foreach (i, ref e; a) + e = i; + realloc(a, 4096); + assert(a.length == 4096); + foreach (i, e; a[0..16]) + assert(e == i); + cfree(a.ptr); + } +} //TODO: should be in the standard library struct STuple(A...) @@ -65,12 +92,6 @@ template at(size_t i) { mixin("alias _" ~ ToString!(i) ~ " at;"); }; } -void move(T)(ref T[] a, size_t src, size_t dest, size_t length) -{ - if (a.length > 1) - memmove(a.ptr + dest, a.ptr + src, length * T.sizeof); -} - enum SignalEventId { firstSlotConnected, @@ -152,32 +173,27 @@ Receiver receiver; Dg invoker; - + ConnectionFlags flags; + static if (is(Receiver == Dg)) { static const isDelegate = true; - - void onReceiverDisposed(Object o) - { - assert (lock !is null); - synchronized(lock) - { - receiver.context = null; - receiver.funcptr = null; - } - } - - // null if receiver doesn't point to a disposable object - Object lock; - + bool isDisposed() { return !receiver.funcptr; } + + void dispose() + { + receiver.funcptr = null; + receiver.context = null; + } Object getObject() - { - return lock ? _d_toObject(receiver.context) : null; + { + return flags & ConnectionFlags.NoObject || !receiver.context + ? null : _d_toObject(receiver.context); } } else @@ -258,7 +274,7 @@ } ---- */ -public enum ConnectionFlags +public enum ConnectionFlags : ubyte { /// None, @@ -323,14 +339,6 @@ void free() { - static if (SlotType.isDelegate) - { - foreach (ref slot; data) - { - if (auto obj = slot.getObject) - rt_detachDisposeEvent(obj, &slot.onReceiverDisposed); - } - } static if (!strong) cfree(data.ptr); } @@ -338,6 +346,73 @@ public alias void delegate(int signalId, SignalEventId event) SignalEvent; +struct Receivers +{ + struct Data + { + Object object; + int refs; + } + + Data[] data; + void add(Object receiver, DEvent disposeEvent) + { + foreach (ref d; data) + { + if (d.object is receiver) + { + d.refs++; + return; + } + } + + append(data, Data(receiver, 1)); + rt_attachDisposeEvent(receiver, disposeEvent); + } + + void remove(Object receiver, DEvent disposeEvent) + { + foreach (i, ref d; data) + { + if (d.object is receiver) + { + assert (d.refs); + d.refs--; + if (!d.refs) + { + .erase(data, i); + rt_detachDisposeEvent(receiver, disposeEvent); + } + return; + } + } + + assert (false); + } + + // remove all refarences for receiver, receiver has been disposed + void removeAll(Object receiver) + { + foreach (i, ref d; data) + { + if (d.object is receiver) + { + .erase(data, i); + return; + } + } + } + + // remove all references for all receivers, detaching dispose events + void free(DEvent disposeEvent) + { + foreach (i, ref d; data) + rt_detachDisposeEvent(d.object, disposeEvent); + cfree(data.ptr); + data = null; + } +} + struct SignalConnections { bool isInUse; @@ -410,6 +485,21 @@ slotLists.at!(i).free; } } + + void onReceiverDisposed(Object receiver) + { + foreach (i, e; slotLists.tupleof) + { + static if (slotLists.at!(i).SlotType.isDelegate) + { + foreach (ref slot; slotLists.at!(i).data) + { + if (slot.getObject is receiver) + slot.dispose; + } + } + } + } template SlotListType(int slotListId) { @@ -440,9 +530,10 @@ return signalSender_; } -public class SignalHandler +public final class SignalHandler { SignalConnections[] connections; + Receivers receivers; Object owner; int blocked; @@ -463,35 +554,49 @@ } private SlotType!(slotListId)* addSlot(int slotListId)(int signalId, ReceiverType!(slotListId) receiver, - Dg invoker) + Dg invoker, ConnectionFlags flags) { if (signalId >= connections.length) connections.length = signalId + 1; auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker)); + + static if (slot.isDelegate) + { + if (!(flags & ConnectionFlags.NoObject)) + receivers.add(_d_toObject(receiver.context), &onReceiverDisposed); + } - if (signalEvent && connections[signalId].slotCount == 1) + if (signalEvent && connections[signalId].slotCount == 1) signalEvent(signalId, SignalEventId.firstSlotConnected); return slot; } + + void onReceiverDisposed(Object receiver) + { + synchronized(this) + { + foreach(ref c; connections) + c.onReceiverDisposed(receiver); + receivers.removeAll(receiver); + } + } private void removeSlot(int slotListId)(int signalId, int slotId) { + auto slot = connections[signalId].getSlotList!(slotListId).get(slotId); + static if (slot.isDelegate) + { + if (auto obj = slot.getObject) + receivers.remove(obj, &onReceiverDisposed); + } + connections[signalId].removeSlot!(slotListId)(slotId); if (signalEvent && !connections[signalId].slotCount) signalEvent(signalId, SignalEventId.lastSlotDisconnected); } - private SlotType!(slotListId)* addObjectSlot(int slotListId)(size_t signalId, Object obj, Dg receiver, - Dg invoker) - { - auto slot = addSlot!(slotListId)(signalId, receiver, invoker); - slot.lock = this; - rt_attachDisposeEvent(obj, &slot.onReceiverDisposed); - return slot; - } - size_t slotCount(int signalId) { synchronized(this) @@ -503,7 +608,7 @@ } } - void connect(Receiver)(size_t signalId, Receiver receiver, + void connect(Receiver)(int signalId, Receiver receiver, Dg invoker, ConnectionFlags flags) { synchronized(this) @@ -511,25 +616,25 @@ static if (is(typeof(receiver.context))) { Object obj; - if ((flags & ConnectionFlags.NoObject) || (obj = _d_toObject(receiver.context)) is null) + if ((flags & ConnectionFlags.NoObject)) { // strong by default if (flags & ConnectionFlags.Weak) - addSlot!(SlotListId.Weak)(signalId, receiver, invoker); + addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags); else - addSlot!(SlotListId.Strong)(signalId, receiver, invoker); + addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags); } else { // weak by default if (flags & ConnectionFlags.Strong) - addObjectSlot!(SlotListId.Strong)(signalId, obj, receiver, invoker); + addSlot!(SlotListId.Strong)(signalId, receiver, invoker, flags); else - addObjectSlot!(SlotListId.Weak)(signalId, obj, receiver, invoker); + addSlot!(SlotListId.Weak)(signalId, receiver, invoker, flags); } } else - addSlot!(SlotListId.Func)(signalId, receiver, invoker); + addSlot!(SlotListId.Func)(signalId, receiver, invoker, flags); } } @@ -570,11 +675,6 @@ if (slot.receiver == receiver) { - static if (slot.isDelegate) - { - if (auto obj = slot.getObject) - rt_detachDisposeEvent(obj, &slot.onReceiverDisposed); - } removeSlot!(slotListId)(signalId, slotId); break TOP; } @@ -673,6 +773,7 @@ ~this() { + receivers.free(&onReceiverDisposed); foreach(ref c; connections) c.free; }