DotNext by Roman Sakno

<PackageReference Include="DotNext" Version="2.10.1" />

 Timeout

public struct Timeout
Helps to compute timeout for asynchronous operations.
using DotNext.Diagnostics; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; namespace DotNext.Threading { [StructLayout(LayoutKind.Auto)] public readonly struct Timeout { private readonly Timestamp created; private readonly TimeSpan? timeout; public TimeSpan Value => timeout ?? System.Threading.Timeout.InfiniteTimeSpan; public bool IsInfinite => !timeout.HasValue; public bool IsExpired { get { if (timeout.TryGetValue(out TimeSpan value)) return created.Elapsed > value; return false; } } public TimeSpan? RemainingTime { get { if (timeout.TryGetValue(out TimeSpan value)) { value -= created.Elapsed; if (!(value >= TimeSpan.Zero)) return null; return value; } return System.Threading.Timeout.InfiniteTimeSpan; } } public Timeout(TimeSpan timeout) { created = Timestamp.Current; if (timeout == System.Threading.Timeout.InfiniteTimeSpan) this.timeout = null; else { if (timeout < TimeSpan.Zero) throw new ArgumentOutOfRangeException("timeout"); this.timeout = timeout; } } public void ThrowIfExpired() { if (IsExpired) throw new TimeoutException(); } public void ThrowIfExpired(out TimeSpan remaining) { if (!RemainingTime.TryGetValue(out remaining)) throw new TimeoutException(); } public static bool operator true([In] [IsReadOnly] ref Timeout timeout) { return timeout.IsExpired; } public static bool operator false([In] [IsReadOnly] ref Timeout timeout) { return !timeout.IsExpired; } public static implicit operator TimeSpan([In] [IsReadOnly] ref Timeout timeout) { return timeout.Value; } } }