// Exercise 2: timeouts and cancellation (medium) // // Implement: // - WithTimeoutAsync(task, timeout): returns the task's result if it // completes within `timeout`; otherwise throws TimeoutException. // Use Task.WhenAny(task, Task.Delay(timeout)) — and remember that the // winner of WhenAny is a Task you still need to await to get the value // (or to observe its exception). // - DownloadAllAsync(urls, token): "downloads" each url with // FakeDownloadAsync, passing the CancellationToken through. If the token // is cancelled mid-way, let the OperationCanceledException propagate — // do NOT swallow it. // // Run with: dotnet run (inside a console project). using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; class Program { static async Task FakeDownloadAsync(string url, CancellationToken token) { await Task.Delay(100, token); // throws TaskCanceledException when cancelled return $"<{url}>"; } static async Task SlowAsync(int ms, int value) { await Task.Delay(ms); return value; } #pragma warning disable IDE0060, CS1998 static async Task WithTimeoutAsync(Task task, TimeSpan timeout) { // TODO throw new NotImplementedException(); } static async Task> DownloadAllAsync(IEnumerable urls, CancellationToken token) { // TODO: sequentially await FakeDownloadAsync(url, token) for each url return new List(); } #pragma warning restore IDE0060, CS1998 static async Task Main() { int pass = 0, total = 0; total++; try { int v = await WithTimeoutAsync(SlowAsync(50, 7), TimeSpan.FromMilliseconds(500)); if (v == 7) { Console.WriteLine("PASS: fast task returns its value"); pass++; } else Console.WriteLine("FAIL: wrong value " + v); } catch (Exception e) { Console.WriteLine("FAIL: fast task threw " + e.GetType().Name); } total++; try { await WithTimeoutAsync(SlowAsync(1000, 7), TimeSpan.FromMilliseconds(100)); Console.WriteLine("FAIL: slow task did not time out"); } catch (TimeoutException) { Console.WriteLine("PASS: slow task times out"); pass++; } catch (Exception e) { Console.WriteLine("FAIL: expected TimeoutException, got " + e.GetType().Name); } total++; var all = await DownloadAllAsync(new[] { "a", "b" }, CancellationToken.None); if (string.Join("", all) == "") { Console.WriteLine("PASS: downloads all"); pass++; } else Console.WriteLine("FAIL: DownloadAllAsync, got " + string.Join("", all)); total++; using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(150)); try { await DownloadAllAsync(new[] { "a", "b", "c", "d" }, cts.Token); Console.WriteLine("FAIL: cancellation was swallowed"); } catch (OperationCanceledException) { Console.WriteLine("PASS: cancellation propagates"); pass++; } Console.WriteLine($"--- {pass}/{total} checks passed ---"); } }