Browse Source

Performance improvement for Value change notifications.

tags/2021-05-28
jules 13 years ago
parent
commit
66ecd6bc7c
3 changed files with 92 additions and 60 deletions
  1. +79
    -18
      modules/juce_data_structures/values/juce_Value.cpp
  2. +2
    -4
      modules/juce_data_structures/values/juce_Value.h
  3. +11
    -38
      modules/juce_data_structures/values/juce_ValueTree.cpp

+ 79
- 18
modules/juce_data_structures/values/juce_Value.cpp View File

@@ -23,39 +23,100 @@
============================================================================== ==============================================================================
*/ */
class SharedValueSourceUpdater : public ReferenceCountedObject,
private AsyncUpdater
{
public:
SharedValueSourceUpdater() : insideCallback (false) {}
typedef ReferenceCountedObjectPtr<SharedValueSourceUpdater> Ptr;
void update (Value::ValueSource* source)
{
sourcesToUpdate.add (source);
if (! insideCallback)
triggerAsyncUpdate();
}
static SharedValueSourceUpdater* getOrCreateSharedUpdater()
{
Ptr& p = getSharedUpdater();
if (p == nullptr)
p = new SharedValueSourceUpdater();
return p;
}
static void releaseIfUnused()
{
if (Ptr& p = getSharedUpdater())
if (p->getReferenceCount() == 1)
p = nullptr;
}
private:
ReferenceCountedArray<Value::ValueSource> sourcesToUpdate;
bool insideCallback;
static Ptr& getSharedUpdater()
{
static Ptr updater;
return updater;
}
void handleAsyncUpdate()
{
int maxLoops = 10;
const ScopedValueSetter<bool> inside (insideCallback, true, false);
const Ptr localRef (this);
while (sourcesToUpdate.size() > 0 && --maxLoops >= 0)
{
ReferenceCountedArray<Value::ValueSource> sources;
sources.swapWithArray (sourcesToUpdate);
for (int i = 0; i < sources.size(); ++i)
sources.getObjectPointerUnchecked(i)->sendChangeMessage (true);
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SharedValueSourceUpdater);
};
Value::ValueSource::ValueSource() Value::ValueSource::ValueSource()
{ {
} }
Value::ValueSource::~ValueSource() Value::ValueSource::~ValueSource()
{ {
asyncUpdater = nullptr;
SharedValueSourceUpdater::releaseIfUnused();
} }
void Value::ValueSource::sendChangeMessage (const bool synchronous) void Value::ValueSource::sendChangeMessage (const bool synchronous)
{ {
if (synchronous)
{
// (hold a local reference to this object in case it's freed during the callbacks)
const ReferenceCountedObjectPtr<ValueSource> localRef (this);
const int numListeners = valuesWithListeners.size();
for (int i = valuesWithListeners.size(); --i >= 0;)
if (numListeners > 0)
{
if (synchronous)
{ {
Value* const v = valuesWithListeners[i];
asyncUpdater = nullptr;
const ReferenceCountedObjectPtr<ValueSource> localRef (this);
if (v != nullptr)
v->callListeners();
for (int i = numListeners; --i >= 0;)
if (Value* const v = valuesWithListeners[i])
v->callListeners();
}
else if (asyncUpdater == nullptr)
{
SharedValueSourceUpdater* const updater = SharedValueSourceUpdater::getOrCreateSharedUpdater();
asyncUpdater = updater;
updater->update (this);
} }
} }
else
{
if (valuesWithListeners.size() > 0)
triggerAsyncUpdate();
}
}
void Value::ValueSource::handleAsyncUpdate()
{
sendChangeMessage (true);
} }
//============================================================================== //==============================================================================


+ 2
- 4
modules/juce_data_structures/values/juce_Value.h View File

@@ -169,8 +169,7 @@ public:
of a ValueSource object. If you're feeling adventurous, you can create your own custom of a ValueSource object. If you're feeling adventurous, you can create your own custom
ValueSource classes to allow Value objects to represent your own custom data items. ValueSource classes to allow Value objects to represent your own custom data items.
*/ */
class JUCE_API ValueSource : public SingleThreadedReferenceCountedObject,
private AsyncUpdater
class JUCE_API ValueSource : public SingleThreadedReferenceCountedObject
{ {
public: public:
ValueSource(); ValueSource();
@@ -196,8 +195,7 @@ public:
//============================================================================== //==============================================================================
friend class Value; friend class Value;
SortedSet <Value*> valuesWithListeners; SortedSet <Value*> valuesWithListeners;
void handleAsyncUpdate();
ReferenceCountedObjectPtr<ReferenceCountedObject> asyncUpdater;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ValueSource);
}; };


+ 11
- 38
modules/juce_data_structures/values/juce_ValueTree.cpp View File

