Howto:Use Property Tree Objects: Difference between revisions

From FlightGear wiki
Jump to navigation Jump to search
m (cleanup needed)
Line 9: Line 9:
When David Megginson came up with the SGPropertyChangeListener API, he wanted it to be a viable alternative to tying, for these very reasons:
When David Megginson came up with the SGPropertyChangeListener API, he wanted it to be a viable alternative to tying, for these very reasons:


* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg03176.html
{{cquote|<nowiki>The value-changed events will be fired automatically
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06978.html
unless the property is tied, in which case the controlling code must
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06993.html
invoke it specifically (or not).</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06993.html|title=<nowiki>[Flightgear-devel] More on property listeners</nowiki>|author=<nowiki>David Megginson</nowiki>|date=<nowiki>Tue, 18 Jun 2002 13:37:01 -0700</nowiki>}}</ref>|<nowiki>David Megginson</nowiki>}}
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg19612.html


{{cquote|<nowiki>Certainly this point needs to be emphasized,
otherwise there will be a situation where someone moves some code from
using non-tied to tied props (for whatever reason) and the behavior of
every system that depends on those properties subtly changes. This is a
worst case scenario, naturally ...</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06975.html|title=<nowiki>Re: [Flightgear-devel] ANN: SGPropertyChangeListener</nowiki>|author=<nowiki>James Turner</nowiki>|date=<nowiki>Tue, 18 Jun 2002 00:56:03 -0700</nowiki>}}</ref>|<nowiki>James Turner</nowiki>}}


{{cquote|<nowiki>I'm getting less and less fond of tied properties as we go on.</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06978.html|title=<nowiki>Re: [Flightgear-devel] ANN: SGPropertyChangeListener</nowiki>|author=<nowiki>David Megginson</nowiki>|date=<nowiki>Tue, 18 Jun 2002 06:31:04 -0700</nowiki>}}</ref>|<nowiki>David Megginson</nowiki>}}


In particular, see David’s comments here, where he suggested to refrain from binding/tying properties:
{{cquote|<nowiki>Right now, I'm looking at listeners
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg03176.html
mainly as an alternative to tying, but soon we'll want to use them for
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg11876.html
other purposes as well.</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06978.html|title=<nowiki>Re: [Flightgear-devel] ANN: SGPropertyChangeListener</nowiki>|author=<nowiki>David Megginson</nowiki>|date=<nowiki>Tue, 18 Jun 2002 06:31:04 -0700</nowiki>}}</ref>|<nowiki>David Megginson</nowiki>}}


Note that some of these threads are as old as 10+ years meanwhile, and there’s still tons of code making use of tying.
{{cquote|<nowiki>I'm inclined to eliminate tying
altogether and have every module set properties explicitly; what does
everyone else think?</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg11876.html|title=<nowiki>Re: [Flightgear-devel] Live property picker</nowiki>|author=<nowiki>David Megginson</nowiki>|date=<nowiki>Wed, 05 Feb 2003 17:56:40 -0800</nowiki>}}</ref>|<nowiki>David Megginson</nowiki>}}
 
{{cquote|<nowiki>I think that I can do a little fancy stuff with
the listener to help with memory management -- if the abstract base
class keeps a back pointer to every property node it's listening to,
then the destructor could unregister it automatically to avoid
dangling pointers. </nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06978.html|title=<nowiki>Re: [Flightgear-devel] ANN: SGPropertyChangeListener</nowiki>|author=<nowiki>David Megginson</nowiki>|date=<nowiki>Tue, 18 Jun 2002 06:31:04 -0700</nowiki>}}</ref>|<nowiki>David Megginson</nowiki>}}
 
