[NUI] Check if event handler of the DisposedQueued object is valid
authordongsug.song <dongsug.song@samsung.com>
Sat, 13 Nov 2021 06:43:01 +0000 (15:43 +0900)
committerdongsug-song <35130733+dongsug-song@users.noreply.github.com>
Wed, 17 Nov 2021 08:08:13 +0000 (17:08 +0900)
- The objects that are waiting in disposeQueue are not yet signal disconnected, and their states are implict disposed.
- When dali callback comes and the obeject is in the queue, it seems that the problem will occur.
- User event callback checks IsDisposeQueued before Invoked, and if Dispose == False, IsDisposeQueued == true, then event callback is not invoked.
- when Dispose == True, User callback != NULL, throw exception.

src/Tizen.NUI/src/internal/Common/KeyInputFocusSignal.cs
src/Tizen.NUI/src/internal/Interop/Interop.KeyInputFocusManager.cs
src/Tizen.NUI/src/public/BaseComponents/ViewEvent.cs
src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs
src/Tizen.NUI/src/public/Common/BaseHandle.cs

index 849d49f..2ca8e19 100755 (executable)
@@ -46,20 +46,14 @@ namespace Tizen.NUI
 
         public void Connect(System.Delegate func)
         {
-            System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func);
-            {
-                Interop.KeyInputFocusManager.KeyInputFocusSignalConnect(SwigCPtr, new System.Runtime.InteropServices.HandleRef(this, ip));
-                if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
-            }
+            Interop.KeyInputFocusManager.KeyInputFocusSignalConnect(SwigCPtr, func);
+            if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
         }
 
         public void Disconnect(System.Delegate func)
         {
-            System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate<System.Delegate>(func);
-            {
-                Interop.KeyInputFocusManager.KeyInputFocusSignalDisconnect(SwigCPtr, new System.Runtime.InteropServices.HandleRef(this, ip));
+                Interop.KeyInputFocusManager.KeyInputFocusSignalDisconnect(SwigCPtr, func);
                 if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
-            }
         }
 
         public void Emit(View arg)
index 3674d45..e5da738 100755 (executable)
  *
  */
 
