You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
201 lines
6.5 KiB
201 lines
6.5 KiB
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Tools
|
|
{
|
|
public sealed class AsyncMutex : IDisposable
|
|
{
|
|
private readonly SemaphoreSlim sem;
|
|
|
|
public AsyncMutex()
|
|
{
|
|
sem = new SemaphoreSlim(1, 1);
|
|
}
|
|
private AsyncMutex(SemaphoreSlim from)
|
|
{
|
|
sem = from;
|
|
}
|
|
|
|
private class Lock : IDisposable
|
|
{
|
|
public AsyncMutex Parent { get; }
|
|
public Lock(AsyncMutex held)
|
|
{
|
|
Parent = held;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Parent.sem.Release();
|
|
}
|
|
}
|
|
|
|
public IDisposable Acquire(int msTimeout, CancellationToken token = default)
|
|
{
|
|
sem.Wait(msTimeout, token);
|
|
return new Lock(this);
|
|
}
|
|
|
|
public IDisposable Acquire(CancellationToken token)
|
|
{
|
|
sem.Wait(token);
|
|
return new Lock(this);
|
|
}
|
|
|
|
public IDisposable Acquire()
|
|
{
|
|
sem.Wait();
|
|
return new Lock(this);
|
|
}
|
|
|
|
public async Task<IDisposable> AcquireAsync(int msTimeout, CancellationToken token = default)
|
|
{
|
|
await sem.WaitAsync(msTimeout, token);
|
|
return new Lock(this);
|
|
}
|
|
|
|
public async Task<IDisposable> AcquireAsync(CancellationToken token = default)
|
|
{
|
|
await sem.WaitAsync(token);
|
|
return new Lock(this);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
sem.Dispose();
|
|
}
|
|
|
|
public static AsyncMutex Semaphore(int count, int max)
|
|
{
|
|
SemaphoreSlim sem = new SemaphoreSlim(count, max);
|
|
return new AsyncMutex(sem);
|
|
}
|
|
public static AsyncMutex Semaphore(int count)
|
|
=> Semaphore(count, count);
|
|
}
|
|
|
|
public static class ExAsyncTools
|
|
{
|
|
#region Then()
|
|
public static async Task Then(this Task t, Action lam)
|
|
{
|
|
await t;
|
|
lam();
|
|
}
|
|
public static Task Then(this Task t, Action lam, CancellationToken token)
|
|
=> Then(t, (_) => lam(), token);
|
|
public static async Task Then(this Task t, Action<CancellationToken> lam, CancellationToken token)
|
|
{
|
|
TaskCompletionSource<bool> fail = new TaskCompletionSource<bool>();
|
|
|
|
using (token.Register(() => fail.TrySetResult(false)))
|
|
{
|
|
if ((await Task.WhenAny(t, fail.Task)) == t)
|
|
{
|
|
lam(token);
|
|
}
|
|
token.ThrowIfCancellationRequested();
|
|
}
|
|
}
|
|
public static async Task Then(this Task t, Func<Task> lam)
|
|
{
|
|
await t;
|
|
await lam();
|
|
}
|
|
public static Task Then(this Task t, Func<Task> lam, CancellationToken token)
|
|
=> Then(t, (_) => lam(), token);
|
|
public static async Task Then(this Task t, Func<CancellationToken, Task> lam, CancellationToken token)
|
|
{
|
|
TaskCompletionSource<bool> fail = new TaskCompletionSource<bool>();
|
|
|
|
using (token.Register(() => fail.TrySetResult(false)))
|
|
{
|
|
if ((await Task.WhenAny(t, fail.Task)) == t)
|
|
{
|
|
await lam(token);
|
|
}
|
|
token.ThrowIfCancellationRequested();
|
|
}
|
|
}
|
|
public static async Task<T> Then<T>(this Task<T> t, Action<T> lam)
|
|
{
|
|
T res = await t;
|
|
lam(res);
|
|
return res;
|
|
}
|
|
public static Task<T> Then<T>(this Task<T> t, Action<T> lam, CancellationToken token)
|
|
=> Then(t, (v, _) => lam(v), token);
|
|
public static async Task<T> Then<T>(this Task<T> t, Action<T, CancellationToken> lam, CancellationToken token)
|
|
{
|
|
TaskCompletionSource<bool> fail = new TaskCompletionSource<bool>();
|
|
|
|
using (token.Register(() => fail.TrySetResult(false)))
|
|
{
|
|
if ((await Task.WhenAny(t, fail.Task)) == t)
|
|
{
|
|
var res = t.Result;
|
|
lam(res, token);
|
|
return res;
|
|
}
|
|
throw new OperationCanceledException();
|
|
}
|
|
}
|
|
public static async Task<T> Then<T>(this Task<T> t, Func<T, T> lam)
|
|
{
|
|
T res = await t;
|
|
return lam(res);
|
|
}
|
|
public static Task<T> Then<T>(this Task<T> t, Func<T, T> lam, CancellationToken token)
|
|
=> Then(t, (v, _) => lam(v), token);
|
|
public static async Task<T> Then<T>(this Task<T> t, Func<T, CancellationToken, T> lam, CancellationToken token)
|
|
{
|
|
TaskCompletionSource<bool> fail = new TaskCompletionSource<bool>();
|
|
|
|
using (token.Register(() => fail.TrySetResult(false)))
|
|
{
|
|
if ((await Task.WhenAny(t, fail.Task)) == t)
|
|
{
|
|
var res = t.Result;
|
|
res = lam(res, token);
|
|
return res;
|
|
}
|
|
throw new OperationCanceledException();
|
|
}
|
|
}
|
|
public static async Task<T> Then<T>(this Task<T> t, Func<T, Task<T>> lam)
|
|
{
|
|
return await lam(await t);
|
|
}
|
|
public static Task<T> Then<T>(this Task<T> t, Func<T, Task<T>> lam, CancellationToken token)
|
|
=> Then(t, (x, _) => lam(x), token);
|
|
public static async Task<T> Then<T>(this Task<T> t, Func<T, CancellationToken, Task<T>> lam, CancellationToken token)
|
|
{
|
|
TaskCompletionSource<bool> fail = new TaskCompletionSource<bool>();
|
|
|
|
using (token.Register(() => fail.TrySetResult(false)))
|
|
{
|
|
if ((await Task.WhenAny(t, fail.Task)) == t)
|
|
{
|
|
var res = t.Result;
|
|
res = await lam(res, token);
|
|
return res;
|
|
}
|
|
throw new OperationCanceledException();
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public static CancellationTokenSource Link(this in CancellationToken token, params CancellationToken[] others)
|
|
{
|
|
return CancellationTokenSource.CreateLinkedTokenSource(new CancellationToken[] { token }.Concat(others).ToArray());
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|