-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathExitUtils.cs
More file actions
130 lines (108 loc) · 4.02 KB
/
Copy pathExitUtils.cs
File metadata and controls
130 lines (108 loc) · 4.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
using System;
using System.Collections.Generic;
using System.Diagnostics;
using DCL.Diagnostics;
using UnityEngine;
using Utility.Multithreading;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace DCL.Utility
{
public class OnQuittingCleanUpCandidate
{
private readonly string name;
private readonly Action callback;
public string Name => name;
public OnQuittingCleanUpCandidate(string name, Action callback)
{
this.name = name;
this.callback = callback;
}
internal void Execute(Stopwatch stopwatch)
{
long startedAtMs = stopwatch.ElapsedMilliseconds;
try
{
callback.Invoke();
}
catch (Exception e)
{
ReportHub.LogException(e, ReportCategory.UNSPECIFIED);
}
long elapsedMs = stopwatch.ElapsedMilliseconds - startedAtMs;
ReportHub.LogProductionInfo($"[ExitUtils] '{name}' cleanup took {elapsedMs}ms (total {stopwatch.ElapsedMilliseconds}ms)");
}
}
public static class ExitUtils
{
private static readonly Mutex<List<OnQuittingCleanUpCandidate>> candidates = new (new ());
private static readonly Atomic<bool> isExiting = new (false);
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void SubscribeToApplicationQuitting()
{
Application.quitting += OnApplicationQuitting;
}
private static void OnApplicationQuitting()
{
Application.quitting -= OnApplicationQuitting;
ReportHub.LogProductionInfo("[ExitUtils] triggered by Unity's Application.quitting");
Exit();
}
public static void RegisterCleanUpCandidate(OnQuittingCleanUpCandidate candidate)
{
if (isExiting)
{
ReportHub.LogProductionInfo($"[ExitUtils] Ignored RegisterCleanUpCandidate('{candidate.Name}') because Exit() is already in progress");
return;
}
using var scope = candidates.Lock();
for (var i = 0; i < scope.Value.Count; i++)
{
if (scope.Value[i].Name == candidate.Name)
throw new InvalidOperationException($"[ExitUtils] Cleanup candidate '{candidate.Name}' is already registered");
}
scope.Value.Add(candidate);
}
public static void UnregisterCleanUpCandidate(string name)
{
if (isExiting)
{
ReportHub.LogProductionInfo($"[ExitUtils] Ignored UnregisterCleanUpCandidate('{name}') because Exit() is already in progress");
return;
}
using var scope = candidates.Lock();
for (var i = 0; i < scope.Value.Count; i++)
{
if (scope.Value[i].Name == name)
{
scope.Value.RemoveAt(i);
return;
}
}
}
public static void Exit()
{
Stopwatch stopwatch = Stopwatch.StartNew();
ReportHub.LogProductionInfo($"[ExitUtils] Exit requested at {stopwatch.ElapsedMilliseconds}ms");
if (isExiting)
{
ReportHub.LogProductionInfo("[ExitUtils] Exit() ignored because it is already in progress");
return;
}
isExiting.Set(true);
using (var scope = candidates.Lock())
{
foreach (OnQuittingCleanUpCandidate candidate in scope.Value)
candidate.Execute(stopwatch);
}
ReportHub.LogProductionInfo($"[ExitUtils] CleanUpCandidates finished at {stopwatch.ElapsedMilliseconds}ms");
#if UNITY_EDITOR
EditorApplication.isPlaying = false;
#else
UnityEngine.Application.Quit();
#endif
ReportHub.LogProductionInfo($"[ExitUtils] Quit call dispatched at {stopwatch.ElapsedMilliseconds}ms");
}
}
}