From 90a55055c7677ffad6267df48e2d9b07a7f9c16b Mon Sep 17 00:00:00 2001 From: Matthew Jakeman Date: Wed, 11 Nov 2020 19:41:23 +1300 Subject: [PATCH 1/2] Fix Garbage Collection of ClosureHelper.MarshalCallback Keeps a reference to the MarshalCallback delegate to prevent the Garbage Collector from collecting it while still in use. Partially fixes #82 --- Libs/GObject/Classes/ClosureHelper.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Libs/GObject/Classes/ClosureHelper.cs b/Libs/GObject/Classes/ClosureHelper.cs index 2056ecb46..95bfe1d88 100644 --- a/Libs/GObject/Classes/ClosureHelper.cs +++ b/Libs/GObject/Classes/ClosureHelper.cs @@ -12,6 +12,10 @@ internal class ClosureHelper : IDisposable private static readonly Dictionary Handlers = new Dictionary(); + // We need to store a reference to MarshalCallback to + // prevent the delegate from being collected by the GC + private readonly ClosureMarshal _marshalCallback; + private bool _disposedValue; private readonly Action? _callback; private readonly ActionRefValues? _callbackRefValues; @@ -41,7 +45,8 @@ public ClosureHelper(Object obj, ActionRefValues callbackRefValues) : this(obj) private ClosureHelper(Object obj) { Handle = Closure.Native.new_object((uint) Marshal.SizeOf(typeof(Closure)), obj.Handle); - Closure.Native.set_marshal(Handle, MarshalCallback); + _marshalCallback = MarshalCallback; + Closure.Native.set_marshal(Handle, _marshalCallback); } ~ClosureHelper() => Dispose(false); @@ -50,12 +55,12 @@ private ClosureHelper(Object obj) #region Methods - private void MarshalCallback(IntPtr closure, ref Value return_value, uint n_param_values, - Value[] param_values, IntPtr invocation_hint, IntPtr marshal_data) + private void MarshalCallback(IntPtr closure, ref Value returnValue, uint nParamValues, + Value[] paramValues, IntPtr invocationHint, IntPtr marshalData) { _callback?.Invoke(); - _callbackRefValues?.Invoke(ref param_values); + _callbackRefValues?.Invoke(ref paramValues); } public static bool TryGetByDelegate(Action action, out ClosureHelper closure) From 7cc037efb6d5a0d8f2930343323ec9e484e33ebe Mon Sep 17 00:00:00 2001 From: Matthew Jakeman Date: Wed, 11 Nov 2020 20:06:55 +1300 Subject: [PATCH 2/2] Fix Garbage Collection of Object.OnFinalized Stops the Garbage Collector from collecting the WeakNotify delegate used by Object.cs to dispose of unmanaged resources. This should be the last instance of garbage collected delegates, but the fix is quite simple if we encounter this issue again. Fixes #82 --- Libs/GObject/Classes/Object.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Libs/GObject/Classes/Object.cs b/Libs/GObject/Classes/Object.cs index c11ce0bc6..09960785a 100644 --- a/Libs/GObject/Classes/Object.cs +++ b/Libs/GObject/Classes/Object.cs @@ -27,6 +27,10 @@ public partial class Object : INotifyPropertyChanged, IDisposable #region Properties protected internal IntPtr Handle { get; private set; } + + // We need to store a reference to WeakNotify to + // prevent the delegate from being collected by the GC + private WeakNotify? _onFinalized; protected bool Disposed { get; private set; } @@ -135,7 +139,11 @@ protected virtual void Initialize() { } // Modify this in the future to play nicely with virtual function support? private void OnFinalized(IntPtr data, IntPtr where_the_object_was) => Dispose(); - private void RegisterOnFinalized() => Native.weak_ref(Handle, OnFinalized, IntPtr.Zero); + private void RegisterOnFinalized() + { + _onFinalized = OnFinalized; + Native.weak_ref(Handle, _onFinalized, IntPtr.Zero); + } private void RegisterProperties() { @@ -182,9 +190,7 @@ private void RegisterEvent(string eventName, ClosureHelper closure, bool after) if (ret == 0) throw new Exception($"Could not connect to event {eventName}"); - // Add to our closures list so the callback - // doesn't get garbage collected. - // closures.Add(closure); + // Add to our closures list so the callback doesn't get garbage collected. Closures[closure] = ret; }