Fix GC finalizer issue
authorSeungkeun Lee <sngn.lee@samsung.com>
Thu, 13 Oct 2016 07:15:16 +0000 (16:15 +0900)
committerSeungkeun Lee <sngn.lee@samsung.com>
Tue, 18 Oct 2016 05:10:56 +0000 (14:10 +0900)
 - Finalizer was called in GC thread and it can't access EFL object
 - Remove all finalizer code that access EFL object
 - Hold the reference of EvasObject that was added on Window
 - Fix wrong IDispose implemention
   - When Dispose(false) native handle should be freed,
     but we didn't, it cause seg-fault, after released C# layer, native callback can invoked.
   - So, We release native handle when Dispose(false) was called.
   - but, it will cause some error message 'Maybe it has already been freed.'

Change-Id: Ic225799ac8cf9fd102a67d870043cb24bad55c7d

src/ElmSharp/ElmSharp/Conformant.cs
src/ElmSharp/ElmSharp/EvasObject.cs
src/ElmSharp/ElmSharp/ItemObject.cs
src/ElmSharp/ElmSharp/Window.cs
src/ElmSharp/Interop/Interop.EvasObjectEvent.cs
src/ElmSharp/Interop/Interop.SmartEvent.cs
test/ElmSharp.Test/TestRunner.cs

index 8914a70dbc1c8498667ed505934e7f49b36d3ed5..fc4fd7bcd15aadd5792981b014fa5fc6f46750fe 100644 (file)
@@ -8,7 +8,7 @@ namespace ElmSharp
         {
             Interop.Evas.evas_object_size_hint_weight_set(Handle, 1.0, 1.0);
             Interop.Elementary.elm_win_conformant_set(parent.Handle, true);
-            Interop.Elementary.elm_win_resize_object_add(parent.Handle, Handle);
+            parent.AddResizeObject(this);
         }
 
         protected override IntPtr CreateHandle(EvasObject parent)
index 123e635ffc7f1f6b0916bb32cbf06935219728b3..c373a7de0e466cbaee51eaaa1d72696fd3d59f24 100644 (file)
@@ -26,6 +26,14 @@ namespace ElmSharp
             OnInstantiated();
         }
 
+        // C# Finalizer was called on GC thread
+        // So, We can't access to EFL object
+        // And When Finalizer was called, Field can be already released.
+        //~EvasObject()
+        //{
+        //    Unrealize();
+        //}
+
         public event EventHandler Deleted;
         public event EventHandler<EvasKeyEventArgs> KeyUp;
         public event EventHandler<EvasKeyEventArgs> KeyDown;
@@ -212,11 +220,6 @@ namespace ElmSharp
             Interop.Evas.evas_object_size_hint_weight_set(Handle, x, y);
         }
 
-        ~EvasObject()
-        {
-           Unrealize();
-        }
-
         public void Show()
         {
             Interop.Evas.evas_object_show(Handle);
@@ -247,6 +250,9 @@ namespace ElmSharp
             Deleted?.Invoke(this, EventArgs.Empty);
             OnInvalidate();
             Handle = IntPtr.Zero;
+
+            (Parent as Window)?.RemoveChild(this);
+            Parent = null;
             _deleted = null;
         }
 
@@ -297,6 +303,9 @@ namespace ElmSharp
                 Parent = parent;
                 Handle = CreateHandle(parent);
                 Debug.Assert(Handle != IntPtr.Zero);
+
+                (parent as Window)?.AddChild(this);
+
                 OnRealized();
                 _deleted = new Interop.EvasObjectEvent(this, Handle, Interop.Evas.ObjectCallbackType.Del);
                 _keydown = new Interop.EvasObjectEvent<EvasKeyEventArgs>(this, Handle, Interop.Evas.ObjectCallbackType.KeyDown, EvasKeyEventArgs.Create);
@@ -317,10 +326,25 @@ namespace ElmSharp
             if (IsRealized)
             {
                 OnUnrealize();
-                Interop.Evas.evas_object_del(Handle);
+                IntPtr toBeDeleted = Handle;
                 Handle = IntPtr.Zero;
-                Parent = null;
+                _deleted?.Dispose();
                 _deleted = null;
+                _keydown?.Dispose();
+                _keydown = null;
+                _keyup?.Dispose();
+                _keyup = null;
+                _moved?.Dispose();
+                _moved = null;
+                _resized?.Dispose();
+                _resized = null;
+                _renderPost?.Dispose();
+                _renderPost = null;
+
+                (Parent as Window)?.RemoveChild(this);
+
+                Interop.Evas.evas_object_del(toBeDeleted);
+                Parent = null;
             }
         }
     }
index e8c9be7c37d419802f84fee652a2edb54ff1c703..a3cc6e2a5e0add733d9820f4e4344ae6de8e768e 100644 (file)
@@ -23,11 +23,14 @@ namespace ElmSharp
             Handle = handle;
         }
 
