CSharp Premature Garbage Collection
Denver Coneybeare via Swig-user <[email protected]> Mon, 18 Oct 2021 12:23:54 -0400
| Newsgroups | gmane.comp.programming.swig |
|---|---|
| Message-ID | <CAH1B0WS=nDxXRbX1WdtfaG2Me_N7c5_R9VqQXMCvH44m2LxCmg@mail.gmail.com> |
TL;DR I am experiencing premature garbage collection in the C# SWIG
wrappers and need help to work around it, probably by inserting calls
to GC.KeepAlive().
According to the SWIG documentation at
http://www.swig.org/Doc4.0/CSharp.html#CSharp
> There is no additional 'premature garbage collection prevention parameter' as the marshalling of the HandleRef object takes care of ensuring a reference to the proxy class is held until the unmanaged call completed.
However, it appears that premature garbage collection is indeed an
issue that needs to be explicitly addressed, as was done with Java.
We, at Google, are running into this problem sporadically in
https://github.com/firebase/firebase-unity-sdk, where we "wrap" our
C++ SDK in C# using SWIG.
Here is an example SWIG file:
%module CSharpPgcppIssue
struct Person {
};
struct Pet {
void set_owner(Person person);
};
When compiled with SWIG 4.0.2 with this command:
swig -c++ -csharp -o CSharpPgcppIssue.cpp -outdir . CSharpPgcppIssue.swig
The following code is generated in Pet.cs:
public void set_owner(Person person) {
CSharpPgcppIssuePINVOKE.Pet_set_owner(swigCPtr, Person.getCPtr(person));
if (CSharpPgcppIssuePINVOKE.SWIGPendingException.Pending) throw
CSharpPgcppIssuePINVOKE.SWIGPendingException.Retrieve();
}
The problem is that both "this" and "person" may become candidates for
garbage collection at the point when
CSharpPgcppIssuePINVOKE.Pet_set_owner() enters the C++ code. If the
garbage collector chooses to collect them at the earliest possible
point then "swigCPtr" and the pointer returned from
"Person.getCPtr(person)" would be deleted and the C++ code would,
therefore, access freed memory. We actually ran into this when
targeting Android using the "release" compiler configuration in Unity
2020.2.2f1 and now I'm trying to fix it.
The easy fix for this is that I came up with is to insert calls to
GC.KeepAlive() (https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive)
as follows:
public void set_owner(Person person) {
CSharpPgcppIssuePINVOKE.Pet_set_owner(swigCPtr, Person.getCPtr(person));
GC.KeepAlive(this);
GC.KeepAlive(person);
if (CSharpPgcppIssuePINVOKE.SWIGPendingException.Pending) throw
CSharpPgcppIssuePINVOKE.SWIGPendingException.Retrieve();
}
But I can't find a way to inject these necessary calls. Is this
possible with SWIG typemaps to somehow inject these calls to
GC.KeepAlive()? Is there another way to prevent premature garbage
collection in C#?
Thank you in advance for any help you can provide.