Some questions about ReferenceCount

Hi all,

I have to admit, I’m still a bit confused about this smart reference counting system that Panda3D provides.
When loading very large collada files in my exporter, my code hogs, leaks, segfaults, aborts, whatever. My guess is I’m using this reference counting system wrong, and reference counts are not handled well. Can anybody give me more clarity about when to use ©PT, or when to just use an ordinary pointer? To be more specific, I have these n00b questions:
(1) Should I use PT(class) to store pointers everywhere? Also in the return values of functions, arguments passed to functions, class members, etc?
(2) In the destructor of my classes, do I specifically need to delete pointers or decrease reference counts or so when my member pointer is not a smart-pointer?
(3) Can I directly do something like this:

FooBar *Yadda::get_new_foo_bar() {
  return new Foobar();

Or, do I need to specifically store it in a PointerTo before returning it, to increase the reference count? Or is that automatically done when it is finally cast into a PT(FooBar) in some other code?
Or do I maybe need to make my class return a PT(FooBar) instead of the ordinary pointer? I looked at other panda classes and they don’t do the latter, they just use ordinary pointers in most cases.
(4) Can I have a class both inheriting from (Typed)ReferenceCount and some other class that doesn’t use smart-RC at all, or by doing that will I risk a leak?

Thanks in advance,

OK, the general rule of thumb is, when dealing with a ReferenceCount object, you should use an ordinary pointer only if you are confident that there is a PT(object) that is also holding the pointer somewhere else, and that that PT(object) will persist for at least the lifetime of your ordinary pointer. If you have any doubt at all, you should use a PT(object).

In Panda examples where you see the method returning an ordinary pointer, these are generally accessors into the PT(object) that is already stored within the class, so it’s safe to return an ordinary pointer. However, in cases where the object has been newly constructed and is not stored anywhere, you will see the Panda method returning a PT(object). For instance, PartBundle::bind_anim() returns a PT(AnimControl), because that AnimControl was newly created and is not stored anywhere in the class.

In general, when receiving a parameter into a function, it’s safe to receive an ordinary pointer, because the caller must have it stored in a PT(object) somewhere that will persist for at least the lifetime of the called function.

Also, when you first create a new ReferenceCount object, you should generally always store it immediately in a PT(object), because initially its reference count is zero, and it is in danger of getting accidentally deleted if you do anything with it without first storing it in a PT(object).

Once it is stored in a PT(object), you need to ensure that there is at least some PT(object) somewhere in the world that is always storing it, until is is no longer needed.

To answer your specific questions:

(1) Use PT(object) everywhere, whenever you want to store the pointer. For just passing pointers around, use either ordinary pointers or PT(object) pointers, according to whether you are confident the reference count is already stored elsewhere or not. If you have any doubt, use PT(object) pointers.

(2) You should never explicitly delete pointers to ReferenceCount-inherited objects. That is certain to cause a double-delete, since the reference-counting system will delete it automatically when the reference count is decremented to zero. You should also not explicitly decrease reference counts. Instead, store the object in a PT(object) pointer within your class, and let the reference-counting system work automatically, the way it is supposed to.

(3) No, this will be bad, because there is no PT(object) anywhere holding on to the object. In this case you should return a PT(FooBar) instead of FooBar *. Other than that the function can remain as-is, though if you wanted to modify the function to do anything at all with your new FooBar object before returning it, you should be careful to store it in a temporary PT(FooBar) when you construct it (and not an ordinary FooBar *).

(4) Sure, we do this all the time. For instance, lots of things inherit from ReferenceCount (or some derivative), and Namable. But your new derived class is now a ReferenceCount object, and must be treated like a ReferenceCount object, meaning store it in a PT(object) pointer, and never delete it explicitly.


Okay, thanks very much for your detailed explanation! It has now become more clear to me.

EDIT: And yes, changing my return values to PointerTo indeed solved my random aborts, segs and other problems 8)