+using global::System;
+using global::System.Runtime.InteropServices;
+
 namespace Tizen.NUI
 {
     internal static partial class Interop
     {
         internal static partial class KeyInputFocusManager
         {
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_SWIGUpcast")]
-            public static extern global::System.IntPtr Upcast(global::System.IntPtr jarg1);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_SWIGUpcast")]
+            public static extern IntPtr Upcast(IntPtr jarg1);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_KeyInputFocusManager")]
-            public static extern global::System.IntPtr NewKeyInputFocusManager();
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_KeyInputFocusManager")]
+            public static extern IntPtr NewKeyInputFocusManager();
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_KeyInputFocusManager")]
-            public static extern void DeleteKeyInputFocusManager(global::System.Runtime.InteropServices.HandleRef jarg1);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_KeyInputFocusManager")]
+            public static extern void DeleteKeyInputFocusManager(HandleRef jarg1);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_Get")]
-            public static extern global::System.IntPtr Get();
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_Get")]
+            public static extern IntPtr Get();
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_SetFocus")]
-            public static extern void SetFocus(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_SetFocus")]
+            public static extern void SetFocus(HandleRef jarg1, HandleRef jarg2);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_GetCurrentFocusControl")]
-            public static extern global::System.IntPtr GetCurrentFocusControl(global::System.Runtime.InteropServices.HandleRef jarg1);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_GetCurrentFocusControl")]
+            public static extern IntPtr GetCurrentFocusControl(HandleRef jarg1);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_RemoveFocus")]
-            public static extern void RemoveFocus(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_RemoveFocus")]
+            public static extern void RemoveFocus(HandleRef jarg1, HandleRef jarg2);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_KeyInputFocusChangedSignal")]
-            public static extern global::System.IntPtr KeyInputFocusChangedSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusManager_KeyInputFocusChangedSignal")]
+            public static extern IntPtr KeyInputFocusChangedSignal(HandleRef jarg1);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_Empty")]
-            [return: global::System.Runtime.InteropServices.MarshalAs(global::System.Runtime.InteropServices.UnmanagedType.U1)]
-            public static extern bool KeyInputFocusSignalEmpty(global::System.Runtime.InteropServices.HandleRef jarg1);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_Empty")]
+            [return: MarshalAs(UnmanagedType.U1)]
+            public static extern bool KeyInputFocusSignalEmpty(HandleRef jarg1);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_GetConnectionCount")]
-            public static extern uint KeyInputFocusSignalGetConnectionCount(global::System.Runtime.InteropServices.HandleRef jarg1);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_GetConnectionCount")]
+            public static extern uint KeyInputFocusSignalGetConnectionCount(HandleRef jarg1);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_Connect")]
-            public static extern void KeyInputFocusSignalConnect(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_Connect", CallingConvention = CallingConvention.Cdecl)]
+            public static extern void KeyInputFocusSignalConnect(HandleRef jarg1, Delegate jarg2);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_Disconnect")]
-            public static extern void KeyInputFocusSignalDisconnect(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_Disconnect", CallingConvention = CallingConvention.Cdecl)]
+            public static extern void KeyInputFocusSignalDisconnect(HandleRef jarg1, Delegate jarg2);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_Emit")]
-            public static extern void KeyInputFocusSignalEmit(global::System.Runtime.InteropServices.HandleRef jarg1, global::System.Runtime.InteropServices.HandleRef jarg2);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_KeyInputFocusSignal_Emit")]
+            public static extern void KeyInputFocusSignalEmit(HandleRef jarg1, HandleRef jarg2);
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_KeyInputFocusSignal")]
-            public static extern global::System.IntPtr NewKeyInputFocusSignal();
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_new_KeyInputFocusSignal")]
+            public static extern IntPtr NewKeyInputFocusSignal();
 
-            [global::System.Runtime.InteropServices.DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_KeyInputFocusSignal")]
-            public static extern void DeleteKeyInputFocusSignal(global::System.Runtime.InteropServices.HandleRef jarg1);
+            [DllImport(NDalicPINVOKE.Lib, EntryPoint = "CSharp_Dali_delete_KeyInputFocusSignal")]
+            public static extern void DeleteKeyInputFocusSignal(HandleRef jarg1);
         }
     }
 }
index 6db4c8d..6645c13 100755 (executable)
@@ -42,8 +42,10 @@ namespace Tizen.NUI.BaseComponents
         private EventHandler<VisibilityChangedEventArgs> visibilityChangedEventHandler;
         private VisibilityChangedEventCallbackType visibilityChangedEventCallback;
         private EventHandler keyInputFocusGainedEventHandler;
+
         private KeyInputFocusGainedCallbackType keyInputFocusGainedCallback;
         private EventHandler keyInputFocusLostEventHandler;
+
         private KeyInputFocusLostCallbackType keyInputFocusLostCallback;
         private EventHandler onRelayoutEventHandler;
         private OnRelayoutEventCallbackType onRelayoutEventCallback;
@@ -80,10 +82,12 @@ namespace Tizen.NUI.BaseComponents
         private delegate void ResourcesLoadedCallbackType(IntPtr control);
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void _backgroundResourceLoadedCallbackType(IntPtr view);
-        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
         private delegate void KeyInputFocusGainedCallbackType(IntPtr control);
-        [UnmanagedFunctionPointer(CallingConvention.StdCall)]
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
         private delegate void KeyInputFocusLostCallbackType(IntPtr control);
+
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
         private delegate void OnRelayoutEventCallbackType(IntPtr control);
         [UnmanagedFunctionPointer(CallingConvention.StdCall)]
@@ -126,10 +130,14 @@ namespace Tizen.NUI.BaseComponents
                 if (keyInputFocusGainedEventHandler == null)
                 {
                     using KeyInputFocusSignal signal = new KeyInputFocusSignal(Interop.ViewSignal.KeyInputFocusGainedSignal(SwigCPtr), false);
-                    if (signal?.Empty() == false)
+
+                    if (keyInputFocusGainedCallback != null)
                     {
                         signal?.Disconnect(keyInputFocusGainedCallback);
-                        keyInputFocusGainedCallback = null;
+                        if (signal?.Empty() == true)
+                        {
+                            keyInputFocusGainedCallback = null;
+                        }
                     }
                 }
             }
@@ -159,10 +167,14 @@ namespace Tizen.NUI.BaseComponents
                 if (keyInputFocusLostEventHandler == null)
                 {
                     using KeyInputFocusSignal signal = new KeyInputFocusSignal(Interop.ViewSignal.KeyInputFocusLostSignal(SwigCPtr), false);
-                    if (signal?.Empty() == false)
+
+                    if (keyInputFocusLostCallback != null)
                     {
                         signal?.Disconnect(keyInputFocusLostCallback);
-                        keyInputFocusLostCallback = null;
+                        if (signal?.Empty() == true)
+                        {
+                            keyInputFocusLostCallback = null;
+                        }
                     }
                 }
             }
@@ -770,18 +782,74 @@ namespace Tizen.NUI.BaseComponents
 
         private void OnKeyInputFocusGained(IntPtr view)
         {
-            if (keyInputFocusGainedEventHandler != null)
+            if (IsNativeHandleInvalid())
             {
-                keyInputFocusGainedEventHandler(this, null);
+                if (this.Disposed)
+                {
+                    if (keyInputFocusGainedEventHandler != null)
+                    {
+                        var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
+                        var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
+                        var me = this.GetType().FullName;
+
+                        throw new ObjectDisposedException(nameof(SwigCPtr), $"Error! NUI's native dali object is already disposed. " +
+                            $"OR the native dali object handle of NUI becomes null! \n" +
+                            $" process:{process} thread:{thread}, isDisposed:{this.Disposed}, isDisposeQueued:{this.IsDisposeQueued}, me:{me}\n");
+                    }
+                }
+                else
+                {
+                    if (this.IsDisposeQueued)
+                    {
+                        var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
+                        var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
+                        var me = this.GetType().FullName;
+
+                        //in this case, the View object is ready to be disposed waiting on DisposeQueue, so event callback should not be invoked!
+                        Tizen.Log.Error("NUI", "in this case, the View object is ready to be disposed waiting on DisposeQueue, so event callback should not be invoked! just return here! \n" +
+                            $"process:{process} thread:{thread}, isDisposed:{this.Disposed}, isDisposeQueued:{this.IsDisposeQueued}, me:{me}\n");
+                        return;
+                    }
+                }
             }
+
+            keyInputFocusGainedEventHandler?.Invoke(this, null);
         }
 
         private void OnKeyInputFocusLost(IntPtr view)
         {
-            if (keyInputFocusLostEventHandler != null)
+            if (IsNativeHandleInvalid())
             {
-                keyInputFocusLostEventHandler(this, null);
+                if (this.Disposed)
+                {
+                    if (keyInputFocusLostEventHandler != null)
+                    {
+                        var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
+                        var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
+                        var me = this.GetType().FullName;
+
+                        throw new ObjectDisposedException(nameof(SwigCPtr), $"Error! NUI's native dali object is already disposed. " +
+                            $"OR the native dali object handle of NUI becomes null! \n" +
+                            $" process:{process} thread:{thread}, isDisposed:{this.Disposed}, isDisposeQueued:{this.IsDisposeQueued}, me:{me}\n");
+                    }
+                }
+                else
+                {
+                    if (this.IsDisposeQueued)
+                    {
+                        var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
+                        var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
+                        var me = this.GetType().FullName;
+
+                        //in this case, the View object is ready to be disposed waiting on DisposeQueue, so event callback should not be invoked!
+                        Tizen.Log.Error("NUI", "in this case, the View object is ready to be disposed waiting on DisposeQueue, so event callback should not be invoked! just return here! \n" +
+                            $"process:{process} thread:{thread}, isDisposed:{this.Disposed}, isDisposeQueued:{this.IsDisposeQueued}, me:{me}\n");
+                        return;
+                    }
+                }
             }
+
+            keyInputFocusLostEventHandler?.Invoke(this, null);
         }
 
         private bool OnKeyEvent(IntPtr view, IntPtr keyEvent)
index 319d96b..2db7853 100755 (executable)
@@ -1177,12 +1177,12 @@ namespace Tizen.NUI.BaseComponents
                         ThemeManager.ThemeChangedInternal.Remove(OnThemeChanged);
                     }
                 }
-                if(widthConstraint != null)
+                if (widthConstraint != null)
                 {
                     widthConstraint.Remove();
                     widthConstraint.Dispose();
                 }
-                if(heightConstraint != null)
+                if (heightConstraint != null)
                 {
                     heightConstraint.Remove();
                     heightConstraint.Dispose();
@@ -1351,6 +1351,7 @@ namespace Tizen.NUI.BaseComponents
                 using KeyInputFocusSignal signal = new KeyInputFocusSignal(Interop.ViewSignal.KeyInputFocusLostSignal(GetBaseHandleCPtrHandleRef), false);
                 signal?.Disconnect(keyInputFocusLostCallback);
                 keyInputFocusLostCallback = null;
+                keyInputFocusLostEventHandler = null;
             }
 
             if (keyInputFocusGainedCallback != null)
@@ -1360,6 +1361,7 @@ namespace Tizen.NUI.BaseComponents
                 using KeyInputFocusSignal signal = new KeyInputFocusSignal(Interop.ViewSignal.KeyInputFocusGainedSignal(GetBaseHandleCPtrHandleRef), false);
                 signal?.Disconnect(keyInputFocusGainedCallback);
                 keyInputFocusGainedCallback = null;
+                keyInputFocusGainedEventHandler = null;
             }
 
             if (backgroundResourceLoadedCallback != null)
