C# wrapper can crash #295
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
It looks like the C# wrapper is not following "best practices" of calling native code. I think you are just getting lucky that the code is not crashing on you (likely since the reference is long-lived).
Please use GC.KeepAlive() to ensure the GC doesn't finalize YodaNode object while you are within a native function call. Some background:
https://goo.gl/WeL3H4
https://msdn.microsoft.com/en-us/library/ms182300.aspx
https://blogs.msdn.microsoft.com/oldnewthing/20100813-00/?p=13153
I would also suggest creating a light-weight "handle" class whose sole purpose would be holding the native IntPtr and finalization of it. It is a poor practice to have a finalizable object that's holding many managed references. Why? Because now they cannot be collected until the owning object has fully died (as they could potentially be needed by its finalizer method). Literature and a much deeper explanation is available in this excellent book by Marcus Heege.
@itsff Can you share a concrete example of the scenario that we need to call
GC.KeepAlive()
? I think all managed objects already live longer than the associated unmanaged objects.@splhack I just read through most of the provided links and did some research. It seems the following is true if you run this outside of visual studio
If if you are inside a object method the finalizer can be run:
That means, it could be that the node gets finalized inside the CalculateLayout() function, if you don't use
node
afterwards (GC.KeepAlive
would force keeping the referince till after the end of the call).Which means the memory gets freed while the calculation runs.
The suggest fix is to use
SafeHandle
instead ofIntPtr
as the interop marshaler handles this in a special way to ensure that this doesn't get freed inside an interop call.I think the following should work, do you agree @itsff ?
and change the
Native.cs
like, e.g.:and removing the Finalizer from
YogaNode
.Well done, @woehrl01.
Moving to a dedicated
SafeHandle
class should address both of my concerns. First and foremost, the automatic marshaller will ensure theSafeHandle
isn't disposed while the library is making a P/Invoke call. Second, moving away from having a finalizer inYogaNode
will allow managed objects referenced byYogaNode
to be GCed earlier, thus improving overall efficiency.To add some background to our discussion: C# is one of those languages where a lifetime of an object can (and often is) much shorter than the "scope" it is declared in. The CLR will mark objects eligible for collection as soon as they are no longer used. As @woehrl01 pointed out in his comment, the last time the
node
object is being referenced is within the body ofYogaNode::CalculateLayout
when its privateIntPtr
member variable is read. Sonode
can be destroyed by the GC while the execution is still withinYogaNode::CalculateLayout
right before the invocation of the native static function.YogaNode
's finalizer will kick in on its own thread, and in turn delete the native memory being used by the native function. To prevent this from happening, there are basically two options: either callGC.KeepAlive(obj)
to mark the object as still in use, or (as @woehrl01 suggested) switch to using aSafeHandle
so that the framework can reference count your native calls.@woehrl01 or @itsff could you open a PR with these changes. Great find!
@itsff cool, I leave it up to you. I don't mind doing the PR. Don't want to heist some "credits" so. 😄
Sounds good, gentlemen. I will do a PR tonight. @woehrl01 @emilsjolander
It seems
Microsoft.Win32.SafeHandles
is available on all Unity platform https://github.com/OSVR/Managed-OSVR/pull/10#r31332441Please use this NuGet package so the behavior is correctly polyfilled across all NETstandard platforms:
https://www.nuget.org/packages/System.Runtime.Handles/4.3.0
Resolved by PR #297