namespace Duplicati.Library.RemoteControl; /// /// Helper class to support period refreshes, with signal support /// public class PeriodicRefresher : IDisposable { /// /// The last time the refresh was done /// private DateTime _lastRefresh = DateTime.MinValue; /// /// The interval between refreshes /// private readonly TimeSpan _refreshInterval; /// /// The minimum interval between refreshes (debounce) /// private readonly TimeSpan _minimumRefreshInterval; /// /// The action to run on refresh /// private readonly Func _refreshAction; /// /// The cancellation token source /// private readonly CancellationTokenSource _cts; /// /// The task completion source used to signal a refresh /// private TaskCompletionSource _tcs; /// /// Creates a new instance of the class /// /// The interval between refreshes /// The minimum interval between refreshes (debounce) /// The action to run on refresh /// The cancellation token to use public PeriodicRefresher(TimeSpan refreshInterval, TimeSpan minimumRefresh, Func refreshAction, CancellationToken cancellationToken) { _refreshInterval = refreshInterval; _minimumRefreshInterval = minimumRefresh; _refreshAction = refreshAction; _cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); _tcs = new TaskCompletionSource(); } /// /// Signals a refresh /// public void Signal() { _tcs.TrySetResult(true); } /// /// Runs the refresh loop /// /// The task to await public async Task RunLoopAsync() { while (!_cts.Token.IsCancellationRequested) { var t = await Task.WhenAny(_tcs.Task, Task.Delay(_refreshInterval, _cts.Token)); if (_cts.Token.IsCancellationRequested) return; if (t == _tcs.Task) Interlocked.Exchange(ref _tcs, new TaskCompletionSource()); if ((_lastRefresh + _minimumRefreshInterval) < DateTime.Now) { _lastRefresh = DateTime.Now; await _refreshAction(_cts.Token); } } } /// public void Dispose() { _cts.Cancel(); _cts.Dispose(); } }