using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
namespace Tizen.NUI
{
private static readonly DisposeQueue disposableQueue = new DisposeQueue();
private List<IDisposable> disposables = new List<IDisposable>();
private System.Object listLock = new object();
+
+ // Dispose incrementally at least max(100, incrementallyDisposedQueue * 20%) for each 1 event callback
+ private const long minimumIncrementalCount = 100;
+ private const long minimumIncrementalRate = 20;
+ private List<IDisposable> incrementallyDisposedQueue = new List<IDisposable>();
+
private EventThreadCallback eventThreadCallback;
private EventThreadCallback.CallbackDelegate disposeQueueProcessDisposablesDelegate;
- private bool isCalled = false;
+ private bool initialied = false;
+ private bool processorRegistered = false;
+ private bool eventThreadCallbackTriggered = false;
+
+ private bool incrementalDisposeSupported = false;
+ private bool fullCollectRequested = false;
private DisposeQueue()
{
~DisposeQueue()
{
Tizen.Log.Debug("NUI", $"DisposeQueue is destroyed\n");
+ initialied = false;
}
public static DisposeQueue Instance
get { return disposableQueue; }
}
+ public bool IncrementalDisposeSupported
+ {
+ get => incrementalDisposeSupported;
+ set => incrementalDisposeSupported = value;
+ }
+
+ public bool FullyDisposeNextCollect
+ {
+ get => fullCollectRequested;
+ set => fullCollectRequested = value;
+ }
+
public void Initialize()
{
- if (isCalled == false)
+ if (!initialied)
{
disposeQueueProcessDisposablesDelegate = new EventThreadCallback.CallbackDelegate(ProcessDisposables);
eventThreadCallback = new EventThreadCallback(disposeQueueProcessDisposablesDelegate);
- isCalled = true;
+ initialied = true;
DebugFileLogging.Instance.WriteLog("DiposeTest START");
}
disposables.Add(disposable);
}
- if (eventThreadCallback != null)
+ if (initialied && eventThreadCallback != null && !eventThreadCallbackTriggered)
{
+ eventThreadCallbackTriggered = true;
+ eventThreadCallback.Trigger();
+ }
+ }
+
+ public void TriggerProcessDisposables(object o, EventArgs e)
+ {
+ processorRegistered = false;
+
+ if (eventThreadCallback != null && !eventThreadCallbackTriggered)
+ {
+ eventThreadCallbackTriggered = true;
eventThreadCallback.Trigger();
}
}
public void ProcessDisposables()
{
+ eventThreadCallbackTriggered = false;
+
lock (listLock)
{
- foreach (IDisposable disposable in disposables)
+ if (disposables.Count > 0)
{
- disposable.Dispose();
- DebugFileLogging.Instance.WriteLog($"disposable.Dispose(); type={disposable.GetType().FullName}, hash={disposable.GetHashCode()}");
+ DebugFileLogging.Instance.WriteLog($"Newly add {disposables.Count} count of disposables. Total disposables count is {incrementallyDisposedQueue.Count + disposables.Count}.\n");
+ // Move item from end, due to the performance issue.
+ while (disposables.Count > 0)
+ {
+ var disposable = disposables.Last();
+ disposables.RemoveAt(disposables.Count - 1);
+ incrementallyDisposedQueue.Add(disposable);
+ }
+ disposables.Clear();
}
- disposables.Clear();
}
+
+ if (incrementallyDisposedQueue.Count > 0)
+ {
+ if (!incrementalDisposeSupported ||
+ (!fullCollectRequested && !ProcessorController.Instance.Initialized))
+ {
+ // Full Dispose if IncrementalDisposeSupported is false, or ProcessorController is not initialized yet.
+ fullCollectRequested = true;
+ }
+ ProcessDisposablesIncrementally();
+ }
+ }
+
+ private void ProcessDisposablesIncrementally()
+ {
+ var disposeCount = fullCollectRequested ? incrementallyDisposedQueue.Count
+ : Math.Min(incrementallyDisposedQueue.Count, Math.Max(minimumIncrementalCount, incrementallyDisposedQueue.Count * minimumIncrementalRate / 100));
+
+ DebugFileLogging.Instance.WriteLog((fullCollectRequested ? "Fully" : "Incrementally") + $" dispose {disposeCount} disposables. Will remained disposables count is {incrementallyDisposedQueue.Count - disposeCount}.\n");
+
+ fullCollectRequested = false;
+
+ // Dispose item from end, due to the performance issue.
+ while (disposeCount > 0 && incrementallyDisposedQueue.Count > 0)
+ {
+ --disposeCount;
+ var disposable = incrementallyDisposedQueue.Last();
+ incrementallyDisposedQueue.RemoveAt(incrementallyDisposedQueue.Count - 1);
+
+ DebugFileLogging.Instance.WriteLog($"disposable.Dispose(); type={disposable.GetType().FullName}, hash={disposable.GetHashCode()}");
+ disposable.Dispose();
+ }
+
+ if (incrementallyDisposedQueue.Count > 0)
+ {
+ if (ProcessorController.Instance.Initialized && !processorRegistered)
+ {
+ processorRegistered = true;
+ ProcessorController.Instance.ProcessorOnceEvent += TriggerProcessDisposables;
+ ProcessorController.Instance.Awake();
+ }
+ }
+
+ DebugFileLogging.Instance.WriteLog($"Incrementally dispose finished.\n");
}
}
}
internal sealed class ProcessorController : Disposable
{
private static ProcessorController instance = null;
+ private bool initialied = false;
private ProcessorController() : this(Interop.ProcessorController.New(), true)
{
internal ProcessorController(global::System.IntPtr cPtr, bool cMemoryOwn) : base(cPtr, cMemoryOwn)
{
- onceEventIndex = 0u;
- internalProcessorOnceEvent = new EventHandler[2];
- internalProcessorOnceEvent[0] = null;
- internalProcessorOnceEvent[1] = null;
-
- processorCallback = new ProcessorEventHandler(Process);
- Interop.ProcessorController.SetCallback(SwigCPtr, processorCallback);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public event EventHandler ProcessorEvent;
public event EventHandler LayoutProcessorEvent;
+ public bool Initialized => initialied;
+
public static ProcessorController Instance
{
get
}
}
+ public void Initialize()
+ {
+ if (initialied == false)
+ {
+ initialied = true;
+
+ Interop.ProcessorController.Initialize(SwigCPtr);
+
+ onceEventIndex = 0u;
+ internalProcessorOnceEvent = new EventHandler[2];
+ internalProcessorOnceEvent[0] = null;
+ internalProcessorOnceEvent[1] = null;
+
+ processorCallback = new ProcessorEventHandler(Process);
+ Interop.ProcessorController.SetCallback(SwigCPtr, processorCallback);
+ NDalicPINVOKE.ThrowExceptionIfExists();
+ }
+ }
+
public void Process()
{
// Let us add once event into 1 index during 0 event invoke
public void Awake()
{
Interop.ProcessorController.Awake(SwigCPtr);
+ NDalicPINVOKE.ThrowExceptionIfExists();
}
} // class ProcessorController
} // namespace Tizen.NUI