{{cquote|<nowiki>Actually, I've come to the conclusion that it's best just to leave
them in the property tree whenever possible, and to bind only where
necessary.  A lot of stuff, such as control positions, doesn't really
need to live in any C++ variable.  Since you're copying data from
JSBSim to FlightGear anyway, it shouldn't make much difference whether
you copy it into the property tree or into a C++ variable.</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg03176.html|title=<nowiki>re: [Flightgear-devel] Property Access</nowiki>|author=<nowiki>David Megginson</nowiki>|date=<nowiki>Tue, 26 Feb 2002 18:49:46 -0800</nowiki>}}</ref>|<nowiki>David Megginson</nowiki>}}
 
{{cquote|<nowiki>Andy Ross just punted the whole question
by using the fg(Get|Set)* methods directly, without saving pointers,
arguing (reasonably enough) that any extra overhead gets lost during
the wait for the graphics hardware to be ready for the next frame.  To
test this assumption, I hacked my copy of FlightGear to loop through
property accesses multiple times and watched the framerate.</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg03176.html|title=<nowiki>re: [Flightgear-devel] Property Access</nowiki>|author=<nowiki>David Megginson</nowiki>|date=<nowiki>Tue, 26 Feb 2002 18:49:46 -0800</nowiki>}}</ref>|<nowiki>David Megginson</nowiki>}}
 
{{cquote|<nowiki>The surprise here is that most of the loss comes not from the map
lookup (which is skipped by the SGPropertyNode pointer access) but
from the SGPropertyNode::getValue itself: at 10,000 accesses, the
first 39% of the overhead comes from getValue, and only the remaining
11% from the map lookup.  That's just wrong, but it's also good news,
because the map lookup should be easy to fix -- I'm doing a profiling
build now to help me hunt down the problem.  So far, we're making far
fewer than 500 property lookups per frame, so there is no noticable
drop, and you may be just as well off using fg(Get|Set)* directly for
now.</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg03176.html|title=<nowiki>re: [Flightgear-devel] Property Access</nowiki>|author=<nowiki>David Megginson</nowiki>|date=<nowiki>Tue, 26 Feb 2002 18:49:46 -0800</nowiki>}}</ref>|<nowiki>David Megginson</nowiki>}}
 
