diff --git a/Plugin/Common/TaskCache.cs b/Plugin/Common/TaskCache.cs new file mode 100644 index 0000000..36bce21 --- /dev/null +++ b/Plugin/Common/TaskCache.cs @@ -0,0 +1,72 @@ +using System.Collections.Concurrent; + +namespace LootValueEX.Common; + +internal class TaskCache +{ + private readonly SemaphoreSlim _semaphore; + private readonly ConcurrentDictionary _taskDict; + + internal TaskCache(int maxTasks = 10) + { + _semaphore = new SemaphoreSlim(maxTasks); + _taskDict = new ConcurrentDictionary(); + } + + private bool IsTaskCancelled(Guid taskGuid) => + _taskDict.TryGetValue(taskGuid, out Task task) && task.Status == TaskStatus.Canceled; + + internal Guid AddTask(Func taskFactory) + { + Guid taskGuid = Guid.NewGuid(); + CancellationTokenSource cts = new CancellationTokenSource(); + Task task = taskFactory(cts.Token); + + _taskDict[taskGuid] = task; + + return taskGuid; + } + + internal bool RemoveTask(Guid taskGuid) + { + if (_taskDict.TryGetValue(taskGuid, out Task task)) + { + if (_taskDict.TryRemove(taskGuid, out _)) + { + task.Dispose(); + _semaphore.Release(); + return true; + } + } + + return false; + } + + internal async Task RunTaskByGuidAsync(Guid taskGuid) + { + await _semaphore.WaitAsync(); + try + { + if ((_taskDict.TryGetValue(taskGuid, out Task task))) + { + if (!task.IsCompleted && !task.Status.Equals(TaskStatus.Running)) + await task; + return task; + } + + Plugin.Log.LogError($"Task {taskGuid.ToString()} not found."); + } + catch (OperationCanceledException) + { + RemoveTask(taskGuid); + Plugin.Log.LogDebug($"Task {taskGuid.ToString()} got cancelled."); + } + return Task.FromResult(false); + } + + internal async Task AddAndRunTaskAsync(Func taskFactory) + { + Guid taskGuid = AddTask(taskFactory); + return await RunTaskByGuidAsync(taskGuid); + } +} \ No newline at end of file