-        ~ItemObject()
-        {
-            if (Handle != IntPtr.Zero)
-                Interop.Elementary.elm_object_item_del(Handle);
-        }
+        // C# Finalizer was called on GC thread
+        // So, We can't access to EFL object
+        // And When Finalizer was called, Field can be already released.
+        //~ItemObject()
+        //{
+        //    if (Handle != IntPtr.Zero)
+        //        Interop.Elementary.elm_object_item_del(Handle);
+        //}
 
         public int Id { get; private set; }
 
index efc239df8d0bb06bace39720b81b64bc2fed3795..02933dfad7079142e530e19be788c4c2a53096ab 100644 (file)
@@ -37,6 +37,7 @@ namespace ElmSharp
     {
         Interop.SmartEvent _deleteRequest;
         Interop.SmartEvent _rotationChanged;
+        HashSet<EvasObject> _referenceHolder = new HashSet<EvasObject>();
 
         public Window(string name) : this(null, name)
         {
@@ -151,6 +152,16 @@ namespace ElmSharp
             return Interop.Elementary.elm_win_add(parent != null ? parent.Handle : IntPtr.Zero, Name, 0);
         }
 
+        internal void AddChild(EvasObject obj)
+        {
+            _referenceHolder.Add(obj);
+        }
+
+        internal void RemoveChild(EvasObject obj)
+        {
+            _referenceHolder.Remove(obj);
+        }
+
         static int[] ConvertDegreeArray(DisplayRotation value)
         {
             List<int> rotations = new List<int>();
index 9f5cade89fad2f009bf784a4d49001489b2d9736..3b8b614a3cf48912e2047ece04a10678420bc36f 100644 (file)
@@ -78,10 +78,11 @@ internal static partial class Interop
             {
                 if (disposing)
                 {
-                    foreach (var cb in _nativeCallbacks)
-                    {
-                        Evas.evas_object_event_callback_del(_handle, _type, cb.callback);
-                    }
+                    // Place holder to dispose managed state (managed objects).
+                }
+                foreach (var cb in _nativeCallbacks)
+                {
+                    Evas.evas_object_event_callback_del(_handle, _type, cb.callback);
                 }
                 _nativeCallbacks.Clear();
                 _disposed = true;
@@ -152,10 +153,11 @@ internal static partial class Interop
             {
                 if (disposing)
                 {
-                    foreach (var cb in _nativeCallbacks)
-                    {
-                        Evas.evas_object_event_callback_del(_handle, _type, cb.callback);
-                    }
+                    // Place holder to dispose managed state (managed objects).
+                }
+                foreach (var cb in _nativeCallbacks)
+                {
+                    Evas.evas_object_event_callback_del(_handle, _type, cb.callback);
                 }
                 _nativeCallbacks.Clear();
                 _disposed = true;
index 5116e6a9b9d1bb5ce8c19543ede77634032ef888..b83d4b48a95fd0b94b58e52b63add446301bcaa4 100644 (file)
@@ -74,10 +74,11 @@ internal static partial class Interop
         {
             if (disposing)
             {
-                foreach (var cb in _nativeCallbacks)
-                {
-                    Evas.evas_object_smart_callback_del(_handle, _eventName, cb.callback);
-                }
+                // Place holder to dispose managed state (managed objects).
+            }
+            foreach (var cb in _nativeCallbacks)
+            {
+                Evas.evas_object_smart_callback_del(_handle, _eventName, cb.callback);
             }
             _nativeCallbacks.Clear();
         }
@@ -85,6 +86,7 @@ internal static partial class Interop
         public void Dispose()
         {
             Dispose(true);
+            GC.SuppressFinalize(this);
         }
     }
 
@@ -142,10 +144,11 @@ internal static partial class Interop
         {
             if (disposing)
             {
-                foreach (var cb in _nativeCallbacks)
-                {
-                    Evas.evas_object_smart_callback_del(_handle, _eventName, cb.callback);
-                }
+                // Place holder to dispose managed state (managed objects).
+            }
+            foreach (var cb in _nativeCallbacks)
+            {
+                Evas.evas_object_smart_callback_del(_handle, _eventName, cb.callback);
             }
             _nativeCallbacks.Clear();
         }
@@ -153,6 +156,7 @@ internal static partial class Interop
         public void Dispose()
         {
             Dispose(true);
+            GC.SuppressFinalize(this);
         }
     }
 }
index 4c95734ba5ad30c9ebc8efc9bb9ed00ce93f0e4b..52fc0710727d509f8e4ccaf481245270461138b0 100644 (file)
@@ -96,6 +96,7 @@ namespace ElmSharp.Test
                     if (e.KeyName == EvasKeyEventArgs.PlatformBackButtonName)
                     {
                         window.Hide();
+                        window.Unrealize();
                     }
                 };
             }