{{cquote|<nowiki>The problem, which was discussed at the time, was that certain properties (eg, 'bound' ones, I think) don't fire listeners correctly. David Megginson and I proposed a few schemes, which would allow keeping both the existing binding interface, but also the existing listener interface : they basically revolve around listeners which poll their SGProperty once per tick : so there is still polling, but it's centralized.</nowiki><ref>{{cite web |url=http://www.mail-archive.com/flightgear-devel@flightgear.org/msg19612.html|title=<nowiki>[Flightgear-devel] SGPropertyListener (was Re: [Flightgear-flightmodel] crash reporting)</nowiki>|author=<nowiki>James Turner</nowiki>|date=<nowiki>Fri, 19 Dec 2003 17:46:56 -0800</nowiki>}}</ref>|<nowiki>James Turner</nowiki>}}


This is even though, people generally tend to suggest not to use tying:
<references/>


* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06981.html
Note that some of these threads are as old as 10+ years meanwhile, and there’s still tons of code making use of tying.
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06993.html
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg02796.html
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg03176.html
* http://www.mail-archive.com/flightgear-devel@flightgear.org/msg03803.html


More than a decade ago, tied properties were a performance hack.
More than a decade ago, tied properties were a performance hack.

Revision as of 15:40, 20 September 2013

WIP.png Work in progress
This article or section will be worked on in the upcoming hours or days.
See history for the latest developments.

Background: Tied properties are problematic

There have been many issues reported due to tied properties, pretty much since the very beginning of tying. And even David Megginson himself suggested to abandon tying altogether. Even til today, tied properties still cause problems and error messages when re-initializing or terminating FlightGear.

There have been lots of discussions on phasing out tied properties completely, because using them often means that other features cannot work properly (think listeners, I/O protocols, threading etc).

When David Megginson came up with the SGPropertyChangeListener API, he wanted it to be a viable alternative to tying, for these very reasons:

Cquote1.png The value-changed events will be fired automatically unless the property is tied, in which case the controlling code must invoke it specifically (or not).[1]
— David Megginson
Cquote2.png
Cquote1.png Certainly this point needs to be emphasized, otherwise there will be a situation where someone moves some code from using non-tied to tied props (for whatever reason) and the behavior of every system that depends on those properties subtly changes. This is a worst case scenario, naturally ...[2]
— James Turner
Cquote2.png
Cquote1.png I'm getting less and less fond of tied properties as we go on.[3]
— David Megginson
Cquote2.png
Cquote1.png Right now, I'm looking at listeners mainly as an alternative to tying, but soon we'll want to use them for other purposes as well.[4]
— David Megginson
Cquote2.png
Cquote1.png I'm inclined to eliminate tying altogether and have every module set properties explicitly; what does everyone else think?[5]
— David Megginson
Cquote2.png
Cquote1.png I think that I can do a little fancy stuff with the listener to help with memory management -- if the abstract base class keeps a back pointer to every property node it's listening to, then the destructor could unregister it automatically to avoid dangling pointers. [6]
— David Megginson
Cquote2.png
Cquote1.png Actually, I've come to the conclusion that it's best just to leave them in the property tree whenever possible, and to bind only where necessary. A lot of stuff, such as control positions, doesn't really need to live in any C++ variable. Since you're copying data from JSBSim to FlightGear anyway, it shouldn't make much difference whether you copy it into the property tree or into a C++ variable.[7]
— David Megginson
Cquote2.png
Cquote1.png Andy Ross just punted the whole question by using the fg(Get|Set)* methods directly, without saving pointers, arguing (reasonably enough) that any extra overhead gets lost during the wait for the graphics hardware to be ready for the next frame. To test this assumption, I hacked my copy of FlightGear to loop through property accesses multiple times and watched the framerate.[8]
— David Megginson
Cquote2.png
Cquote1.png The surprise here is that most of the loss comes not from the map lookup (which is skipped by the SGPropertyNode pointer access) but from the SGPropertyNode::getValue itself: at 10,000 accesses, the first 39% of the overhead comes from getValue, and only the remaining 11% from the map lookup. That's just wrong, but it's also good news, because the map lookup should be easy to fix -- I'm doing a profiling build now to help me hunt down the problem. So far, we're making far fewer than 500 property lookups per frame, so there is no noticable drop, and you may be just as well off using fg(Get|Set)* directly for now.[9]
— David Megginson
Cquote2.png
Cquote1.png The problem, which was discussed at the time, was that certain properties (eg, 'bound' ones, I think) don't fire listeners correctly. David Megginson and I proposed a few schemes, which would allow keeping both the existing binding interface, but also the existing listener interface : they basically revolve around listeners which poll their SGProperty once per tick : so there is still polling, but it's centralized.[10]
— James Turner
Cquote2.png
  1. David Megginson (Tue, 18 Jun 2002 13:37:01 -0700). [Flightgear-devel] More on property listeners.
  2. James Turner (Tue, 18 Jun 2002 00:56:03 -0700). Re: [Flightgear-devel] ANN: SGPropertyChangeListener.
  3. David Megginson (Tue, 18 Jun 2002 06:31:04 -0700). Re: [Flightgear-devel] ANN: SGPropertyChangeListener.
  4. David Megginson (Tue, 18 Jun 2002 06:31:04 -0700). Re: [Flightgear-devel] ANN: SGPropertyChangeListener.
  5. David Megginson (Wed, 05 Feb 2003 17:56:40 -0800). Re: [Flightgear-devel] Live property picker.
  6. David Megginson (Tue, 18 Jun 2002 06:31:04 -0700). Re: [Flightgear-devel] ANN: SGPropertyChangeListener.
  7. David Megginson (Tue, 26 Feb 2002 18:49:46 -0800). re: [Flightgear-devel] Property Access.
  8. David Megginson (Tue, 26 Feb 2002 18:49:46 -0800). re: [Flightgear-devel] Property Access.
  9. David Megginson (Tue, 26 Feb 2002 18:49:46 -0800). re: [Flightgear-devel] Property Access.
  10. James Turner (Fri, 19 Dec 2003 17:46:56 -0800). [Flightgear-devel] SGPropertyListener (was Re: [Flightgear-flightmodel] crash reporting).

Note that some of these threads are as old as 10+ years meanwhile, and there’s still tons of code making use of tying.

More than a decade ago, tied properties were a performance hack. All the ideas of using the property tree for IPC between processes (i.e. using HLA) won’t work directly with tied properties.

If you really WANT tied properties, the same effect (but much cleaner) could be accomplished here by using a PropertyObject and extending the PropertyObject as required, for example by making it derive from SGPropertyChangeListener.

Introducing: Property Objects

Cquote1.png Ideally, many more things would use the listener interface, and thus any polling of properties would be removed from all the other code, and only done if required by the property listeners themselves. However, I think that large number of properties (all the JSBSim ones?) are of the 'bound' type, so quite a bit of polling would still occur (iff there's a listener on the property).[1]
— James Turner
Cquote2.png

It's not really tenable to have pieces of the API frozen in perpetuity - whether next year or in ten years, things evolve - especially, since the property code was designed over a decade ago, multi-core machines have become rather common!

There is a long term plan to improve multi-threading support, by enforcing subsystems to *only* communicate via the property tree, which has light-weight locks thanks to some work by Mathias. With a dependency graph between subsystems it would then become possible to run any 'clean' subsystem on a pool of worker threads (maybe just one, maybe more). [1]

For further information, please see: Property threading.

In general, FG has quite a few data dependencies internally which make multi-threading challenging right now - there's groundwork to make the data-dependencies more explicit (i.e, via the property tree) that has to happen before pieces can easily move to other threads [2].

Moving us along the road to supporting better thread- and process- separation of the simulator elements, James has added a first attempt at a 'propertyObject'.

This is a template wrapper for SGPropertyNode*, with conversion operators to/from the related primitive type.

#include <simgear/props/propertyObject.hxx>

simgear::PropertyObject<int> frameRate("/sim/rendering/fps");
int theFrameRate = frameRate; // maps to getIntValue
frameRate = 1234; // maps to setIntValue
if (frameRate == 789) {
 //...
}

and so on. (The std::string variant tries to convert nicely to char* too, where possible)

To the extent possible by C++, once you've declared a property object, you should be able to treat it exactly like a plain int/double/bool/string, but of course the actual storage is the property. (if you find areas where this falls down, of course let me know, or fix them)

The goal of the PropertyObject is that it's hard to use inefficiently - it lazily does the the property lookup, but caches the result.

It has another interesting feature, suggested by ThorstenB - if you try to read an invalid path, it doesn't silently succeed - it throws an exception! This is to avoid lurking typos in property paths, which has been an issue. (I will be adding a 'weak' variant, that takes a default value, for the cases where people want to read a property that might not exist)

Suggestions are most welcome - for example there's no read-only version at the moment, but there could be.

Apart from being a nicely short syntax, there is another goal - tied properties are a menace, for thread-safety, for un-tie-ing cleanly on subsystem shutdown, and for not firing change listeners. My hope here is to create something that has semantics that work in a multi-threaed or multi-process world - but which is as painless to use in code as a tied value. I think I've succeeded, but time will tell.

(One other thing I will be adding, probably to SGPropertyNode itself, is a nicer listener syntax, since I'm aware of various pieces of code that use tied setters as a value-changed notification - SGPropertyChangeListener is a rather cumbersome solution for this)

I'm not going to start converting tied properties to use propertyObjects next week, or next month, but certainly next year. The hope is that by that time, this code is mature, efficient and so on - so please try it, use it and fix it!


Predefined Typedefs:

  • typedef simgear::PropertyObject<double> SGPropObjDouble;
  • typedef simgear::PropertyObject<bool> SGPropObjBool;
  • typedef simgear::PropertyObject<std::string> SGPropObjString;
  • typedef simgear::PropertyObject<long> SGPropObjInt;