/*
- * Copyright(c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright(c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Tizen.NUI.Binding;
+using global::System.Diagnostics;
namespace Tizen.NUI
{
/// swigCMemOwn
/// </summary>
/// <since_tizen> 3 </since_tizen>
- [Obsolete("Deprecated in API9, Will be removed in API11, Please use SwigCMemOwn")]
+ [Obsolete("Deprecated in API9, will be removed in API11, Use SwigCMemOwn")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
protected bool swigCMemOwn;
/// <summary>
- /// A flag to check if it is already disposed.
+ /// The flag to check if it is already disposed of.
/// </summary>
/// <since_tizen> 3 </since_tizen>
- [Obsolete("Deprecated in API9, Will be removed in API11, Please use Disposed")]
+ [Obsolete("Deprecated in API9, will be removed in API11, Use Disposed")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1051:Do not declare visible instance fields", Justification = "<Pending>")]
protected bool disposed = false;
private global::System.Runtime.InteropServices.HandleRef swigCPtrCopy;
private bool registerMe;
- //A Flag to check who called Dispose(). (By User or DisposeQueue)
+ //The flag to check who called Dispose(). (By User or DisposeQueue)
private bool isDisposeQueued = false;
/// <summary>
//to catch derived classes dali native exceptions
if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ DebugFileLogging.Instance.WriteLog($"BaseHandle.contructor with cMemeryOwn:{cMemoryOwn} START");
+
registerMe = swigCMemOwn = cMemoryOwn;
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
// using copy constructor to create another native handle so Registry.Unregister works fine.
if (registerMe)
{
-
// Register this instance of BaseHandle in the registry.
Registry.Register(this);
}
+
+ disposeDebuggingCtor();
+ DebugFileLogging.Instance.WriteLog($" BaseHandle.contructor with cMemeryOwn END");
+ DebugFileLogging.Instance.WriteLog($"=============================");
}
internal BaseHandle(global::System.IntPtr cPtr)
{
+ DebugFileLogging.Instance.WriteLog($"BaseHandle.contructor START");
+
registerMe = swigCMemOwn = true;
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
// Register this instance of BaseHandle in the registry.
Registry.Register(this);
}
+
+ disposeDebuggingCtor();
+ DebugFileLogging.Instance.WriteLog($"BaseHandle.contructor END");
+ DebugFileLogging.Instance.WriteLog($"=============================");
}
/// <summary>
/// Dispose.
/// </summary>
/// <since_tizen> 3 </since_tizen>
+ // following this guide: https://docs.microsoft.com/ko-kr/dotnet/fundamentals/code-analysis/quality-rules/ca1063?view=vs-2019 (CA1063)
~BaseHandle() => Dispose(false);
/// <summary>
/// Event when a property is set.
/// </summary>
/// <since_tizen> 5 </since_tizen>
+ /// <seealso cref="BindableObject.PropertyChanged"/>
+ [Obsolete("Deprecated in API9, will be removed in API11, Use BindableObject.PropertyChanged instead.")]
public event PropertyChangedEventHandler PropertySet;
internal global::System.Runtime.InteropServices.HandleRef GetBaseHandleCPtrHandleRef
/// <since_tizen> 3 </since_tizen>
public void Dispose()
{
- Dispose(true);
- System.GC.SuppressFinalize(this);
+ if (isDisposeQueued)
+ {
+ Dispose(DisposeTypes.Implicit);
+ }
+ else
+ {
+ Dispose(true);
+ }
+ GC.SuppressFinalize(this);
}
/// <summary>
//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;
+
+ DebugFileLogging.Instance.WriteLog("[NUI][BaseHandle] 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");
+
+ Tizen.Log.Fatal("NUI", $"[NUI][BaseHandle] 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");
+
+ //to fix ArtApp black screen issue. this will be enabled after talking with ArtApp team and fixing it.
+ // throw new global::System.InvalidOperationException("[NUI][BaseHandle] 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)
{
+ DebugFileLogging.Instance.WriteLog($"should not be here! (dead code) this will be removed!");
+
+ Tizen.Log.Fatal("NUI", $"[NUI] should not be here! (dead code) this will be removed!");
+
+ //to fix ArtApp black screen issue. this will be enabled after talking with ArtApp team and fixing it.
+ // throw new global::System.Exception($"[NUI] should not be here! (dead code) this will be removed!");
Dispose(DisposeTypes.Implicit);
}
else
/// Resets the handle.
/// </summary>
/// <since_tizen> 3 </since_tizen>
+ /// <remark>
+ /// This will be deprecated, please use Dispose() instead.
+ /// </remark>
public void Reset()
{
- Interop.BaseHandle.Reset(swigCPtrCopy);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ this.Dispose();
+ NUILog.Error("[ERROR] This(BaseHandle.Reset) will be deprecated, please use Dispose() instead!");
}
/// <summary>
/// <since_tizen> 3 </since_tizen>
public bool IsEqual(BaseHandle rhs)
{
- if (disposed == true)
+ if (disposed == true || rhs == null || !rhs.HasBody())
{
return false;
}
return;
}
+ DebugFileLogging.Instance.WriteLog($"BaseHandle.Dispose({type}) START");
+
if (type == DisposeTypes.Explicit)
{
//Called by User
Registry.Unregister(this);
}
+ disposeDebuggingDispose(type);
+
if (SwigCPtr.Handle != IntPtr.Zero)
{
if (swigCMemOwn)
}
swigCPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
}
+ else
+ {
+ var me = this.GetType().FullName;
+ DebugFileLogging.Instance.WriteLog($"[ERR] SwigCPtr is NULL, need to check! me:{me}");
+ Log.Error("NUI", $"[ERR] SwigCPtr is NULL, need to check! me:{me}");
+ }
+
if (swigCPtrCopy.Handle != global::System.IntPtr.Zero)
{
swigCMemOwn = false;
Interop.BaseHandle.DeleteBaseHandle(swigCPtrCopy);
swigCPtrCopy = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
}
+ else
+ {
+ var me = this.GetType().FullName;
+ DebugFileLogging.Instance.WriteLog($"[ERR] swigCPtrCopy is NULL, need to check! me:{me}");
+ Log.Error("NUI", $"[ERR] swigCPtrCopy is NULL, need to check! me:{me}");
+ }
disposed = true;
+ DebugFileLogging.Instance.WriteLog($"BaseHandle.Dispose({type}) END");
+ DebugFileLogging.Instance.WriteLog($"=============================");
+ NUILog.Debug($"BaseHandle.Dispose({type}) END");
+ NUILog.Debug($"=============================");
}
/// <summary>
internal global::System.Runtime.InteropServices.HandleRef SwigCPtr
{
- get => swigCPtr;
- set
+ get
{
- swigCPtr = value;
+ if (swigCPtr.Handle == IntPtr.Zero)
+ {
+ var process = global::System.Diagnostics.Process.GetCurrentProcess().Id;
+ var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
+ var me = this.GetType().FullName;
+
+ Tizen.Log.Fatal("NUI", $"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");
+
+ Tizen.Log.Fatal("NUI", $"[ERROR] back trace!");
+ global::System.Diagnostics.StackTrace st = new global::System.Diagnostics.StackTrace(true);
+ for (int i = 0; i < st.FrameCount; i++)
+ {
+ global::System.Diagnostics.StackFrame sf = st.GetFrame(i);
+ Tizen.Log.Fatal("NUI", " Method " + sf.GetMethod());
+ }
+ Tizen.Log.Fatal("NUI", "Error! just return here with null swigCPtr! this can cause unknown error or crash in next step");
+
+ //to fix ArtApp black screen issue. this will be enabled after talking with ArtApp team and fixing it.
+ // 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;
}
}
+ internal bool IsNativeHandleInvalid()
+ {
+ return swigCPtr.Handle == IntPtr.Zero;
+ }
+
/// <summary>
/// swigCMemOwn
/// </summary>
protected internal bool SwigCMemOwn => swigCMemOwn;
/// <summary>
- /// A flag to check if it is already disposed.
+ /// The flag to check if it is already disposed of.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
protected internal bool Disposed => disposed;
+
+ /// <summary>
+ /// The flag to check if it is disposed by DisposeQueue.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ protected internal bool IsDisposeQueued => isDisposeQueued;
+
+ [Conditional("NUI_DISPOSE_DEBUG_ON")]
+ private void disposeDebuggingCtor()
+ {
+ DebugFileLogging.Instance.WriteLog($"type:{GetType()} copyNativeHandle:{swigCPtrCopy.Handle.ToString("X8")}");
+ if (this is BaseComponents.View view)
+ {
+ DebugFileLogging.Instance.WriteLog($"ID:{view.ID} Name:{view.Name}");
+ //back trace
+ global::System.Diagnostics.StackTrace st = new global::System.Diagnostics.StackTrace(true);
+ for (int i = 0; i < st.FrameCount; i++)
+ {
+ global::System.Diagnostics.StackFrame sf = st.GetFrame(i);
+ DebugFileLogging.Instance.WriteLog($"[{i}] {sf.GetMethod()}");
+ }
+ }
+ }
+
+ [Conditional("NUI_DISPOSE_DEBUG_ON")]
+ private void disposeDebuggingDispose(DisposeTypes type)
+ {
+ DebugFileLogging.Instance.WriteLog($"swigCMemOwn:{swigCMemOwn} type:{GetType()} copyNativeHandle:{swigCPtrCopy.Handle.ToString("X8")} HasBody:{HasBody()}");
+
+ if (swigCPtr.Handle != IntPtr.Zero && swigCPtrCopy.Handle != IntPtr.Zero && HasBody())
+ {
+ using var currentProcess = global::System.Diagnostics.Process.GetCurrentProcess();
+ var process = currentProcess.Id;
+ var thread = global::System.Threading.Thread.CurrentThread.ManagedThreadId;
+ var me = this.GetType().FullName;
+ var daliId = "unknown";
+ var hash = this.GetType().GetHashCode();
+ var name = "unknown";
+
+ if (this is BaseComponents.View)
+ {
+ daliId = Interop.Actor.GetId(swigCPtrCopy).ToString();
+ name = Interop.Actor.GetName(swigCPtrCopy);
+ BaseObject tmp = new BaseObject(Interop.BaseHandle.GetBaseObject(swigCPtrCopy), false);
+ var refCnt = tmp.ReferenceCount();
+ tmp.Dispose();
+ if (refCnt > 2)
+ {
+ DebugFileLogging.Instance.WriteLog($"[ERR] reference count is over than 2. Could be a memory leak. Need to check! \n" +
+ $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me} \n" +
+ $" disposeType:{type}, name:{name}, daliID:{daliId}, hash:{hash}, refCnt:{refCnt}");
+
+ Log.Error("NUI", $"[ERR] reference count is over than 2. Could be a memory leak. Need to check! \n" +
+ $" process:{process} thread:{thread}, isDisposed:{this.disposed}, isDisposeQueued:{this.isDisposeQueued}, me:{me} \n" +
+ $" disposeType:{type}, name:{name}, daliID:{daliId}, hash:{hash}, refCnt:{refCnt}");
+ }
+ }
+ }
+ }
+
}
}