-
Notifications
You must be signed in to change notification settings - Fork 1
/
Rollback.cs
132 lines (117 loc) · 3.02 KB
/
Rollback.cs
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
131
132
// ----------------------------------------------------------------------------
// The MIT License
// Rollback https://github.com/korchoon/rollback
// Copyright (c) 2016-2022 Mikhail Korchun <[email protected]>
// ----------------------------------------------------------------------------
using System;
public interface IRollback
{
bool IsDisposed { get; }
void Defer(Action action);
void RemoveDeferred(Action action);
}
/// <summary>
/// Container to defer actions to be executed on Dispose (resource cleanup, cancel side effects)
/// </summary>
public class Rollback : IRollback, IDisposable
{
public bool IsDisposed { get; private set; }
private readonly object lockObject;
private Action deferredActions;
public Rollback()
{
lockObject = new object();
IsDisposed = false;
}
/// <summary>
/// Defer action to be executed upon Dispose
/// </summary>
/// <param name="action"></param>
public void Defer(Action action)
{
lock (lockObject)
{
Asr.IsFalse(IsDisposed, "This rollback is disposed. Cannot defer action.");
deferredActions = action + deferredActions; // first in last out order
}
}
/// <summary>
/// Remove deferred action so it won't be executed
/// </summary>
/// <param name="action"></param>
public void RemoveDeferred(Action action)
{
lock (lockObject)
{
Asr.IsFalse(IsDisposed, "This rollback is disposed. Cannot remove deferred action.");
#if DEBUG
var countPrev = deferredActions.GetInvocationList().Length;
deferredActions -= action;
if (deferredActions.GetInvocationList().Length == countPrev)
{
Asr.Fail("Trying to remove action which wasn't deferred");
}
#else
deferredActions -= action;
#endif
}
}
/// <summary>
/// Executes deferred actions in reverse order
/// </summary>
public void Dispose()
{
lock (lockObject)
{
Asr.IsFalse(IsDisposed, "Rollback is already disposed");
IsDisposed = true;
deferredActions?.Invoke();
deferredActions = null;
}
}
}
public static class RollbackExtensions
{
/// <summary>
/// Opens a child rollback which will be disposed with current one. Allows cascade disposals
/// </summary>
/// <returns></returns>
public static Rollback OpenRollback(this IRollback parentRollback)
{
Asr.IsFalse(parentRollback.IsDisposed, "This rollback is disposed. Cannot open rollback from it.");
var result = new Rollback();
parentRollback.Defer(DisposeChild);
result.Defer(CancelDisposingChild);
return result;
void DisposeChild()
{
result.Dispose();
}
void CancelDisposingChild()
{
parentRollback.RemoveDeferred(DisposeChild);
}
}
}
public static class Asr
{
[Conditional("DEBUG")]
public static void Fail(string message)
{
UnityEngine.Assertions.Assert.IsTrue(false, message);
}
[Conditional("DEBUG")]
public static void IsTrue(bool expression, string message = null)
{
if (expression)
return;
Fail(message);
}
[Conditional("DEBUG")]
public static void IsFalse(bool expression, string message = null)
{
if (!expression)
return;
Fail(message);
}
}