-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Switch.cs
210 lines (191 loc) · 6.78 KB
/
Switch.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//-----------------------------------------------------------------------
// <copyright file="Switch.cs" company="Akka.NET Project">
// Copyright (C) 2009-2023 Lightbend Inc. <http://www.lightbend.com>
// Copyright (C) 2013-2023 .NET Foundation <https://github.com/akkadotnet/akka.net>
// </copyright>
//-----------------------------------------------------------------------
using System;
namespace Akka.Util
{
/// <summary>
/// An atomic switch that can be either on or off
/// </summary>
public class Switch
{
private readonly Util.AtomicBoolean _switch;
private readonly object _lock = new();
/// <summary>
/// TBD
/// </summary>
/// <param name="startAsOn">TBD</param>
public Switch(bool startAsOn = false)
{
_switch = new Util.AtomicBoolean(startAsOn);
}
/// <summary>
/// TBD
/// </summary>
/// <param name="from">TBD</param>
/// <param name="action">TBD</param>
/// <returns>TBD</returns>
protected bool TranscendFrom(bool from, Action action)
{
lock(_lock)
{
if(_switch.CompareAndSet(from, !from))
{
try
{
action();
}
catch(Exception)
{
_switch.CompareAndSet(!from, from); // revert status
throw;
}
return true;
}
return false;
}
}
/// <summary>
/// Executes the provided action if the lock is on. This is done under a lock so be _very_ careful with longrunning/blocking operations in it.
/// Only executes the action if the switch is on, and switches it off immediately after obtaining the lock.
/// Will switch it back on if the provided action throws an exception.
/// </summary>
/// <param name="action">TBD</param>
/// <returns>Returns <c>true</c> if the switch was switched off</returns>
public bool SwitchOff(Action action)
{
return TranscendFrom(true, action);
}
/// <summary>
/// Executes the provided action if the lock is off. This is done under a lock so be _very_ careful with longrunning/blocking operations in it.
/// Only executes the action if the switch is off, and switches it on immediately after obtaining the lock.
/// Will switch it back off if the provided action throws an exception.
/// </summary>
/// <param name="action">TBD</param>
/// <returns>Returns <c>true</c> if the switch was switched on</returns>
public bool SwitchOn(Action action)
{
return TranscendFrom(false, action);
}
/// <summary>
/// Switches the switch off (if on). Uses locking.
/// </summary>
/// <returns>Returns <c>true</c> if the switch was switched off</returns>
public bool SwitchOff()
{
lock(_lock)
{
return _switch.CompareAndSet(true, false);
}
}
/// <summary>
/// Switches the switch on (if off). Uses locking.
/// </summary>
/// <returns>Returns <c>true</c> if the switch was switched on</returns>
public bool SwitchOn()
{
lock(_lock)
{
return _switch.CompareAndSet(false, true);
}
}
/// <summary>
/// Executes the provided action and returns if the action was executed or not, if the switch is IMMEDIATELY on (i.e. no lock involved)
/// </summary>
/// <param name="action">The action.</param>
/// <returns>Return <c>true</c> if the switch was on</returns>
public bool IfOn(Action action)
{
if(_switch.Value)
{
action();
return true;
}
return false;
}
/// <summary>
/// Executes the provided action and returns if the action was executed or not, if the switch is IMMEDIATELY off (i.e. no lock involved)
/// </summary>
/// <param name="action">The action.</param>
/// <returns>Return <c>true</c> if the switch was off</returns>
public bool IfOff(Action action)
{
if(!_switch.Value)
{
action();
return true;
}
return false;
}
/// <summary>
/// Executes the provided action and returns if the action was executed or not, if the switch is on, waiting for any pending changes to happen before (locking)
/// Be careful of longrunning or blocking within the provided action as it can lead to deadlocks or bad performance
/// </summary>
/// <param name="action">TBD</param>
/// <returns>TBD</returns>
public bool WhileOn(Action action)
{
lock(_lock)
{
if(_switch.Value)
{
action();
return true;
}
return false;
}
}
/// <summary>
/// Executes the provided action and returns if the action was executed or not, if the switch is off, waiting for any pending changes to happen before (locking)
/// Be careful of longrunning or blocking within the provided action as it can lead to deadlocks or bad performance
/// </summary>
/// <param name="action">TBD</param>
/// <returns>TBD</returns>
public bool WhileOff(Action action)
{
lock(_lock)
{
if(!_switch.Value)
{
action();
return true;
}
return false;
}
}
/// <summary>
/// Gets a value indicating whether this switch is on. No locking.
/// </summary>
/// <value>
/// <c>true</c> if this instance is on; otherwise, <c>false</c>.
/// </value>
public bool IsOn
{
get { return _switch.Value; }
}
/// <summary>
/// Gets a value indicating whether this switch is off. No locking.
/// </summary>
/// <value>
/// <c>true</c> if this instance is off; otherwise, <c>false</c>.
/// </value>
public bool IsOff
{
get { return !_switch.Value; }
}
/// <summary>
/// TBD
/// </summary>
/// <param name="action">TBD</param>
public void Locked(Action action)
{
lock (_lock)
{
action();
}
}
}
}