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 initialized = 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");
55 if (processorRegistered && ProcessorController.Instance.Initialized)
57 processorRegistered = false;
58 ProcessorController.Instance.ProcessorOnceEvent -= TriggerProcessDisposables;
62 public static DisposeQueue Instance
64 get { return disposableQueue; }
67 public bool IncrementalDisposeSupported
69 get => incrementalDisposeSupported;
70 set => incrementalDisposeSupported = value;
73 public bool FullyDisposeNextCollect
75 get => fullCollectRequested;
76 set => fullCollectRequested = value;
79 public void Initialize()
83 disposeQueueProcessDisposablesDelegate = new EventThreadCallback.CallbackDelegate(ProcessDisposables);
84 eventThreadCallback = new EventThreadCallback(disposeQueueProcessDisposablesDelegate);
87 DebugFileLogging.Instance.WriteLog("DiposeTest START");
91 public void Add(IDisposable disposable)
95 disposables.Add(disposable);
98 if (initialized && eventThreadCallback != null)
100 if (!eventThreadCallbackTriggered)
102 eventThreadCallbackTriggered = true;
103 eventThreadCallback.Trigger();
108 // Flush Disposable queue synchronously if it is not initialized yet.
109 // TODO : Need to check thread here if we need.
110 ProcessDisposables();
114 public void TriggerProcessDisposables(object o, EventArgs e)
116 processorRegistered = false;
118 if (initialized && eventThreadCallback != null)
120 if (!eventThreadCallbackTriggered)
122 eventThreadCallbackTriggered = true;
123 eventThreadCallback.Trigger();
128 // Flush Disposable queue synchronously if it is not initialized yet.
129 // TODO : Need to check thread here if we need.
130 ProcessDisposables();
134 public void ProcessDisposables()
136 eventThreadCallbackTriggered = false;
140 if (disposables.Count > 0)
142 DebugFileLogging.Instance.WriteLog($"Newly add {disposables.Count} count of disposables. Total disposables count is {incrementallyDisposedQueue.Count + disposables.Count}.\n");
143 // Move item from end, due to the performance issue.
144 while (disposables.Count > 0)
146 var disposable = disposables.Last();
147 disposables.RemoveAt(disposables.Count - 1);
148 incrementallyDisposedQueue.Add(disposable);
154 if (incrementallyDisposedQueue.Count > 0)
156 if (!incrementalDisposeSupported ||
157 (!fullCollectRequested && !ProcessorController.Instance.Initialized))
159 // Full Dispose if IncrementalDisposeSupported is false, or ProcessorController is not initialized yet.
160 fullCollectRequested = true;
162 ProcessDisposablesIncrementally();
166 private void ProcessDisposablesIncrementally()
168 var disposeCount = fullCollectRequested ? incrementallyDisposedQueue.Count
169 : Math.Min(incrementallyDisposedQueue.Count, Math.Max(minimumIncrementalCount, incrementallyDisposedQueue.Count * minimumIncrementalRate / 100));
171 DebugFileLogging.Instance.WriteLog((fullCollectRequested ? "Fully" : "Incrementally") + $" dispose {disposeCount} disposables. Will remained disposables count is {incrementallyDisposedQueue.Count - disposeCount}.\n");
173 fullCollectRequested = false;
175 // Dispose item from end, due to the performance issue.
176 while (disposeCount > 0 && incrementallyDisposedQueue.Count > 0)
179 var disposable = incrementallyDisposedQueue.Last();
180 incrementallyDisposedQueue.RemoveAt(incrementallyDisposedQueue.Count - 1);
182 DebugFileLogging.Instance.WriteLog($"disposable.Dispose(); type={disposable.GetType().FullName}, hash={disposable.GetHashCode()}");
183 disposable.Dispose();
186 if (incrementallyDisposedQueue.Count > 0)
188 if (ProcessorController.Instance.Initialized && !processorRegistered)
190 processorRegistered = true;
191 ProcessorController.Instance.ProcessorOnceEvent += TriggerProcessDisposables;
192 ProcessorController.Instance.Awake();
196 DebugFileLogging.Instance.WriteLog($"Incrementally dispose finished.\n");