using System.Collections.Concurrent; namespace LightlessSync.Utils; public sealed class TaskRegistry where HandleType : notnull { private readonly ConcurrentDictionary> _activeTasks = new(); public Task GetOrStart(HandleType handle, Func taskFactory) => GetOrStartInternal(handle, taskFactory); public Task GetOrStart(HandleType handle, Func> taskFactory) => GetOrStartInternal(handle, taskFactory); public bool TryGetExisting(HandleType handle, out Task task) { if (_activeTasks.TryGetValue(handle, out Lazy? entry)) { task = entry.Value; if (!task.IsCompleted) { return true; } _activeTasks.TryRemove(new KeyValuePair>(handle, entry)); } task = Task.CompletedTask; return false; } private Task GetOrStartInternal(HandleType handle, Func taskFactory) { Lazy entry = _activeTasks.GetOrAdd(handle, _ => CreateEntry(handle, taskFactory)); Task task = entry.Value; if (task.IsCompleted) { _activeTasks.TryRemove(new KeyValuePair>(handle, entry)); } return task; } private Task GetOrStartInternal(HandleType handle, Func> taskFactory) { Lazy entry = _activeTasks.GetOrAdd(handle, _ => CreateEntry(handle, taskFactory)); Task task = entry.Value; if (task.IsCompleted) { _activeTasks.TryRemove(new KeyValuePair>(handle, entry)); } return (Task)task; } private Lazy CreateEntry(HandleType handle, Func taskFactory) { Lazy entry = null!; entry = new Lazy(() => ExecuteAndRemove(handle, entry, taskFactory), LazyThreadSafetyMode.ExecutionAndPublication); return entry; } private Lazy CreateEntry(HandleType handle, Func> taskFactory) { Lazy entry = null!; entry = new Lazy(() => ExecuteAndRemove(handle, entry, taskFactory), LazyThreadSafetyMode.ExecutionAndPublication); return entry; } private async Task ExecuteAndRemove(HandleType handle, Lazy entry, Func taskFactory) { try { await taskFactory().ConfigureAwait(false); } finally { _activeTasks.TryRemove(new KeyValuePair>(handle, entry)); } } private async Task ExecuteAndRemove(HandleType handle, Lazy entry, Func> taskFactory) { try { return await taskFactory().ConfigureAwait(false); } finally { _activeTasks.TryRemove(new KeyValuePair>(handle, entry)); } } }