From 382c770775920720f0d6b9ce46b5f7f9e94930c1 Mon Sep 17 00:00:00 2001 From: "dongsug.song" Date: Sat, 13 Nov 2021 15:43:01 +0900 Subject: [PATCH] [NUI] Check if event handler of the DisposedQueued object is valid - 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/internal/Common/KeyInputFocusSignal.cs | 12 +-- .../Interop/Interop.KeyInputFocusManager.cs | 65 ++++++++-------- .../src/public/BaseComponents/ViewEvent.cs | 88 +++++++++++++++++++--- .../src/public/BaseComponents/ViewInternal.cs | 6 +- src/Tizen.NUI/src/public/Common/BaseHandle.cs | 24 +++++- 5 files changed, 141 insertions(+), 54 deletions(-) diff --git a/src/Tizen.NUI/src/internal/Common/KeyInputFocusSignal.cs b/src/Tizen.NUI/src/internal/Common/KeyInputFocusSignal.cs index 849d49f..2ca8e19 100755 --- a/src/Tizen.NUI/src/internal/Common/KeyInputFocusSignal.cs +++ b/src/Tizen.NUI/src/internal/Common/KeyInputFocusSignal.cs @@ -46,20 +46,14 @@ namespace Tizen.NUI public void Connect(System.Delegate func) { - System.IntPtr ip = System.Runtime.InteropServices.Marshal.GetFunctionPointerForDelegate(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(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) diff --git a/src/Tizen.NUI/src/internal/Interop/Interop.KeyInputFocusManager.cs b/src/Tizen.NUI/src/internal/Interop/Interop.KeyInputFocusManager.cs index 3674d45..e5da738 100755 --- a/src/Tizen.NUI/src/internal/Interop/Interop.KeyInputFocusManager.cs +++ b/src/Tizen.NUI/src/internal/Interop/Interop.KeyInputFocusManager.cs @@ -15,57 +15,60 @@ * */ +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); } } } diff --git a/src/Tizen.NUI/src/public/BaseComponents/ViewEvent.cs b/src/Tizen.NUI/src/public/BaseComponents/ViewEvent.cs index bf74987..cfe53e3 100755 --- a/src/Tizen.NUI/src/public/BaseComponents/ViewEvent.cs +++ b/src/Tizen.NUI/src/public/BaseComponents/ViewEvent.cs @@ -42,8 +42,10 @@ namespace Tizen.NUI.BaseComponents private EventHandler 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) diff --git a/src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs b/src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs index febbbd4..04342e1 100755 --- a/src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs +++ b/src/Tizen.NUI/src/public/BaseComponents/ViewInternal.cs @@ -1163,12 +1163,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(); @@ -1303,6 +1303,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) @@ -1310,6 +1311,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) diff --git a/src/Tizen.NUI/src/public/Common/BaseHandle.cs b/src/Tizen.NUI/src/public/Common/BaseHandle.cs index 65e9844..29b22fc 100644 --- a/src/Tizen.NUI/src/public/Common/BaseHandle.cs +++ b/src/Tizen.NUI/src/public/Common/BaseHandle.cs @@ -330,11 +330,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 @@ -570,7 +577,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; } @@ -592,5 +605,12 @@ namespace Tizen.NUI /// [EditorBrowsable(EditorBrowsableState.Never)] protected internal bool Disposed => disposed; + + /// + /// A flag to check if it is disposed by DisposeQueue. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + protected internal bool IsDisposeQueued => isDisposeQueued; + } } -- 2.7.4