Assertion error in collisionLoop causes crash

Hi all,
I’m stumbling upon the following error:

Assertion failed: _ref_count == 0 || _ref_count == local_ref_count at line 108 of c:\buildslave\sdk-windows-amd64\build\built1.10\include\referenceCount.I
Assertion failed: _node != nullptr at line 46 of c:\buildslave\sdk-windows-amd64\build\built1.10\include\nodePathComponent.I
Traceback (most recent call last):
  File "C:\Panda3D-1.10.10-x64\direct\showbase\ShowBase.py", line 2117, in __collisionLoop
    self.cTrav.traverse(self.render)
AssertionError: _ref_count == 0 || _ref_count == local_ref_count at line 108 of c:\buildslave\sdk-windows-amd64\build\built1.10\include\referenceCount.I
:task(error): Exception occurred in PythonTask collisionLoop
Traceback (most recent call last):
  File "testing_file.py", line 570, in <module>
    w.run()
  File "C:\Panda3D-1.10.10-x64\direct\showbase\ShowBase.py", line 3325, in run
    self.taskMgr.run()
  File "C:\Panda3D-1.10.10-x64\direct\task\Task.py", line 546, in run
    self.step()
  File "C:\Panda3D-1.10.10-x64\direct\task\Task.py", line 500, in step
    self.mgr.poll()
  File "C:\Panda3D-1.10.10-x64\direct\showbase\ShowBase.py", line 2117, in __collisionLoop
    self.cTrav.traverse(self.render)
AssertionError: _ref_count == 0 || _ref_count == local_ref_count at line 108 of c:\buildslave\sdk-windows-amd64\build\built1.10\include\referenceCount.I

I’m running a test whereby the collisionNodes that will be the into are made in the c++ side, but the from collisionNode is made in the python side and consists of a single collisionBox.

This is how in brief, the into collisionNodes are made:

std::vector<PT(CollisionNode)> collision_nodes;
...
int counter_cnn=0;
PT(CollisionNode) coll_node_gen;
...
for(int x=0;x<max_limit;x++)
{
  if(counter_cnn==0)
  {
      coll_node_gen= new CollisionNode("col_poly_set");
      collision_nodes.push_back(coll_node_gen);
  }
  PT(CollisionPolygon) c_pol = new CollisionPolygon(pnt1,pnt2,pnt3,pnt4);
  coll_node_gen->add_solid(c_pol);
  counter_cnn++;
  if(counter_cnn==7)
     counter_cnn=0;
}
...
int f_nodes_size=collision_nodes.size();
BitMask32 floor_mask;
floor_mask.set_bit(1);

for(int i=0;i<f_nodes_size;i++)
{
    NodePath np_cn=np_floor.attach_new_node(collision_nodes[i]);
    np_cn.set_name("0.0.cnode.0");
    np_cn.node()->set_into_collide_mask(floor_mask);
}
...

That is the c++ side, which is done in a c++ asyncTask, for speed reasons. The python side is direct:

...
  box=CollisionBox(minimum,maximum)
  cn=render.attachNewNode(CollisionNode('cnode'))
  cn.node().addSolid(box)
  cn.show()
  cn.node().setFromCollideMask(BitMask32.allOn())
  cn.node().setIntoCollideMask(BitMask32.allOff())
  self.boxQueue=CollisionHandlerQueue()
  self.traverser = CollisionTraverser('traverser')
  base.cTrav = self.traverser
  base.cTrav.addCollider(cn, self.boxQueue)
...

However, the application crashes with the aforementioned error. It doesn’t crash if nothing is added to the traverser. Simply adding the collider to the traverser yields the reference count issue. Am I overlooking something or doing something incorrectly? What could be the issue?
Any help or pointers will be much appreciated. If there is anymore information needed to understand what could be causing the crash, kindly ask me.

Thank you in advance.

I’m not seeing the whole code here, so it’s hard to see what’s going wrong. It has clearly something to do with reference counting not being done correctly somewhere, but there’s nothing obviously jumping out at me from what I’m seeing, so it must be in some code I’m not seeing.

I managed to reproduce the error, albeit in contracted form:

Assertion failed: _ref_count == 0 || _ref_count == local_ref_count at line 108 of c:\buildslave\sdk-windows-amd64\build\built1.10\include\referenceCount.I
Assertion failed: _node != nullptr at line 37 of c:\buildslave\sdk-windows-amd64\build\built1.10\include\nodePathComponent.I

Using this python code:

from direct.showbase.ShowBase import ShowBase
from direct.task import Task
from direct.interval.IntervalGlobal import *
from panda3d.core import *
from direct.gui.DirectGui import *
from direct.particles.ParticleEffect import ParticleEffect
from panda3d.physics import *

