2 * Copyright(c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using System.Collections.Generic;
20 using System.Diagnostics.CodeAnalysis;
25 [SuppressMessage("Microsoft.Design", "CA1001:Types that own disposable fields should be disposable", Justification = "This is a singleton class and is not disposed")]
26 internal class DisposeQueue
28 private static readonly DisposeQueue disposableQueue = new DisposeQueue();
29 private List<IDisposable> disposables = new List<IDisposable>();
30 private System.Object listLock = new object();
32 // Dispose incrementally at least max(100, incrementallyDisposedQueue * 20%) for each 1 event callback
33 private const long minimumIncrementalCount = 100;
34 private const long minimumIncrementalRate = 20;
35 private List<IDisposable> incrementallyDisposedQueue = new List<IDisposable>();
37 private EventThreadCallback eventThreadCallback;
38 private EventThreadCallback.CallbackDelegate disposeQueueProcessDisposablesDelegate;
40 private bool initialied = false;
41 private bool processorRegistered = false;
42 private bool eventThreadCallbackTriggered = false;
44 private bool incrementalDisposeSupported = false;
45 private bool fullCollectRequested = false;
47 private DisposeQueue()
53 Tizen.Log.Debug("NUI", $"DisposeQueue is destroyed\n");
57 public static DisposeQueue Instance
59 get { return disposableQueue; }
62 public bool IncrementalDisposeSupported
64 get => incrementalDisposeSupported;
65 set => incrementalDisposeSupported = value;
68 public bool FullyDisposeNextCollect
70 get => fullCollectRequested;
71 set => fullCollectRequested = value;
74 public void Initialize()
78 disposeQueueProcessDisposablesDelegate = new EventThreadCallback.CallbackDelegate(ProcessDisposables);
79 eventThreadCallback = new EventThreadCallback(disposeQueueProcessDisposablesDelegate);
82 DebugFileLogging.Instance.WriteLog("DiposeTest START");
86 public void Add(IDisposable disposable)
90 disposables.Add(disposable);
93 if (initialied && eventThreadCallback != null && !eventThreadCallbackTriggered)
95 eventThreadCallbackTriggered = true;
96 eventThreadCallback.Trigger();
100 public void TriggerProcessDisposables(object o, EventArgs e)
102 processorRegistered = false;
104 if (eventThreadCallback != null && !eventThreadCallbackTriggered)
106 eventThreadCallbackTriggered = true;
107 eventThreadCallback.Trigger();
111 public void ProcessDisposables()
113 eventThreadCallbackTriggered = false;
117 if (disposables.Count > 0)
119 DebugFileLogging.Instance.WriteLog($"Newly add {disposables.Count} count of disposables. Total disposables count is {incrementallyDisposedQueue.Count + disposables.Count}.\n");
120 // Move item from end, due to the performance issue.
121 while (disposables.Count > 0)
123 var disposable = disposables.Last();
124 disposables.RemoveAt(disposables.Count - 1);
125 incrementallyDisposedQueue.Add(disposable);
131 if (incrementallyDisposedQueue.Count > 0)
133 if (!incrementalDisposeSupported ||
134 (!fullCollectRequested && !ProcessorController.Instance.Initialized))
136 // Full Dispose if IncrementalDisposeSupported is false, or ProcessorController is not initialized yet.
137 fullCollectRequested = true;
139 ProcessDisposablesIncrementally();
143 private void ProcessDisposablesIncrementally()
145 var disposeCount = fullCollectRequested ? incrementallyDisposedQueue.Count
146 : Math.Min(incrementallyDisposedQueue.Count, Math.Max(minimumIncrementalCount, incrementallyDisposedQueue.Count * minimumIncrementalRate / 100));
148 DebugFileLogging.Instance.WriteLog((fullCollectRequested ? "Fully" : "Incrementally") + $" dispose {disposeCount} disposables. Will remained disposables count is {incrementallyDisposedQueue.Count - disposeCount}.\n");
150 fullCollectRequested = false;
152 // Dispose item from end, due to the performance issue.
153 while (disposeCount > 0 && incrementallyDisposedQueue.Count > 0)
156 var disposable = incrementallyDisposedQueue.Last();
157 incrementallyDisposedQueue.RemoveAt(incrementallyDisposedQueue.Count - 1);
159 DebugFileLogging.Instance.WriteLog($"disposable.Dispose(); type={disposable.GetType().FullName}, hash={disposable.GetHashCode()}");
160 disposable.Dispose();
163 if (incrementallyDisposedQueue.Count > 0)
165 if (ProcessorController.Instance.Initialized && !processorRegistered)
167 processorRegistered = true;
168 ProcessorController.Instance.ProcessorOnceEvent += TriggerProcessDisposables;
169 ProcessorController.Instance.Awake();
173 DebugFileLogging.Instance.WriteLog($"Incrementally dispose finished.\n");