Howto:Use Property Tree Objects

From FlightGear wiki
Jump to navigation Jump to search
Cleanup.png This article may require cleanup to meet the quality standards of the wiki. Please improve this article if you can.

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, effects, nasal, threading etc).

Cquote1.png you will almost certainly want to refrain from using tied properties, for all the reasons discussed at: Howto:Use Property Tree Objects

James introduced the PropertyObject<> templates specifically to get rid of tied properties - which is going to be increasingly important in any rendering related code that may possibly run asynchronously (osg cull/update traversals), but is also going to greatly simplify other multi-threading efforts (think HLA).

The other issue obviously being that tied properties do not work nicely with many other subsystems (think Nasal), while propertyObjects help encapsulate state management.
— Hooray (Dec 16th, 2015). Re: Implementing moonlight (or not).
(powered by Instant-Cquotes)
Cquote2.png

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;


JSBSim

Cquote1.png JSBSim grabs a copy of the relevant simgear code to support properties once every couple of years. When JSBSim is built within FlightGear, we of course just use what's there in the FlightGear tree. But, when JSBSim is incorporated into other products, they use what we have in our codebase. I'm concerned that at some point the property system will evolve to the point where serious conflicts arise, and backwards compatibility is broken. We (JSBSim) depend on the property system.
Cquote2.png
Cquote1.png it's not really tenable to have pieces of the API froen in perpetuity - whether next year or in ten years, things evolve. [1] Either JSBSim needs to be prepared to stay in sync (eg, update from the Simgear code monthly or quarterly), or you need to decide that you want your own code, and fork it 'properly' - at which point we'd make a bridge in the FG side between JSBSim properties and SimGear ones - which is fine.
Cquote2.png
Cquote1.png tie() gets renamed to Tie() - doh. Since I assume changing every JSBSIm use of Tie() is a non-starter, we'll have to devise something special on the FG side, to keep tie() 'apparently' working for JSBSim - not ideal, but probably doable. Anyway, something for the future!
Cquote2.png
Cquote1.png I'm only proposing one non-backwards compatible change - the removal of tie/untie, and probably not in the next twelve months - certainly not the next six. I just checked and the JSBSim code makes extremely limited use of tie(), in a couple of source tiles total. If you'd rather not use the new property features, that's fine - it'll just mean more boilerplate code to use the existing APIs when tie() does finally cease to exist. (And you can carry on shipping a version of props.hxx/.cxx with JSBSim-standalone, that *does* include tie(), to avoid breaking existing users who might have tie in their code) As far as I'm concerned, all of 2) is my problem - if I remove tie(), then I have to fix all the previous users, including JSBSim. You don't need to do anything, and I don't expect any JSBSim breakage.
Cquote2.png
Cquote1.png [I looked] into replacing tied properties with propertyObjects (once I was made aware of them) in JSBSim. It should be doable, but the inclusion of boost in the new properties code was a setback. Adding boost as a dependency for JSBSim standalone would be ridiculous.
— erik (Dec 19th, 2015). Re: Effects and <use> for MP properties.
(powered by Instant-Cquotes)
Cquote2.png
Cquote1.png To be able to add propertyObjects to JSBSim I've updates the standalone mode of the properties code in SimGear. Since I think adding Boost as a dependency to JSBSim standalone I've also made sure the stand alone version does not use any code from Boost. To be able to do that I has to copy some code from the current version of the properties code in JSBSim but all in all I think it's a rather clean patch: http://www.adalin.com/ehtw.info/props-standalone.diff I would appreciate it if anyone would look at the patch and make or suggest any improvements. Then this code could go in JSBSim and both versions of the property code are largely in sync again.
— Erik Hofman (Dec 20th, 2015). [Flightgear-devel] (JSBSim) Standalone Properties.
(powered by Instant-Cquotes)
Cquote2.png


Cquote1.png For now I've only added the code but not used it anywhere. This has the advantage of getting (compiler) errors early before new functionality gets added. Most of the code remains the same, even the Tie functions are still there and for stand alone programs they are perfectly fine. But JSBSim will be used in a HLA environment soon because that's where FlightGear is heading and for that the new code is useful (accessing tied properties from multiple sources).
— Erik Hofman (Dec 25th, 2015). [Flightgear-devel] (JSBSim) Standalone Properties.
(powered by Instant-Cquotes)
Cquote2.png