index 0367d49..ec2248b 100644 (file)
@@ -362,11 +362,18 @@ namespace Tizen.NUI
                 //Throw exception if Dispose() is called in separate thread.
                 if (!Window.IsInstalled())
                 {
-                    throw new System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread.");
+                    var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
+                    var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
+                    var me = this.GetType().FullName;
+
+                    throw new global::System.InvalidOperationException("This API called from separate thread. This API must be called from MainThread. \n" +
+                        $" process:{process} thread:{thread}, disposing:{disposing}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
                 }
 
                 if (isDisposeQueued)
                 {
+                    Tizen.Log.Fatal("NUI", $"should not be here! (dead code) this will be removed!");
+                    throw new global::System.Exception($"[NUI] should not be here! (dead code) this will be removed!");
                     Dispose(DisposeTypes.Implicit);
                 }
                 else
@@ -612,7 +619,13 @@ namespace Tizen.NUI
             {
                 if (swigCPtr.Handle == IntPtr.Zero)
                 {
-                    throw new ObjectDisposedException(nameof(SwigCPtr), "Error! NUI's native dali object is already disposed. OR the native dali object handle of NUI becomes null!");
+                    var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
+                    var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
+                    var me = this.GetType().FullName;
+
+                    throw new ObjectDisposedException(nameof(SwigCPtr), $"Error! NUI's native dali object is already disposed. " +
+                        $"OR the native dali object handle of NUI becomes null! \n" +
+                        $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me}\n");
                 }
                 return swigCPtr;
             }
@@ -634,5 +647,12 @@ namespace Tizen.NUI
         /// </summary>
         [EditorBrowsable(EditorBrowsableState.Never)]
         protected internal bool Disposed => disposed;
+
+        /// <summary>
+        /// A flag to check if it is disposed by DisposeQueue.
+        /// </summary>
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        protected internal bool IsDisposeQueued => isDisposeQueued;
+
     }
 }