class run_me(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        #Async-task to load up the collision-meshes:
        taskMgr.setupTaskChain('chain_name', numThreads = 1, tickClock = None,threadPriority = None, frameBudget = None,frameSync = True, timeslicePriority = None)
        taskMgr.add(self.worldExpandTest,"worldExpandTestNom", taskChain = "chain_name")
        #taskMgr.add(self.worldExpandTest,"worldExpandTestNom")
        #Collider:
        minimum=Point3(0,0,0)
        maximum=Point3(4,4,4)
        box=CollisionBox(minimum,maximum)
        cn=render.attachNewNode(CollisionNode('cnode'))
        cn.node().addSolid(box)
        cn.show()
        cn.node().setFromCollideMask(BitMask32.allOn())
        cn.node().setIntoCollideMask(BitMask32.allOff())
        self.boxQueue=CollisionHandlerQueue()
        self.traverser = CollisionTraverser('traverser')
        base.cTrav = self.traverser
        base.cTrav.addCollider(cn, self.boxQueue)
        #taskMgr.add(self.parseTest, 'testParse')
        
    def worldExpandTest(self,task):
        for i in range(1):
            if(not render.find("dummi_"+str(i))):
                print("NUMZ?: ",i)
                fNomWall="mesh"+str(i+1)+".bam"
                terrainTest=base.loader.loadModel(fNomWall)
                terrainTest.reparentTo(render)
                terrainTest.setName("dummi_"+str(i))
        return task.cont
        
w = run_me()
w.run()

Loading this model:
mesh1.bam (2.9 KB)
It only consists of four flat faces that have been generated from c++ and then saved as a .bam file via write_bam_file. Simply running the code with any collision-checking enabled crashes with the aforementioned error. I generated 8 other files just in case that one does not reproduce the error on your pc. I am using panda3d version 1.10.10. I’m so sorry to ask this, but would it be possible to tell me what’s wrong from looking at the file alone?

I could provide the main portions of the c++ code that produced the file as well if needed, since I don’t want to congest this post unnecessarily.

So, an interesting observation is that parenting the collision geometry to render and not to the visible geometry, seems to make the crash go away!

floor=terrainTest.find("colPolySetFloor")
floor.wrtReparentTo(render)

I suppose I will tentatively use this as a workaround, especially if no other solution or explanation is posited.

Hmm… As I noted in another thread, you’re setting your colliding object to collide with everything–including visible geometry. (Hence, I imagine, your seeing PandaNodes in your PStats output for cTrav.)

Perhaps, then, the problem comes from the fact that in colliding with your “floor” collision-node, the colliding object is also colliding with the visible geometry that I imagine is associated with that node–which is the parent of the “floor” collision-node. This, I’m suggesting, might confuse the collision system in some way.

So, to test this: What happens if, instead of setting your colliding object’s “from”-mask to “allOn”, you instead set it to some arbitrary value–let’s say 2–and then set your “floor” collision-node’s “into”-mask to the same value. This while leaving your “floor” collision-node attached to your visible geometry as before.

Thanks for the suggestion. But modifying the code by adding this to the from-object:

floorMask=BitMask32.bit(4)
cn.setCollideMask(BitMask32.allOff())
cn.node().setCollideMask(BitMask32.allOff())
cn.node().setFromCollideMask(floorMask)

And this to the into-object:

floor=terrainTest.find("colPolySetFloor")
#floor.wrtReparentTo(render)
for kid in floor.getChildren():
    kid.show()
    kid.setCollideMask(BitMask32.allOff())
    kid.node().setCollideMask(BitMask32.allOff())
    kid.node().setIntoCollideMask(wallMask)

Still results in the same error:

Assertion failed: _ref_count == 0 || _ref_count == local_ref_count at line 108 of c:\buildslave\sdk-windows-amd64\build\built1.10\include\referenceCount.I
Assertion failed: _node != nullptr at line 37 of c:\buildslave\sdk-windows-amd64\build\built1.10\include\nodePathComponent.I

It’s the reason I uploaded the very small model with the sample code, just in case someone can reproduce the error and examine the model to see what’s wrong with it. If the error with the model can be pointed out, then I’d know what to modify in the c++ code that produces it. :confused: So until that happens, should it ever happen, I’ll just stick with parenting the collision-mesh to render.

Ah, that’s a pity–but fair enough!

So, I played then around with your code and model a bit, and I managed to make the assertion error go away:

In short, your model has a few elements that contain the string “$4$4”. Thinking that these might be causing trouble of some sort, I converted the model to the “egg”-format and renamed them (to “mew”). And with appropriate changes to the code in order to load this new model, I found that, indeed–it ran without assertion error!

Thanks again, but calling terrainTest.ls() reveals this hierarchy:

GeomNode dummi_0 (1 geoms) S:(TransparencyAttrib)
  PandaNode colPolySetWall
  PandaNode colPolySetFloor
    CollisionNode 0.0.cnode.0 (4 solids) (hidden)
  GeomNode chunk_nom_$4$4$_liq (1 geoms) S:(CullBinAttrib DepthOffsetAttrib TransparencyAttrib)
    PandaNode colPolySetLiq

Removing the nodepaths that don’t have any collision geometry like this:

wall=terrainTest.find("colPolySetWall")
liqq=terrainTest.find("chunk_nom_$4$4$_liq")
wall.removeNode()
liqq.removeNode()

Doesn’t do anything and still yields the same error. Please note that the error only crops up when an asynchronous task is at play. There is no issue when everything is done on the main thread. The sample code and model I provided are meant to be a replica of a problem in a larger project, where a lot of visible geometry and collision geometry is created on a separate thread. So the question would be why it crashes when loaded exactly the way the sample code loads it.
In any case, as I said, I did find a workaround, which I’ll stick with for now.

Ah, and a bit more searching seems to suggest that it was a change in the code that prevented the issue, curiously enough–my apologies, then!

Looking at the code for “mutexPosixImpl.I” (where the assertion seems to be happening, on line 99), it looks like it’s failing to get a mutex lock for some reason. This at least explains why the issue only appears when using threading.

More than that I don’t know offhand, I’m afraid–threading isn’t my strong suit. (I would guess that something else already has the lock–but what, I don’t know.) But perhaps one of the other forum-members will have more insight!

That said, I’m glad that you do at the least have a workaround for now!

This is a bug, can you please post this on the GitHub issue tracker? Thanks!

Okay, no problem, I added it.