@@ -60,11 +60,8 @@ public:
void sendPropertyChangeMessage (ValueTree& tree, const Identifier& property) void sendPropertyChangeMessage (ValueTree& tree, const Identifier& property)
{ {
for (int i = valueTreesWithListeners.size(); --i >= 0;) for (int i = valueTreesWithListeners.size(); --i >= 0;)
{
ValueTree* const v = valueTreesWithListeners[i];
if (v != nullptr)
if (ValueTree* const v = valueTreesWithListeners[i])
v->listeners.call (&ValueTree::Listener::valueTreePropertyChanged, tree, property); v->listeners.call (&ValueTree::Listener::valueTreePropertyChanged, tree, property);
}
} }
void sendPropertyChangeMessage (const Identifier& property) void sendPropertyChangeMessage (const Identifier& property)
@@ -78,11 +75,8 @@ public:
void sendChildAddedMessage (ValueTree& tree, ValueTree& child) void sendChildAddedMessage (ValueTree& tree, ValueTree& child)
{ {
for (int i = valueTreesWithListeners.size(); --i >= 0;) for (int i = valueTreesWithListeners.size(); --i >= 0;)
{
ValueTree* const v = valueTreesWithListeners[i];
if (v != nullptr)
if (ValueTree* const v = valueTreesWithListeners[i])
v->listeners.call (&ValueTree::Listener::valueTreeChildAdded, tree, child); v->listeners.call (&ValueTree::Listener::valueTreeChildAdded, tree, child);
}
} }
void sendChildAddedMessage (ValueTree child) void sendChildAddedMessage (ValueTree child)
@@ -96,11 +90,8 @@ public:
void sendChildRemovedMessage (ValueTree& tree, ValueTree& child) void sendChildRemovedMessage (ValueTree& tree, ValueTree& child)
{ {
for (int i = valueTreesWithListeners.size(); --i >= 0;) for (int i = valueTreesWithListeners.size(); --i >= 0;)
{
ValueTree* const v = valueTreesWithListeners[i];
if (v != nullptr)
if (ValueTree* const v = valueTreesWithListeners[i])
v->listeners.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child); v->listeners.call (&ValueTree::Listener::valueTreeChildRemoved, tree, child);
}
} }
void sendChildRemovedMessage (ValueTree child) void sendChildRemovedMessage (ValueTree child)
@@ -114,11 +105,8 @@ public:
void sendChildOrderChangedMessage (ValueTree& tree) void sendChildOrderChangedMessage (ValueTree& tree)
{ {
for (int i = valueTreesWithListeners.size(); --i >= 0;) for (int i = valueTreesWithListeners.size(); --i >= 0;)
{
ValueTree* const v = valueTreesWithListeners[i];
if (v != nullptr)
if (ValueTree* const v = valueTreesWithListeners[i])
v->listeners.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree); v->listeners.call (&ValueTree::Listener::valueTreeChildOrderChanged, tree);
}
} }
void sendChildOrderChangedMessage() void sendChildOrderChangedMessage()
@@ -134,18 +122,12 @@ public:
ValueTree tree (this); ValueTree tree (this);
for (int j = children.size(); --j >= 0;) for (int j = children.size(); --j >= 0;)
{
SharedObject* const child = children.getObjectPointer (j);
if (child != nullptr)
if (SharedObject* const child = children.getObjectPointer (j))
child->sendParentChangeMessage(); child->sendParentChangeMessage();
}
for (int i = valueTreesWithListeners.size(); --i >= 0;) for (int i = valueTreesWithListeners.size(); --i >= 0;)
{
ValueTree* const v = valueTreesWithListeners[i];
if (v != nullptr)
if (ValueTree* const v = valueTreesWithListeners[i])
v->listeners.call (&ValueTree::Listener::valueTreeParentChanged, tree); v->listeners.call (&ValueTree::Listener::valueTreeParentChanged, tree);
}
} }
const var& getProperty (const Identifier& name) const noexcept const var& getProperty (const Identifier& name) const noexcept
@@ -167,9 +149,7 @@ public:
} }
else else
{ {
const var* const existingValue = properties.getVarPointer (name);
if (existingValue != nullptr)
if (const var* const existingValue = properties.getVarPointer (name))
{ {
if (*existingValue != newValue) if (*existingValue != newValue)
undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false)); undoManager->perform (new SetPropertyAction (this, name, newValue, *existingValue, false, false));
@@ -270,16 +250,10 @@ public:
bool isAChildOf (const SharedObject* const possibleParent) const noexcept bool isAChildOf (const SharedObject* const possibleParent) const noexcept
{ {
const SharedObject* p = parent;
while (p != nullptr)
{
for (const SharedObject* p = parent; p != nullptr; p = p->parent)
if (p == possibleParent) if (p == possibleParent)
return true; return true;
p = p->parent;
}
return false; return false;
} }
@@ -611,10 +585,9 @@ public:
UndoableAction* createCoalescedAction (UndoableAction* nextAction) UndoableAction* createCoalescedAction (UndoableAction* nextAction)
{ {
MoveChildAction* next = dynamic_cast <MoveChildAction*> (nextAction);
if (next != nullptr && next->parent == parent && next->startIndex == endIndex)
return new MoveChildAction (parent, startIndex, next->endIndex);
if (MoveChildAction* next = dynamic_cast <MoveChildAction*> (nextAction))
if (next->parent == parent && next->startIndex == endIndex)
return new MoveChildAction (parent, startIndex, next->endIndex);
return nullptr; return nullptr;
} }


Loading…
Cancel
Save