I have this code in my Python/C++ script (see below) which adds a new_mesh to my Rhino document. This piece of code (there is more code that creates the new_mesh which is not shown) sits inside a .NET parallel.invoke loop which adds 14 of these new meshes to the document. It all works fine most of the time. But one out of 20 executions or so results in Rhino crashing. Is this because some of the Rhino functions below are not thread safe? Or is there a memory leak from one of these operations?
CRhinoDoc *pDoc = CRhinoDoc::FromRuntimeSerialNumber(doc_serial_number);
ON_3dmObjectAttributes attribs;
pDoc->GetDefaultObjectAttributes(attribs);
CRhinoMeshObject *meshObject = new CRhinoMeshObject(attribs);
meshObject->SetMesh(new_mesh);
if (!pDoc->AddObject(meshObject)) {
// Remove object from Heap if it was not added to doc.
delete meshObject;
}
else { mesh_serial_numbers[i] = meshObject ? meshObject->RuntimeSerialNumber() : 0; }
Is pDoc->AddObject(meshObject) thread safe?
Is meshObject ? meshObject->RuntimeSerialNumber() : 0; } thread safe?
I have spent 20 hours trying to fix this and just keep finding dead ends. If I turn off parallel.invoke for the loop, Rhino never crashes.
It I delay each thread launched by parallel.invoke by an increasing amount, then the code become more stable. In fact the code execution time for the parallel.invoke loop drops from 90 ms down to 75 ms when the threads are delayed by 100ns (0ns, 100ns, 200ns, 300ns . . . are the delays for start time of thread 0, 1, 2, 3 . . .). When I bump this up to 1ms, then the execution time does increase and the stability increases. It seems like without the delays, the code is piling up waiting for a resource. Could this be the pDoc->AddObject(meshObject) step? Is this what is causing the crash?
Good to know. This is the only step that interacts with the document. The other steps are just adding things to ON_Simple_Arrays in order to build the meshes. And then there is a new_mesh->Compact() step which I assume is thread safe.
On a separate question:
Do you know what Compact() does; I know it must remove vertices, textures and normals that do not appear in the face vertex indices and then renumber the vertex indices starting from 0. But what else is it doing? My timing shows this to be much slower that building the mesh so I am considering replacing it (I already wrote code for vertex renumbering some time back). But if I do I want to make sure the resulting mesh is as good as what would have resulted from using Compact.
Steve, I am always glad to receive information from you on Rhino’s operation. Thank you for sharing.
Good to know about ON_SimpleArray restrictions. For my case, the ON_SimpleArray’s are worked on by only 1 thread. Each new_mesh is completely built inside it own thread where I create the new ON_Mesh object and then serial add data to its ON_SimpleArray’s for vertices, colors, textures, normals and faces.
I could move the step that adds the mesh to the document outside the parallel.invoke loop and serially add all the new_mesh’s afterwards. This may bring me back to perfect stability.
I am so happy to be able to see the details of the Compact operation. This will be a big help in my deciding what to do with the Compact step.