Crunchy-Downloader/CRD/Utils/PeriodicWorkRunner.cs
Elwador 985fd9c00f Added **tray icon** [#393](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/393).
Added **ability to switch between account profiles** [#372](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/372).
Added option to **execute a file when the download queue finishes** [#392](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/392).
Added **auto history refresh / auto add to queue** [#394](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/394).
Changed **font loading** to also include fonts from the local fonts folder that are not available on Crunchyroll [#371](https://github.com/Crunchy-DL/Crunchy-Downloader/issues/371).
Updated packages to latest versions
Fixed **history not being saved** after it was updated via the calendar
Fixed **Downloaded toggle in history** being slow for large seasons
2026-03-04 18:17:28 +01:00

83 lines
No EOL
2.3 KiB
C#

using System;
using System.Threading;
using System.Threading.Tasks;
namespace CRD.Utils;
public class PeriodicWorkRunner(Func<CancellationToken, Task> work) : IDisposable{
private CancellationTokenSource? cts;
private Task? loopTask;
private TimeSpan currentInterval;
public DateTime LastRunTime = DateTime.MinValue;
public void StartOrRestart(TimeSpan interval, bool runImmediately = false, bool force = false){
if (interval <= TimeSpan.Zero){
Stop();
currentInterval = Timeout.InfiniteTimeSpan;
return;
}
if (!force && interval == currentInterval){
return;
}
currentInterval = interval;
Stop();
cts = new CancellationTokenSource();
loopTask = RunLoopAsync(interval, runImmediately, cts.Token);
}
public void StartOrRestartMinutes(int minutes, bool runImmediately = false, bool force = false)
=> StartOrRestart(TimeSpan.FromMinutes(minutes), runImmediately);
public void Stop(){
if (cts is null) return;
try{
cts.Cancel();
} finally{
cts.Dispose();
cts = null;
}
}
private async Task RunLoopAsync(TimeSpan interval, bool runImmediately, CancellationToken token){
if (runImmediately){
await SafeRunWork(token).ConfigureAwait(false);
}
using var timer = new PeriodicTimer(interval);
try{
while (await timer.WaitForNextTickAsync(token).ConfigureAwait(false)){
await SafeRunWork(token).ConfigureAwait(false);
}
} catch (OperationCanceledException){
}
}
private int running = 0;
private async Task SafeRunWork(CancellationToken token){
if (Interlocked.Exchange(ref running, 1) == 1){
Console.Error.WriteLine("Task is already running!");
return;
}
try{
await work(token).ConfigureAwait(false);
LastRunTime = DateTime.Now;
} catch (OperationCanceledException) when (token.IsCancellationRequested){
} catch (Exception ex){
Console.Error.WriteLine(ex);
} finally{
Interlocked.Exchange(ref running, 0);
}
}
public void Dispose() => Stop();
}