Skip to content

Instantly share code, notes, and snippets.

@StephenCleary
Last active July 26, 2024 20:32
Show Gist options
  • Save StephenCleary/706f6787826c95b394cdd984ea47fbb8 to your computer and use it in GitHub Desktop.
Save StephenCleary/706f6787826c95b394cdd984ea47fbb8 to your computer and use it in GitHub Desktop.
A concurrent dictionary of tasks that can be resolved by key.
public sealed class TaskDictionary<TRequest, TResult>
where TRequest : notnull
{
public TaskDictionary(IEqualityComparer<TRequest>? comparer = null)
{
_dictionary = new(comparer);
}
public Task<TResult> GetOrAdd(TRequest request)
{
var result = _dictionary.GetOrAdd(request, _ => CreateTcs());
return result.Task;
}
public bool TryAdd(TRequest request, out Task<TResult> result)
{
var tcs = CreateTcs();
if (!_dictionary.TryAdd(request, tcs))
{
result = Task.FromCanceled<TResult>(new CancellationToken(canceled: true));
return false;
}
result = tcs.Task;
return true;
}
public bool TrySetResult(TRequest request, TResult result)
{
if (!_dictionary.TryRemove(request, out var tcs))
return false;
return tcs.TrySetResult(result);
}
public bool TrySetCanceled(TRequest request, CancellationToken? cancellationToken = null)
{
if (!_dictionary.TryRemove(request, out var tcs))
return false;
return !cancellationToken.HasValue ? tcs.TrySetCanceled() : tcs.TrySetCanceled(cancellationToken.Value);
}
public bool TrySetException(TRequest request, Exception exception)
{
if (!_dictionary.TryRemove(request, out var tcs))
return false;
return tcs.TrySetException(exception);
}
private static TaskCompletionSource<TResult> CreateTcs() =>
new(TaskCreationOptions.RunContinuationsAsynchronously | TaskCreationOptions.DenyChildAttach);
private readonly ConcurrentDictionary<TRequest, TaskCompletionSource<TResult>> _dictionary;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment