-
Notifications
You must be signed in to change notification settings - Fork 75
/
Copy pathTimerUpdateTrigger.cs
146 lines (122 loc) · 4.41 KB
/
TimerUpdateTrigger.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
// ReSharper disable MemberCanBePrivate.Global
using System;
using System.Threading;
using System.Threading.Tasks;
namespace RGB.NET.Core;
/// <inheritdoc />
/// <summary>
/// Represents an update trigger that triggers in a set interval.
/// </summary>
public sealed class TimerUpdateTrigger : AbstractUpdateTrigger
{
#region Properties & Fields
private readonly Lock _lock = new();
private readonly CustomUpdateData? _customUpdateData;
/// <summary>
/// Gets or sets the update loop of this trigger.
/// </summary>
private Task? _updateTask;
/// <summary>
/// Gets or sets the cancellation token source used to create the cancellation token checked by the <see cref="_updateTask"/>.
/// </summary>
private CancellationTokenSource? _updateTokenSource;
/// <summary>
/// Gets or sets the cancellation token checked by the <see cref="_updateTask"/>.
/// </summary>
private CancellationToken _updateToken;
private double _updateFrequency = 1.0 / 30.0;
/// <summary>
/// Gets or sets the update-frequency in seconds. (Calculate by using '1.0 / updates per second')
/// </summary>
public double UpdateFrequency
{
get => _updateFrequency;
set => SetProperty(ref _updateFrequency, value);
}
/// <summary>
/// Gets the time it took the last update-loop cycle to run.
/// </summary>
public override double LastUpdateTime { get; protected set; }
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="TimerUpdateTrigger"/> class.
/// </summary>
/// <param name="autostart">A value indicating if the trigger should automatically <see cref="Start"/> right after construction.</param>
public TimerUpdateTrigger(bool autostart = true)
{
if (autostart)
// ReSharper disable once VirtualMemberCallInConstructor - HACK DarthAffe 01.06.2021: I've no idea how to correctly handle that case, for now just disable it
Start();
}
/// <summary>
/// Initializes a new instance of the <see cref="TimerUpdateTrigger"/> class.
/// </summary>
/// <param name="customUpdateData">The update-data passed on each update triggered.</param>
/// <param name="autostart">A value indicating if the trigger should automatically <see cref="Start"/> right after construction.</param>
public TimerUpdateTrigger(CustomUpdateData? customUpdateData, bool autostart = true)
{
this._customUpdateData = customUpdateData;
if (autostart)
// ReSharper disable once VirtualMemberCallInConstructor - HACK DarthAffe 01.06.2021: I've no idea how to correctly handle that case, for now just disable it
Start();
}
#endregion
#region Methods
/// <summary>
/// Starts the trigger if needed, causing it to performing updates.
/// </summary>
public override void Start()
{
lock (_lock)
{
if (_updateTask == null)
{
_updateTokenSource?.Dispose();
_updateTokenSource = new CancellationTokenSource();
_updateTask = Task.Factory.StartNew(UpdateLoop, (_updateToken = _updateTokenSource.Token), TaskCreationOptions.LongRunning, TaskScheduler.Default);
}
}
}
/// <summary>
/// Stops the trigger if running, causing it to stop performing updates.
/// </summary>
public void Stop()
{
lock (_lock)
{
if (_updateTask != null)
{
_updateTokenSource?.Cancel();
try
{
// ReSharper disable once MethodSupportsCancellation
_updateTask.Wait();
}
catch (AggregateException)
{
// ignored
}
finally
{
_updateTask.Dispose();
_updateTask = null;
}
}
}
}
private void UpdateLoop()
{
OnStartup();
using (TimerHelper.RequestHighResolutionTimer())
while (!_updateToken.IsCancellationRequested)
LastUpdateTime = TimerHelper.Execute(TimerExecute, UpdateFrequency * 1000);
}
private void TimerExecute() => OnUpdate(_customUpdateData);
/// <inheritdoc />
public override void Dispose()
{
Stop();
}
#endregion
}