36 lines
995 B
C#
36 lines
995 B
C#
using System.Threading;
|
|
|
|
namespace SimpleAdmin.Application;
|
|
|
|
/// <summary>
|
|
/// 使同一异步调用链可重入,独立请求仍须取得外部互斥锁。
|
|
/// 不在 Acquire 中设置 AsyncLocal,避免值不能流回调用方的问题。
|
|
/// </summary>
|
|
internal static class OnboardingOperationGate
|
|
{
|
|
private sealed record Frame(string Key, Frame Parent);
|
|
private static readonly AsyncLocal<Frame> Held = new();
|
|
|
|
internal static async Task<T> Run<T>(
|
|
string key,
|
|
Func<Task<IAsyncDisposable>> acquire,
|
|
Func<Task<T>> action)
|
|
{
|
|
for (var frame = Held.Value; frame != null; frame = frame.Parent)
|
|
if (frame.Key == key)
|
|
return await action();
|
|
|
|
await using var lease = await acquire();
|
|
var previous = Held.Value;
|
|
Held.Value = new Frame(key, previous);
|
|
try
|
|
{
|
|
return await action();
|
|
}
|
|
finally
|
|
{
|
|
Held.Value = previous;
|
|
}
|
|
}
|
|
}
|