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.
111 // 2023-12-18 Block this logic since some APP call some thread-dependency objects before application start.
112 // ProcessDisposables();
116 public void TriggerProcessDisposables(object o, EventArgs e)
118 processorRegistered = false;
120 if (initialized && eventThreadCallback != null)
122 if (!eventThreadCallbackTriggered)
124 eventThreadCallbackTriggered = true;
125 eventThreadCallback.Trigger();
130 // Flush Disposable queue synchronously if it is not initialized yet.
131 // TODO : Need to check thread here if we need.
133 // 2023-12-18 Block this logic since some APP call some thread-dependency objects before application start.
134 // ProcessDisposables();
138 public void ProcessDisposables()
140 eventThreadCallbackTriggered = false;
144 if (disposables.Count > 0)
146 DebugFileLogging.Instance.WriteLog($"Newly add {disposables.Count} count of disposables. Total disposables count is {incrementallyDisposedQueue.Count + disposables.Count}.\n");
147 // Move item from end, due to the performance issue.
148 while (disposables.Count > 0)
150 var disposable = disposables.Last();
151 disposables.RemoveAt(disposables.Count - 1);
152 incrementallyDisposedQueue.Add(disposable);
158 if (incrementallyDisposedQueue.Count > 0)
160 if (!incrementalDisposeSupported ||
161 (!fullCollectRequested && !ProcessorController.Instance.Initialized))
163 // Full Dispose if IncrementalDisposeSupported is false, or ProcessorController is not initialized yet.
164 fullCollectRequested = true;
166 ProcessDisposablesIncrementally();
170 private void ProcessDisposablesIncrementally()
172 var disposeCount = fullCollectRequested ? incrementallyDisposedQueue.Count
173 : Math.Min(incrementallyDisposedQueue.Count, Math.Max(minimumIncrementalCount, incrementallyDisposedQueue.Count * minimumIncrementalRate / 100));
175 DebugFileLogging.Instance.WriteLog((fullCollectRequested ? "Fully" : "Incrementally") + $" dispose {disposeCount} disposables. Will remained disposables count is {incrementallyDisposedQueue.Count - disposeCount}.\n");
177 fullCollectRequested = false;
179 // Dispose item from end, due to the performance issue.
180 while (disposeCount > 0 && incrementallyDisposedQueue.Count > 0)
183 var disposable = incrementallyDisposedQueue.Last();
184 incrementallyDisposedQueue.RemoveAt(incrementallyDisposedQueue.Count - 1);
186 DebugFileLogging.Instance.WriteLog($"disposable.Dispose(); type={disposable.GetType().FullName}, hash={disposable.GetHashCode()}");
187 disposable.Dispose();
190 if (incrementallyDisposedQueue.Count > 0)
192 if (ProcessorController.Instance.Initialized && !processorRegistered)
194 processorRegistered = true;
195 ProcessorController.Instance.ProcessorOnceEvent += TriggerProcessDisposables;
196 ProcessorController.Instance.Awake();
200 DebugFileLogging.Instance.WriteLog($"Incrementally dispose finished.\n");