# HG changeset patch # User maxter # Date 1254225645 0 # Node ID 501128ac7a2c8dc8c63fd45f14367dc3495825e2 # Parent bb0f228c27cdef6ea0393a1d8ba4ac3f08651f4e signals cleaned up correctly on receiver destruction diff -r bb0f228c27cd -r 501128ac7a2c qt/d1/qt/Signal.d --- a/qt/d1/qt/Signal.d Mon Sep 28 05:53:47 2009 +0000 +++ b/qt/d1/qt/Signal.d Tue Sep 29 12:00:45 2009 +0000 @@ -33,18 +33,45 @@ new OutOfMemoryException(__FILE__, __LINE__); } -unittest +void append(T)(ref T[] a, T element) +{ + auto newLen = a.length + 1; + a = (cast(T*)crealloc(a.ptr, newLen * T.sizeof))[0..newLen]; + if (!a.ptr) + new OutOfMemoryException(__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) { - 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; + 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: This one should be replaced with an appropriate library function @@ -94,12 +121,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, @@ -181,44 +202,31 @@ 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 static const isDelegate = false; - - static typeof(*this) opCall(Receiver r, Dg c) - { - typeof(*this) ret; - ret.receiver = r; - ret.invoker = c; - return ret; - } } enum SlotListId @@ -295,7 +303,7 @@ } ---- */ -public enum ConnectionFlags +public enum ConnectionFlags : ubyte { /// None, @@ -324,7 +332,7 @@ { alias SlotT SlotType; SlotType[] data; - + void length(size_t length) { static if (strong) @@ -360,14 +368,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); } @@ -375,6 +375,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; @@ -430,7 +497,7 @@ } SlotType!(slotListId)* addSlot(int slotListId)(SlotType!(slotListId) slot) - { + { return getSlotList!(slotListId).add(slot); } @@ -438,15 +505,30 @@ { slotLists.at!(slotListId).remove(slotId); } - + void free() - { + { foreach(i, e; slotLists.tupleof) { static if (is(typeof(slotLists.at!(i).free))) 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) { @@ -481,9 +563,10 @@ return signalSender_.val; } -public class SignalHandler +public final class SignalHandler { SignalConnections[] connections; + Receivers receivers; Object owner; int blocked; @@ -493,7 +576,7 @@ alias SignalConnections.ReceiverType ReceiverType; public this(Object owner_) { - owner = owner_; + owner = owner_; } private SignalConnections* getConnections(int signalId) @@ -504,35 +587,50 @@ } 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)); + auto slot = connections[signalId].addSlot!(slotListId)(SlotType!(slotListId)(receiver, invoker, flags)); + + static if (slot.isDelegate) + { + if (!(flags & ConnectionFlags.NoObject)) + receivers.add(_d_toObject(receiver.context), &onReceiverDisposed); + } 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) @@ -544,7 +642,7 @@ } } - void connect(Receiver)(size_t signalId, Receiver receiver, + void connect(Receiver)(int signalId, Receiver receiver, Dg invoker, ConnectionFlags flags) { synchronized(this) @@ -552,25 +650,28 @@ 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); + { + flags |= ConnectionFlags.NoObject; + addSlot!(SlotListId.Func)(signalId, receiver, invoker, flags); + } } } @@ -611,11 +712,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; } @@ -714,6 +810,7 @@ ~this() { + receivers.free(&onReceiverDisposed); foreach(ref c; connections) c.free; } diff -r bb0f228c27cd -r 501128ac7a2c qt/d2/qt/Signal.d --- 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; }