Howto:Use Property Tree Objects
Multicore |
---|
Configuration |
Ongoing Efforts |
Proposals & RFCs |
Background |
For developers |
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).
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. |
When David Megginson came up with the SGPropertyChangeListener API, he wanted it to be a viable alternative to tying, for these very reasons:
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
|
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
|
I'm getting less and less fond of tied properties as we go on.[3] — David Megginson
|
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
|
I'm inclined to eliminate tying
altogether and have every module set properties explicitly; what does
everyone else think?[5] — David Megginson
|
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
|
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
|
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
|
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
|
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
|
- ↑ David Megginson (Tue, 18 Jun 2002 13:37:01 -0700). [Flightgear-devel] More on property listeners.
- ↑ James Turner (Tue, 18 Jun 2002 00:56:03 -0700). Re: [Flightgear-devel] ANN: SGPropertyChangeListener.
- ↑ David Megginson (Tue, 18 Jun 2002 06:31:04 -0700). Re: [Flightgear-devel] ANN: SGPropertyChangeListener.
- ↑ David Megginson (Tue, 18 Jun 2002 06:31:04 -0700). Re: [Flightgear-devel] ANN: SGPropertyChangeListener.
- ↑ David Megginson (Wed, 05 Feb 2003 17:56:40 -0800). Re: [Flightgear-devel] Live property picker.
- ↑ David Megginson (Tue, 18 Jun 2002 06:31:04 -0700). Re: [Flightgear-devel] ANN: SGPropertyChangeListener.
- ↑ David Megginson (Tue, 26 Feb 2002 18:49:46 -0800). re: [Flightgear-devel] Property Access.
- ↑ David Megginson (Tue, 26 Feb 2002 18:49:46 -0800). re: [Flightgear-devel] Property Access.
- ↑ David Megginson (Tue, 26 Feb 2002 18:49:46 -0800). re: [Flightgear-devel] Property Access.
- ↑ 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
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
|
- ↑ James Turner (Fri, 19 Dec 2003 17:46:56 -0800). [Flightgear-devel] SGPropertyListener (was Re: [Flightgear-flightmodel] crash reporting).
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;
- https://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/props/propertyObject.hxx
- https://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/props/propertyObject.cxx
- https://sourceforge.net/p/flightgear/simgear/ci/next/tree/simgear/props/propertyObject_test.cxx
JSBSim
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. — Jon S. Berndt (Nov 20th, 2010). Re: [Flightgear-devel] Further properties heads-up - property
objects..
(powered by Instant-Cquotes) |
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. — James Turner (Nov 20th, 2010). Re: [Flightgear-devel] Further properties heads-up -
property objects..
(powered by Instant-Cquotes) |
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! — James Turner (Nov 20th, 2010). Re: [Flightgear-devel] Further properties heads-up
- property objects..
(powered by Instant-Cquotes) |
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. — James Turner (Nov 20th, 2010). Re: [Flightgear-devel] Further properties heads-up -
property objects..
(powered by Instant-Cquotes) |
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) |
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) |