Nodepath memory management

class Container
{
   public:
     SimpleContainer();
     ~SimpleContainer();

  private:
    NodePath* m_pModelNodePath;
};

The destructor uses NodePath::remove_node() to remove the renderer’s reference to m_pModelNodePath. Since I don’t like memory leaks I would like to make sure the object itself (so the node to which m_pModelNodePath points) is also deleted. I should not call

 delete m_pModelNodePath; 

since I cannot be sure there are no more pointers to it (e.g. if I extend the above code with a get_node_path() method). So I have to be sure it will be deleted automatically later on.

The documentation at http://www.panda3d.org/apiref.php?page=NodePath#removeNode states the node will not only be removed from the renderer but also deleted entirely if there are no more pointers to it. Will it also be deleted in the future (once the condition is met) ? Since NodePath does not inherit from ReferenceCount I cannot simply use

PT(NodePath) ... 

(I get an error saying NodePath does not have the function “_ref()” )

I cannot check if the node is deleted since it the node will remain alive as long as I keep any pointers to it and once I’ve thrown away all of its pointers (so it will be deleted) I cannot check for its existence any more.

Does the panda framework take care of this node in terms of deleting it once there are no more pointers to it ? Can I rely on this to happen or do I have to supply some flags/options/conditions ?

Actually, the NodePath data type itself barely stores some data at all. It is designed to be kept on the stack and therefore it does not inherit from ReferenceCount (hence you can’t store it in a PointerTo pointer).

When the API reference or manual talks about a “node”, it is not talking about a NodePath, but about a PandaNode. PandaNodes are the actual nodes that store data. You can view a NodePath as a handle to a PT(PandaNode), with useful wrapper functions.

Deleting a NodePath (if you keep it on the stack, it is deleted automatically when it goes out of scope, of course) will not have any effect on the existance of the PandaNode that it references, unless it stores the last reference to that node.

In essence, what removeNode does is: it clears the NodePath’s PT(PandaNode) field so that its reference count is decreased.

So I guess the right way to store m_pModelNodePath would be:

  private:
    NodePath m_pModelNodePath; 

My bad … I tried using the node object itself - just as u suggested - but slightly differently by doing

PandaNode m_pModelNodePath;

which of course failed because PandaNode does not have a default constructor. So I thought I needed a pointer, which I could leave without initialization until constructor runtime. Well, problem solved :slight_smile:

Thanks.

Yeah, you should either store a PT(PandaNode) or a NodePath, not a PandaNode or NodePath*.

To further clarify: certain objects in Panda are intended to be used as concrete objects; NodePath is one of these. You should use a NodePath the same way you use an int. You should keep a concrete instance to it, almost never a pointer, because a NodePath is itself a kind of a pointer. You can assign NodePaths back and forth as you like, or pass it into a function or return it. The NodePath itself is a very lightweight object. It’s not a node, which is heavyweight; it’s a pointer to a node.

You don’t have to delete NodePaths or memory manage them any way. You never need to “new” a NodePath. You just store it directly.

Other objects in Panda are intended to be used as pointers. With very few exceptions, these are the objects that inherit from ReferenceCount. PandaNode is one of these. Unlike NodePaths, you should not use PandaNode as a concrete object. If you need to store one in your class, you should always use a PT(PandaNode) (or a NodePath, which is almost the same thing).

David

Hi,

I’m currently having some problems I don’t understand with the memory management of GeomNode and NodePath, that I think are relatively close to the subject of that post.

I’m working on a Python program that have some classes extended in C++, some of these classes use Panda C++ code (I’m using Boost for extensions wrapping).

I need a class to have a NodePath member wrapped to python. Here’s what I tried:

Since GeomNode is ReferenceCount’ed, I have a private member:
PT(GeomNode) m_geomNode;
Since NodePath is not, I have a private member:
NodePath m_NodePath;

Then I instantiate the GeomNode and the NodePath:
m_geomNode = new GeomNode(“planar2D5_mesh”);
m_NodePath = NodePath(m_geomNode);

Finally I expose the NodePath to Python, using DTool_CreatePyInstance because NodePath is not Typed, and “memory_rules = false” because I don’t want to let Python delete my NodePath:
m_oMesh = object(handle<>(DTool_CreatePyInstance(&m_NodePath, Dtool_NodePath, false, false)));

This was creating random segfaults, and sometimes asserting in the PandaNode destructor:
nassertv(cdata->get_up()->empty());
there’s a comment here that says there’s a refcount fault somewhere.

Finally I realized that the real source of the segfaults was the GeomNode.
This simple class:

[b]struct foo{
private:
PT(GeomNode) m_geomNode;

public:
foo(){m_geomNode = new GeomNode(“jojo”);}
~foo(){}
};[/b]

causes segfaults when instantiated in Python:
self._foo = foo()

Not at the time of instantiation, but later during panda jobs… At some point this GeomNode is deleted whereas my class containing it is still alive. I wanted to demonstrate it on a simple example but for now I can’t run pview.cxx (see discourse.panda3d.org/viewtopic.php?t=1921)

Also I just discovered that it crashes when the destructor is called:
self._foo = None # --> crash

EDIT:
Calling the destructor doesn’t crash anymore if I explicitly increase the reference counter:
m_geomNode->ref() #shouldn’t it be done by PT()??
BUT I still have the segfault later…

