Heap safety of "CreateFromNativePointer"

Hello,
the method

public static GeometryBase CreateFromNativePointer(
IntPtr pGeometry
)

from the Rhino.Runtime.Interop class in Rhincommon takes a native pointer, generated by something like

ON_Mesh* m = new ON_Mesh(....);

in C++, and makes the object the garbage collector’s responsibility. This sounds extremely useful: one could just manufacture objects natively, and this would decrease the necessary amount for P/Invoking a lot. However, according to what I’ve read the .dotnet runtime might link to a different C++ runtime library than Rhino itself or the originating dll - such as a plugin’s dll -, and might therefore use a different heap. The garbage collector will eventually ask the runtime to call “delete” on the pointer, and that would lead to a crash if the “new” was done on another heap than the “delete”, see this Microsoft blog entry and many other discussions. So my question is: Has that method been tried out in a context where the GC finalizes and destroys that object, and has it proven safe?
It might be worth considering to have Rhinocommon wrap the pointer in a safe handle, so that the library can provide its own deallocation function.
The problem would be solved if new and delete were both done by opennurbs.dll - Rhino will load one and only one opennurbs.dll. Indeed, in the file opennurbs_memory.h of the Rhino 7 SDK it says:

/*
/////////////////////////////////////////////////////////////////////////////
//
// ALL memory managment in the openNURBS toolkit is done through calls to
//    onmalloc(), oncalloc(), onrealloc(), onfree(), 
//    onmemdup(), onstrdup(), ..., and the
//    new and delete operators
// The on*() functions are all declared in opennurbs_memory.h and defined
// in opennurbs_memory.c.  The new and delete operators that use onmalloc()
// and onfree() are defined in opennurbs_plus_memory_new.cpp.

but there is no header opennurbs_plus_memory_new.h, and in the Rhino 8 SDK’s opennurbs_memory.h, that hint has disappeared! Does that mean Rhino 8 doesn’t have its own new/delete anymore?
TLDR: If a plugin developer does not have the necessary headers to make sure new/delete is consistent with Rhino/Grasshopper’s underlying new/delete, it seems very dangerous/impossible to pass memory ownership between the plugin and Rhino/Grasshopper using CreateFromNativePointer.
Cheers,
Mathias

1 Like

This is extremely important. It saddens me that the Rhino team didn’t answer.

Hi @scudelari,

Yes, I can see this is unanswered.

Back in the old days, when Microsoft’s small block memory allocation was slow, we used a 3rd party memory manager which dramatically improved Rhino’s performance. Modern Rhino does not use any 3rd party memory manager, as it is no longer required.

It there a problem you’re trying to solve?

– Dale

Hi @dale,

I do use, as @mathias.fuchs does as well, a native library that creates Rhino geometries such as ON_Mesh.
I am currently using the new statement to allocate the memory on the native side, and then I wrap it into RhinoCommon using the CreateFromNativePointer.

Is this the right way to allocate the memory for the objects that are send back from the native side, or should we use a special function such as onmalloc(), etc.

Or perhaps even a .NET marshalling helper such as CoTaskMemAlloc?

I feel that what concerns @mathias.fuchs is the main issue here:
1- How should the developer allocate the object in the native side and,
2- Is it guaranteed that by using CreateFromNativePointer we will never have a memory leak?

Thanks!

This is the correct pattern.

New it up.

MOOSECORELIB_C_FUNCTION
ON_Mesh* MooseCreateMesh()
{
  ...
  ON_Mesh* mesh = new ON_Mesh(vertex_count, face_count, false, false);
  ...
  return mesh;
}

Yes. Geometry objects in RhinoCommon inherit from IDisposable. So the native object will be deleted when the geometry is disposed or garbage collected.

public static Mesh CreateMesh()
{
  var ptr = UnsafeNativeMethods.MooseCreateMesh();
  if (ptr == IntPtr.Zero)
    return null;

  var geometry = Interop.CreateFromNativePointer(ptr);
  return geometry as Mesh;
}

– Dale

1 Like