mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-07 04:10:08 +00:00
Fixed an issue iterating arrays of owned objects from object destructors
This commit is contained in:
parent
d5816ad186
commit
a1dc1b1fce
4 changed files with 152 additions and 26 deletions
|
|
@ -27,17 +27,49 @@ namespace juce
|
|||
|
||||
static struct OwnedArrayTest : public UnitTest
|
||||
{
|
||||
OwnedArrayTest() : UnitTest { "OwnedArray" } {}
|
||||
|
||||
struct Base
|
||||
{
|
||||
Base() = default;
|
||||
virtual ~Base() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Base)
|
||||
};
|
||||
|
||||
struct Derived : Base
|
||||
{
|
||||
Derived() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Derived)
|
||||
};
|
||||
|
||||
struct DestructorObj
|
||||
{
|
||||
DestructorObj (OwnedArrayTest& p,
|
||||
OwnedArray<DestructorObj>& arr)
|
||||
: parent (p), objectArray (arr)
|
||||
{}
|
||||
|
||||
~DestructorObj()
|
||||
{
|
||||
data = 0;
|
||||
|
||||
for (auto* o : objectArray)
|
||||
{
|
||||
parent.expect (o != nullptr);
|
||||
parent.expect (o != this);
|
||||
parent.expectEquals (o->data, 956);
|
||||
}
|
||||
}
|
||||
|
||||
OwnedArrayTest& parent;
|
||||
OwnedArray<DestructorObj>& objectArray;
|
||||
int data = 956;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj)
|
||||
};
|
||||
|
||||
OwnedArrayTest() : UnitTest ("OwnedArray", "Containers") {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
beginTest ("After converting move construction, ownership is transferred");
|
||||
|
|
@ -58,6 +90,34 @@ static struct OwnedArrayTest : public UnitTest
|
|||
|
||||
expectEquals (base.size(), 3);
|
||||
}
|
||||
|
||||
beginTest ("Iterate in destructor");
|
||||
{
|
||||
{
|
||||
OwnedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
}
|
||||
|
||||
OwnedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
while (! arr.isEmpty())
|
||||
arr.remove (0);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.removeRange (1, arr.size() - 3);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.set (500, new DestructorObj (*this, arr));
|
||||
}
|
||||
}
|
||||
} ownedArrayTest;
|
||||
|
||||
|
|
|
|||
|
|
@ -625,17 +625,16 @@ public:
|
|||
|
||||
if (numberToRemove > 0)
|
||||
{
|
||||
Array<ObjectClass*> objectsToDelete;
|
||||
|
||||
if (deleteObjects)
|
||||
{
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
{
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (values[i]);
|
||||
values[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
}
|
||||
}
|
||||
objectsToDelete.addArray (values.begin() + startIndex, numberToRemove);
|
||||
|
||||
values.removeElements (startIndex, numberToRemove);
|
||||
|
||||
for (auto& o : objectsToDelete)
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (o);
|
||||
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
|
@ -792,10 +791,14 @@ private:
|
|||
|
||||
void deleteAllObjects()
|
||||
{
|
||||
for (auto& e : values)
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (e);
|
||||
auto i = values.size();
|
||||
|
||||
values.clear();
|
||||
while (--i >= 0)
|
||||
{
|
||||
auto* e = values[i];
|
||||
values.removeElements (i, 1);
|
||||
ContainerDeletePolicy<ObjectClass>::destroy (e);
|
||||
}
|
||||
}
|
||||
|
||||
template <class OtherObjectClass, class OtherCriticalSection>
|
||||
|
|
|
|||
|
|
@ -97,6 +97,34 @@ public:
|
|||
for (auto o : derivedArray)
|
||||
expectEquals (o->getReferenceCount(), 3);
|
||||
}
|
||||
|
||||
beginTest ("Iterate in destructor");
|
||||
{
|
||||
{
|
||||
ReferenceCountedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
}
|
||||
|
||||
ReferenceCountedArray<DestructorObj> arr;
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
while (! arr.isEmpty())
|
||||
arr.remove (0);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.removeRange (1, arr.size() - 3);
|
||||
|
||||
for (int i = 0; i < 1025; ++i)
|
||||
arr.add (new DestructorObj (*this, arr));
|
||||
|
||||
arr.set (500, new DestructorObj (*this, arr));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -105,6 +133,8 @@ private:
|
|||
using Ptr = ReferenceCountedObjectPtr<TestBaseObj>;
|
||||
|
||||
TestBaseObj() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestBaseObj)
|
||||
};
|
||||
|
||||
struct TestDerivedObj : public TestBaseObj
|
||||
|
|
@ -112,6 +142,34 @@ private:
|
|||
using Ptr = ReferenceCountedObjectPtr<TestDerivedObj>;
|
||||
|
||||
TestDerivedObj() = default;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TestDerivedObj)
|
||||
};
|
||||
|
||||
struct DestructorObj : public ReferenceCountedObject
|
||||
{
|
||||
DestructorObj (ReferenceCountedArrayTests& p,
|
||||
ReferenceCountedArray<DestructorObj>& arr)
|
||||
: parent (p), objectArray (arr)
|
||||
{}
|
||||
|
||||
~DestructorObj()
|
||||
{
|
||||
data = 0;
|
||||
|
||||
for (auto* o : objectArray)
|
||||
{
|
||||
parent.expect (o != nullptr);
|
||||
parent.expect (o != this);
|
||||
parent.expectEquals (o->data, 374);
|
||||
}
|
||||
}
|
||||
|
||||
ReferenceCountedArrayTests& parent;
|
||||
ReferenceCountedArray<DestructorObj>& objectArray;
|
||||
int data = 374;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DestructorObj)
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -437,8 +437,9 @@ public:
|
|||
|
||||
if (indexToChange < values.size())
|
||||
{
|
||||
releaseObject (values[indexToChange]);
|
||||
auto* e = values[indexToChange];
|
||||
values[indexToChange] = newObject;
|
||||
releaseObject (e);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -570,9 +571,9 @@ public:
|
|||
|
||||
if (isPositiveAndBelow (indexToRemove, values.size()))
|
||||
{
|
||||
auto** e = values.begin() + indexToRemove;
|
||||
releaseObject (*e);
|
||||
auto* e = *(values.begin() + indexToRemove);
|
||||
values.removeElements (indexToRemove, 1);
|
||||
releaseObject (e);
|
||||
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
|
|
@ -595,10 +596,10 @@ public:
|
|||
|
||||
if (isPositiveAndBelow (indexToRemove, values.size()))
|
||||
{
|
||||
auto** e = values.begin() + indexToRemove;
|
||||
removedItem = *e;
|
||||
releaseObject (*e);
|
||||
auto* e = *(values.begin() + indexToRemove);
|
||||
removedItem = e;
|
||||
values.removeElements (indexToRemove, 1);
|
||||
releaseObject (e);
|
||||
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
|
|
@ -656,14 +657,14 @@ public:
|
|||
|
||||
if (numberToRemove > 0)
|
||||
{
|
||||
for (int i = startIndex; i < endIndex; ++i)
|
||||
{
|
||||
releaseObject (values[i]);
|
||||
values[i] = nullptr; // (in case one of the destructors accesses this array and hits a dangling pointer)
|
||||
}
|
||||
Array<ObjectClass*> objectsToRemove;
|
||||
objectsToRemove.addArray (values.begin() + startIndex, numberToRemove);
|
||||
|
||||
values.removeElements (startIndex, numberToRemove);
|
||||
|
||||
for (auto& o : objectsToRemove)
|
||||
releaseObject (o);
|
||||
|
||||
if ((values.size() << 1) < values.capacity())
|
||||
minimiseStorageOverheads();
|
||||
}
|
||||
|
|
@ -848,10 +849,14 @@ private:
|
|||
|
||||
void releaseAllObjects()
|
||||
{
|
||||
for (auto& v : values)
|
||||
releaseObject (v);
|
||||
auto i = values.size();
|
||||
|
||||
values.clear();
|
||||
while (--i >= 0)
|
||||
{
|
||||
auto* e = values[i];
|
||||
values.removeElements (i, 1);
|
||||
releaseObject (e);
|
||||
}
|
||||
}
|
||||
|
||||
static void releaseObject (ObjectClass* o)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue