|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Runtime.InteropServices; |
|
using System.Threading; |
|
using System.Threading.Tasks; |
|
using static System.Threading.Tasks.TaskContinuationOptions; |
|
|
|
static class Program |
|
{ |
|
static async Task Main() |
|
{ |
|
int[] timeouts = [.. Enumerable.Range(1, 9).Select(static _ => _ * 1)]; |
|
var disposable = await DisposableService.ProbeAsync(timeouts); |
|
|
|
Console.WriteLine($"{disposable?.ToString() ?? "?"} : Result\n"); |
|
Console.ReadKey(); |
|
} |
|
} |
|
|
|
sealed class Disposable(int timeout) : IDisposable |
|
{ |
|
public override string ToString() |
|
{ |
|
return $"{timeout}"; |
|
} |
|
|
|
public void Dispose() |
|
{ |
|
Console.WriteLine($"{timeout} : Disposed\n"); |
|
} |
|
|
|
internal static async Task<Disposable> GetAsync(int timeout, CancellationToken token) |
|
{ |
|
await Task.Delay(timeout, token); |
|
|
|
if (timeout == 1) |
|
throw new ExternalException(); |
|
|
|
return new Disposable(timeout); |
|
} |
|
} |
|
|
|
static class DisposableService |
|
{ |
|
internal static async Task<Disposable?> ProbeAsync(IReadOnlyList<int> timeouts) |
|
{ |
|
using CancellationTokenSource cts = new(); |
|
var tasks = Probe(timeouts, cts.Token); |
|
|
|
try |
|
{ |
|
await foreach (var task in Task.WhenEach(tasks)) |
|
{ |
|
var value = await task; |
|
if (value is null) continue; |
|
|
|
cts.Cancel(); |
|
tasks.Remove(task); |
|
|
|
return value; |
|
} |
|
return null; |
|
} |
|
finally |
|
{ |
|
foreach (var task in tasks) _ = task.ContinueWith(static async task => |
|
{ |
|
var value = await task; |
|
|
|
Console.WriteLine($"{value?.ToString() ?? "?"} : {(value is null ? "Null" : "Object")}\n"); |
|
|
|
value?.Dispose(); |
|
}, RunContinuationsAsynchronously); |
|
} |
|
} |
|
|
|
static List<Task<Disposable?>> Probe(IReadOnlyList<int> timeouts, CancellationToken token) |
|
{ |
|
List<Task<Disposable?>> tasks = new(timeouts.Count); |
|
|
|
foreach (var timeout in timeouts) |
|
tasks.Add(ProbeAsync(timeout, token)); |
|
|
|
return tasks; |
|
} |
|
|
|
static async Task<Disposable?> ProbeAsync(int timeout, CancellationToken token) |
|
{ |
|
Console.WriteLine($"{timeout} : Start\n"); |
|
|
|
try |
|
{ |
|
var disposable = await Disposable.GetAsync(timeout, token); |
|
|
|
Console.WriteLine($"{timeout} : Created\n"); |
|
|
|
return disposable; |
|
} |
|
catch |
|
{ |
|
return null; |
|
} |
|
finally |
|
{ |
|
Console.WriteLine($"{timeout} : {(token.IsCancellationRequested ? "Cancelled" : "Running")}\n"); |
|
} |
|
} |
|
} |