Re: CSharp Premature Garbage Collection
Denver Coneybeare via Swig-user <[email protected]> Wed, 20 Oct 2021 16:19:50 -0400
| Newsgroups | gmane.comp.programming.swig |
|---|---|
| Message-ID | <CAH1B0WQi9NFrPNkiwHr9c+i08mzLUGw640Nkzd0NZ=9PeJ6wCQ@mail.gmail.com> |
Hi William, Thank you for your response. After doing even more debugging, I have come to realize that (obviously) you are right! That is, HandleRef is properly extending the object's lifetime. Sorry to waste your time. I'm now looking into other sources of potential memory corruption in our code, such as race conditions or use-after-free scenarios. --Denver On Wed, Oct 20, 2021 at 7:47 AM William S Fulton <[email protected]> wrote: > > Hi Denver > > You can achieve what you want with the following: > > %typemap(csout, excode=SWIGEXCODE) void Pet::set_owner { > $imcall; > global::System.GC.KeepAlive(this); > global::System.GC.KeepAlive(person);$excode > } > > which is a modification of the typemap in csharp.swg: > > %typemap(csout, excode=SWIGEXCODE) void { > $imcall;$excode > } > > It is very specific to this function. > > I'd consider putting in improved support for this in SWIG, but I don't believe this is a premature garbage collection issue as HandleRef prevents this from happening, so I think you should check your assumption, provide more/exact details of how to replicate with a cutdown example and check whether the dotnet runtime you are using is perhaps not a standard implementation. > > Note https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.handleref?view=net-5.0 states: > > Although using HandleRef to keep an object alive for the duration of a platform invoke call is preferred, you can also use the GC.KeepAlive method for the same purpose. > > William > > > On Mon, 18 Oct 2021 at 18:28, Denver Coneybeare via Swig-user <[email protected]> wrote: >> >> 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. >> >> >> _______________________________________________ >> Swig-user mailing list >> [email protected] >> https://lists.sourceforge.net/lists/listinfo/swig-user