simgear/props/props.*: Modification to calling of listeners when property values change.
We now default to not calling parent nodes' listeners when a node's value
changes.
This avoid overheads introduced by recent addition of locking - walking up the
parent nodes needs to lock/unlock at each step, and was previously happening
whenever any node in the entire tree had its value changed, causing noticable
slowdown.
Node creation/removal still calls parent nodes' listeners as before.
If a node has new attribute VALUE_CHANGED_UP set, we do call its parent node's
listeners.
New SGPropertyNode::Attribute's VALUE_CHANGED_UP and VALUE_CHANGED_DOWN:
VALUE_CHANGED_UP: if set, when fireValueChanged() is called we also call
our parent node's fireValueChanged().
VALUE_CHANGED_DOWN: if set, new and existing child nodes have their
VALUE_CHANGED_UP and VALUE_CHANGED_DOWN attributes set.
Once set, these attributes cannot be cleared (because to correctly handle
this would require reference counting).
SGPropertyLockControl(): added parent_listeners arg. If true, we always call
parent node's fireValueChanged() when a node value changes; this restores
previous property system behaviour. Default is false.
Extra overall timing diagnostics.
This commit is contained in:
+78
-20
@@ -2,10 +2,6 @@
|
|||||||
|
|
||||||
#include "props-unsafe.cxx"
|
#include "props-unsafe.cxx"
|
||||||
|
|
||||||
void SGPropertyLockControl(SGPropertyNode* active, SGPropertyNode* verbose, SGPropertyNode* timing)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
// props.cxx - implementation of a property list.
|
// props.cxx - implementation of a property list.
|
||||||
// Started Fall 2000 by David Megginson, david@megginson.com
|
// Started Fall 2000 by David Megginson, david@megginson.com
|
||||||
@@ -74,6 +70,11 @@ static bool s_property_locking_first_time = true;
|
|||||||
static bool s_property_locking_active = true;
|
static bool s_property_locking_active = true;
|
||||||
static bool s_property_locking_verbose = false;
|
static bool s_property_locking_verbose = false;
|
||||||
static bool s_property_timing_active = false;
|
static bool s_property_timing_active = false;
|
||||||
|
static bool s_property_change_parent_listeners = false;
|
||||||
|
|
||||||
|
static SGPropertyNode* s_main_tree_root = nullptr;
|
||||||
|
|
||||||
|
#include "props_io.hxx"
|
||||||
|
|
||||||
struct SGPropertyLockListener : SGPropertyChangeListener
|
struct SGPropertyLockListener : SGPropertyChangeListener
|
||||||
{
|
{
|
||||||
@@ -95,14 +96,21 @@ struct SGPropertyLockListener : SGPropertyChangeListener
|
|||||||
std::string m_name;
|
std::string m_name;
|
||||||
};
|
};
|
||||||
|
|
||||||
void SGPropertyLockControl(SGPropertyNode* active, SGPropertyNode* verbose, SGPropertyNode* timing)
|
void SGPropertyLockControl(
|
||||||
|
SGPropertyNode* active,
|
||||||
|
SGPropertyNode* verbose,
|
||||||
|
SGPropertyNode* timing,
|
||||||
|
SGPropertyNode* parent_listeners
|
||||||
|
)
|
||||||
{
|
{
|
||||||
std::cerr << __FILE__ << ":" << __LINE__ << ":"
|
std::cerr << __FILE__ << ":" << __LINE__ << ":"
|
||||||
<< " active: " << active->getPath()
|
<< " active: " << active->getPath()
|
||||||
<< " verbose: " << verbose->getPath()
|
<< " verbose: " << verbose->getPath()
|
||||||
<< " timing: " << timing->getPath()
|
<< " timing: " << timing->getPath()
|
||||||
|
<< " parent_listeners: " << parent_listeners->getPath()
|
||||||
<< "\n";
|
<< "\n";
|
||||||
|
|
||||||
|
s_main_tree_root = active->getRootNode();
|
||||||
active->setBoolValue(s_property_locking_active);
|
active->setBoolValue(s_property_locking_active);
|
||||||
active->addChangeListener(new SGPropertyLockListener(s_property_locking_active, "active"));
|
active->addChangeListener(new SGPropertyLockListener(s_property_locking_active, "active"));
|
||||||
|
|
||||||
@@ -111,6 +119,9 @@ void SGPropertyLockControl(SGPropertyNode* active, SGPropertyNode* verbose, SGPr
|
|||||||
|
|
||||||
timing->setBoolValue(s_property_timing_active);
|
timing->setBoolValue(s_property_timing_active);
|
||||||
timing->addChangeListener(new SGPropertyLockListener(s_property_timing_active, "timing"));
|
timing->addChangeListener(new SGPropertyLockListener(s_property_timing_active, "timing"));
|
||||||
|
|
||||||
|
parent_listeners->setBoolValue(s_property_change_parent_listeners);
|
||||||
|
parent_listeners->addChangeListener(new SGPropertyLockListener(s_property_change_parent_listeners, "parent-listeners"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef SG_PROPS_GATHER_TIMING
|
#undef SG_PROPS_GATHER_TIMING
|
||||||
@@ -233,6 +244,10 @@ struct ScopedTime
|
|||||||
<< " average time=" << (*m_total) / (*m_n)
|
<< " average time=" << (*m_total) / (*m_n)
|
||||||
<< " t_projected=" << t_projected << " (" << t_projected_fraction * 100 << "%)"
|
<< " t_projected=" << t_projected << " (" << t_projected_fraction * 100 << "%)"
|
||||||
<< " n_per_sec_projected=" << n_per_sec_projected
|
<< " n_per_sec_projected=" << n_per_sec_projected
|
||||||
|
<< " raw:"
|
||||||
|
<< " t=" << (t - ScopedTime_t0)
|
||||||
|
<< " ScopedTime_count=" << ScopedTime_count
|
||||||
|
<< " ScopedTime_count/(t - ScopedTime_t0)=" << (ScopedTime_count / (t - ScopedTime_t0))
|
||||||
<< "\n";
|
<< "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1086,6 +1101,18 @@ struct SGPropertyNodeImpl
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
appendNode(SGPropertyLockExclusive& exclusive, SGPropertyNode& parent, SGPropertyNode* child)
|
||||||
|
{
|
||||||
|
if (parent._attr & SGPropertyNode::VALUE_CHANGED_DOWN)
|
||||||
|
{
|
||||||
|
// Propogate to child nodea.
|
||||||
|
child->setAttribute(SGPropertyNode::VALUE_CHANGED_DOWN, true);
|
||||||
|
child->setAttribute(SGPropertyNode::VALUE_CHANGED_UP, true);
|
||||||
|
}
|
||||||
|
parent._children.push_back(child);
|
||||||
|
}
|
||||||
|
|
||||||
static SGPropertyNode*
|
static SGPropertyNode*
|
||||||
getChildImplCreate(SGPropertyLockExclusive& exclusive, SGPropertyNode& node0, const char* begin, const char* end, int index)
|
getChildImplCreate(SGPropertyLockExclusive& exclusive, SGPropertyNode& node0, const char* begin, const char* end, int index)
|
||||||
{
|
{
|
||||||
@@ -1097,7 +1124,7 @@ struct SGPropertyNodeImpl
|
|||||||
else {
|
else {
|
||||||
// REVIEW: Memory Leak - 2,028 (1,976 direct, 52 indirect) bytes in 13 blocks are definitely lost
|
// REVIEW: Memory Leak - 2,028 (1,976 direct, 52 indirect) bytes in 13 blocks are definitely lost
|
||||||
node = new SGPropertyNode(begin, end, index, &node0);
|
node = new SGPropertyNode(begin, end, index, &node0);
|
||||||
node0._children.push_back(node);
|
SGPropertyNodeImpl::appendNode(exclusive, node0, node);
|
||||||
exclusive.release();
|
exclusive.release();
|
||||||
node0.fireChildAdded(node);
|
node0.fireChildAdded(node);
|
||||||
exclusive.acquire();
|
exclusive.acquire();
|
||||||
@@ -1233,16 +1260,43 @@ struct SGPropertyNodeImpl
|
|||||||
return ((node._attr & attr) != 0);
|
return ((node._attr & attr) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setAttribute(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, SGPropertyNode::Attribute attr, bool state)
|
static void setChildrenUpDown(SGPropertyLockExclusive& exclusive, SGPropertyNode& node)
|
||||||
{
|
{
|
||||||
(state ? node._attr |= attr : node._attr &= ~attr);
|
for (SGPropertyNode* child: node._children) {
|
||||||
|
SGPropertyLockExclusive exclusive_child(*child);
|
||||||
|
setAttributes(
|
||||||
|
exclusive_child,
|
||||||
|
*child,
|
||||||
|
child->_attr | SGPropertyNode::VALUE_CHANGED_UP | SGPropertyNode::VALUE_CHANGED_DOWN
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setAttributes(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, int attr)
|
static void setAttributes(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, int attr)
|
||||||
{
|
{
|
||||||
|
// Preserve VALUE_CHANGED_UP and VALUE_CHANGED_DOWN if they are already
|
||||||
|
// set.
|
||||||
|
//
|
||||||
|
if (node._attr & SGPropertyNode::VALUE_CHANGED_UP) attr |= SGPropertyNode::VALUE_CHANGED_UP;
|
||||||
|
if (node._attr & SGPropertyNode::VALUE_CHANGED_DOWN) attr |= SGPropertyNode::VALUE_CHANGED_DOWN;
|
||||||
|
|
||||||
|
if ((attr & SGPropertyNode::VALUE_CHANGED_DOWN) && !(node._attr & SGPropertyNode::VALUE_CHANGED_DOWN)) {
|
||||||
|
// We are changing VALUE_CHANGED_DOWN flag from 0 to 1, so set
|
||||||
|
// VALUE_CHANGED_UP and VALUE_CHANGED_DOWN flags in all child
|
||||||
|
// nodes.
|
||||||
|
//
|
||||||
|
setChildrenUpDown(exclusive, node);
|
||||||
|
}
|
||||||
|
|
||||||
node._attr = attr;
|
node._attr = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void setAttribute(SGPropertyLockExclusive& exclusive, SGPropertyNode& node, SGPropertyNode::Attribute attr, bool state)
|
||||||
|
{
|
||||||
|
int attr_new = (state) ? (node._attr | attr) : (node._attr & ~attr);
|
||||||
|
setAttributes(exclusive, node, attr_new);
|
||||||
|
}
|
||||||
|
|
||||||
static int getAttributes(SGPropertyLock& lock, SGPropertyNode& node)
|
static int getAttributes(SGPropertyLock& lock, SGPropertyNode& node)
|
||||||
{
|
{
|
||||||
return node._attr;
|
return node._attr;
|
||||||
@@ -1862,14 +1916,18 @@ struct SGPropertyNodeImpl
|
|||||||
self._listeners,
|
self._listeners,
|
||||||
[&](SGPropertyChangeListener* listener) { listener->valueChanged(node);}
|
[&](SGPropertyChangeListener* listener) { listener->valueChanged(node);}
|
||||||
);
|
);
|
||||||
SGPropertyNode* parent = self._parent;
|
if (s_property_change_parent_listeners || (self._attr & SGPropertyNode::VALUE_CHANGED_UP))
|
||||||
if (parent) {
|
{
|
||||||
exclusive.release();
|
SGPropertyNode* parent = self._parent;
|
||||||
|
if (parent)
|
||||||
{
|
{
|
||||||
SGPropertyLockExclusive parent_exclusive(*parent);
|
exclusive.release();
|
||||||
fireValueChanged(parent_exclusive, *parent, node);
|
{
|
||||||
|
SGPropertyLockExclusive parent_exclusive(*parent);
|
||||||
|
fireValueChanged(parent_exclusive, *parent, node);
|
||||||
|
}
|
||||||
|
exclusive.acquire();
|
||||||
}
|
}
|
||||||
exclusive.acquire();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2088,7 +2146,7 @@ SGPropertyNode::clearValue ()
|
|||||||
* Last used attribute
|
* Last used attribute
|
||||||
* Update as needed when enum Attribute is changed
|
* Update as needed when enum Attribute is changed
|
||||||
*/
|
*/
|
||||||
const int SGPropertyNode::LAST_USED_ATTRIBUTE = LISTENER_SAFE;
|
const int SGPropertyNode::LAST_USED_ATTRIBUTE = VALUE_CHANGED_DOWN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor: always creates a root node.
|
* Default constructor: always creates a root node.
|
||||||
@@ -2356,7 +2414,7 @@ SGPropertyNode::addChild(const char * name, int min_index, bool append)
|
|||||||
SGPropertyNode_ptr node;
|
SGPropertyNode_ptr node;
|
||||||
// REVIEW: Memory Leak - 152 bytes in 1 blocks are definitely lost
|
// REVIEW: Memory Leak - 152 bytes in 1 blocks are definitely lost
|
||||||
node = new SGPropertyNode(name, name + strlen(name), pos, this);
|
node = new SGPropertyNode(name, name + strlen(name), pos, this);
|
||||||
_children.push_back(node);
|
SGPropertyNodeImpl::appendNode(exclusive, *this, node);
|
||||||
SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node);
|
SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -2377,7 +2435,7 @@ SGPropertyNode_ptr SGPropertyNode::addChild(SGPropertyNode_ptr node, const std::
|
|||||||
node->_name = name;
|
node->_name = name;
|
||||||
node->_parent = this;
|
node->_parent = this;
|
||||||
node->_index = pos;
|
node->_index = pos;
|
||||||
_children.push_back(node);
|
SGPropertyNodeImpl::appendNode(exclusive, *this, node);
|
||||||
SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node);
|
SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -2421,7 +2479,7 @@ SGPropertyNode::addChildren( const std::string& name,
|
|||||||
{
|
{
|
||||||
SGPropertyNode_ptr node;
|
SGPropertyNode_ptr node;
|
||||||
node = new SGPropertyNode(name, index, this);
|
node = new SGPropertyNode(name, index, this);
|
||||||
_children.push_back(node);
|
SGPropertyNodeImpl::appendNode(exclusive, *this, node);
|
||||||
fireChildAdded(node);
|
fireChildAdded(node);
|
||||||
nodes.push_back(node);
|
nodes.push_back(node);
|
||||||
}
|
}
|
||||||
@@ -2552,7 +2610,7 @@ SGPropertyNode::getChild (const std::string& name, int index, bool create)
|
|||||||
// REVIEW: Memory Leak - 12,862 (11,856 direct, 1,006 indirect) bytes in 78 blocks are definitely lost
|
// REVIEW: Memory Leak - 12,862 (11,856 direct, 1,006 indirect) bytes in 78 blocks are definitely lost
|
||||||
SGPropertyNode* node = new SGPropertyNode(name, index, this);
|
SGPropertyNode* node = new SGPropertyNode(name, index, this);
|
||||||
// REVIEW: Memory Leak - 104,647 (8 direct, 104,639 indirect) bytes in 1 blocks are definitely lost
|
// REVIEW: Memory Leak - 104,647 (8 direct, 104,639 indirect) bytes in 1 blocks are definitely lost
|
||||||
_children.push_back(node);
|
SGPropertyNodeImpl::appendNode(*this, node);
|
||||||
fireChildAdded(node);
|
fireChildAdded(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@@ -2570,7 +2628,7 @@ SGPropertyNode::getChild (const std::string& name, int index, bool create)
|
|||||||
// REVIEW: Memory Leak - 12,862 (11,856 direct, 1,006 indirect) bytes in 78 blocks are definitely lost
|
// REVIEW: Memory Leak - 12,862 (11,856 direct, 1,006 indirect) bytes in 78 blocks are definitely lost
|
||||||
node = new SGPropertyNode(name, index, this);
|
node = new SGPropertyNode(name, index, this);
|
||||||
// REVIEW: Memory Leak - 104,647 (8 direct, 104,639 indirect) bytes in 1 blocks are definitely lost
|
// REVIEW: Memory Leak - 104,647 (8 direct, 104,639 indirect) bytes in 1 blocks are definitely lost
|
||||||
_children.push_back(node);
|
SGPropertyNodeImpl::appendNode(exclusive, *this, node);
|
||||||
SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node);
|
SGPropertyNodeImpl::fireChildAdded(exclusive, *this, this /*parent*/, node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-8
@@ -8,13 +8,13 @@
|
|||||||
*
|
*
|
||||||
* $Id$
|
* $Id$
|
||||||
*/
|
*/
|
||||||
|
#ifndef __PROPS_HXX
|
||||||
|
#define __PROPS_HXX
|
||||||
|
|
||||||
#ifdef SG_PROPS_UNTHREADSAFE
|
#ifdef SG_PROPS_UNTHREADSAFE
|
||||||
#include "props-unsafe.hxx"
|
#include "props-unsafe.hxx"
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#ifndef __PROPS_HXX
|
|
||||||
#define __PROPS_HXX
|
|
||||||
|
|
||||||
#ifndef PROPS_STANDALONE
|
#ifndef PROPS_STANDALONE
|
||||||
#define PROPS_STANDALONE 0
|
#define PROPS_STANDALONE 0
|
||||||
#endif
|
#endif
|
||||||
@@ -890,6 +890,8 @@ public:
|
|||||||
PRESERVE = 128,
|
PRESERVE = 128,
|
||||||
PROTECTED = 1 << 8,
|
PROTECTED = 1 << 8,
|
||||||
LISTENER_SAFE = 1 << 9, /// it's safe to listen to this property, even if it's tied
|
LISTENER_SAFE = 1 << 9, /// it's safe to listen to this property, even if it's tied
|
||||||
|
VALUE_CHANGED_UP = 1 << 10, // If true, value changes are propogated to parent's listeners.
|
||||||
|
VALUE_CHANGED_DOWN = 1 << 11, // If true, sets new child nodes' VALUE_CHANGED_DOWN and VALUE_CHANGED_UP.
|
||||||
// beware: if you add another attribute here,
|
// beware: if you add another attribute here,
|
||||||
// also update value of "LAST_USED_ATTRIBUTE".
|
// also update value of "LAST_USED_ATTRIBUTE".
|
||||||
};
|
};
|
||||||
@@ -1672,16 +1674,20 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // __PROPS_HXX
|
#endif // SG_PROPS_UNTHREADSAFE
|
||||||
|
|
||||||
// Sets property nodes that control property locking.
|
// Sets property nodes that control property locking.
|
||||||
//
|
//
|
||||||
// active: whether we use locking.
|
// active: whether we use locking.
|
||||||
// verbose: whether we detect and report on lock contention.
|
// verbose: whether we detect and report on lock contention.
|
||||||
// timing: whether we gather timing information (compiled-out by default).
|
// timing: whether we gather timing information (compiled-out by default).
|
||||||
|
// parent_listeners: whether to call parent nodes' listeners when property values change.
|
||||||
//
|
//
|
||||||
void SGPropertyLockControl(SGPropertyNode* active, SGPropertyNode* verbose, SGPropertyNode* timing);
|
void SGPropertyLockControl(
|
||||||
|
SGPropertyNode* active,
|
||||||
|
SGPropertyNode* verbose,
|
||||||
|
SGPropertyNode* timing,
|
||||||
|
SGPropertyNode* parent_listeners
|
||||||
|
);
|
||||||
|
|
||||||
#endif // SG_PROPS_UNTHREADSAFE
|
#endif // __PROPS_HXX
|
||||||
|
|
||||||
// end of props.hxx
|
|
||||||
|
|||||||
Reference in New Issue
Block a user