What am I obviously doing wrong?
Any help would be greatly appreciated, I have to say that I’m dazed and confused.

Thanks
-David

I don’t see anything wrong. That’s certainly the correct way to create and retain a GeomNode, and it shouldn’t cause any problems when foo destructs. Could the problem be in the Boost wrappers somehow? Is it possible your foo destructor is getting called twice, for instance?

Or, maybe your foo class is also passing the GeomNode pointer to someone else, who is storing it without incrementing the reference count–and that someone else is crashing when foo destructs, and it destructs the GeomNode out from under it.

If you can write a simple C++ program that can create and destruct foo without problems (and, looking at foo, I think this is so), then the problem is not in foo itself.

David

Hi David,

Thanks for the reply.
First, I confirm that explicit reference count increase is necessary for GeomNode.
Please copy this struct in pview.cxx:

struct foo{
private:
    PT(GeomNode) m_geomNode;
public:
    foo(){
            m_geomNode = new GeomNode("jojo");
    }
    ~foo(){}
};

Add these lines in the main:

{
	  foo *my_foo = new foo();
	  delete my_foo;
}

Run --> Unhandled exception at 0x01108a0d in panda_viewer.exe: 0xC0000005: Access violation reading location 0x00008e91.

Now add this line to foo’s constructor:

m_geomNode->ref();

Run --> OK

This is not the case with, let’s say, GeomTriangles, that’s also reference counted:

struct foo{
private:
    PT(GeomTriangles) m_geomTriangles;
public:
    foo(){
		m_geomTriangles = new GeomTriangles(GeomEnums::UH_static);
	}
    ~foo(){}
};

{
	  foo *my_foo = new foo();
	  delete my_foo;
}

Run --> OK

So there’s at least this (according to me) strange behavior with GeomNode. I would expect the “new” call to increase the ref count since it’s a PT(). Also note that Boost isn’t involved in this simple example. Can you confirm that this difference of behavior between 2 ref counted objects is not normal? Maybe this is the source of my issues?

Thanks
-David

Uh, yeah, that’s not normal. Something is very wrong in your build. I did paste in the code you illustrate, and it works perfectly, without having to add an additional call to ref().

I suspect it’s not the reference counting that’s going wrong, but something in the GeomNode destructor, so that any time a GeomNode destructs, boom. For instance, try this in main() without monkeying with foo or reference counting:

GeomNode *crashme = new GeomNode("jojo");
delete crashme;

As to why the GeomNode destructor would be crashing, that I can’t speculate. Mismatched DLL’s? Incorrect project settings?

Do you get anything useful in the traceback?

David

You’re right, the code that you provided crashes on my machine, with the following stack:

libpandaexpress.dll!00eb8a0d() 	
 	[Frames below may be incorrect and/or missing, no symbols loaded for libpandaexpress.dll]	
 	libpanda.dll!1008fa15() 	
 	panda_viewer.exe!GeomNode::`scalar deleting destructor'()  + 0x9 bytes	C++
>	panda_viewer.exe!main(int argc=, char * * argv=)  Line 265	C++
 	msvcr90.dll!_initterm(void (void)* * pfbegin=0x00000001, void (void)* * pfend=0x02c224d0)  Line 903	C
 	panda_viewer.exe!__tmainCRTStartup()  Line 582 + 0x17 bytes	C
 	kernel32.dll!75baeccb() 	
 	ntdll.dll!77b4d24d() 	
 	ntdll.dll!77b4d45f() 

I’m on Windows VISTA x64 and uses version 1.6.2 that I installed thanks to the Windows installer provided on the website.
I uninstalled yesterday version 1.5.4 so there shouldn’t be any dlls mismatch.

Maybe someone with the same config could have a try, or at least with Windows installed 1.6.2 version?

-David

I tried on another computer, Windows XP 32, same installed Panda3d-1.6.2, no panda source code
–> same crash

so I’m afraid there could be something in 1.6.2 Windows version…

If confirmed please tell me what process I should follow to log this issue, cause this is really blocking me :frowning:

Thanks for your support.
-David

You are compiling your project in Release mode, not Debug mode, right?

David

Sure!

I’ve read tons of post on this forum before posting about my issue, and I guess I have retained the basics :slight_smile:

Moreover, I’ve already developed some C++ extensions using Panda for my Python program without any issue (and great performance gain!).

The issues started only when I used GeomNodes…

I will try to use Panda3d-1.5.4 instead and see what happens.
-David

This is not crashing in 1.5.4!!!
… but crashes on 1.6.1

Fascinating. Yet surely it’s not crashing in Python, or we’d know of it by now, right? Try this, interactively:

python
>>> from pandac.PandaModules import GeomNode
>>> crashme = GeomNode("jojo")
>>> del crashme
>>>

I trust there’s no crash there? Which is weird, because it’s calling into the exact same C++ library.

Hmm, it occurs to me that there might have been a bug we introduced in 1.6.x (that’s since been fixed) having to do with NDEBUG, which MSVS wants to define automatically when you build in Release mode. Try specifically undefining this in your project.

David

Right!

Yabadabadoo!

Many many thanks David
-David

PS: Nice first name

Great; I’ve just added this tip to the manual. :slight_smile:

David