diff --git a/BytecodeApi/IterationGuard.cs b/BytecodeApi/IterationGuard.cs new file mode 100644 index 0000000..5b99e31 --- /dev/null +++ b/BytecodeApi/IterationGuard.cs @@ -0,0 +1,71 @@ +using System.Diagnostics; + +namespace BytecodeApi; + +/// +/// Class that can be used to guard loops by specifying a maximum number of iterations and/or a timeout. +/// If any of these is exceeded, an infinite loop is assumed and an exception is thrown. +/// +public sealed class IterationGuard +{ + private readonly Stopwatch Stopwatch; + /// + /// Gets the maximum number of iterations, or , if no maximum number of iterations is specified. + /// + public int? MaxIterations { get; } + /// + /// Gets the timeout for the operation, or , if no timeout is specified. + /// + public TimeSpan? Timeout { get; } + /// + /// Gets the current number of iterations that have been executed. + /// This property is incremented by calling the method. + /// + public int Iterations { get; private set; } + /// + /// Gets the time elapsed since the creation of this instance. + /// + public TimeSpan Elapsed => Stopwatch.Elapsed; + + /// + /// Initializes a new instance of the class. + /// + /// The maximum number of iterations, or , to specify no maximum number of iterations. + /// The timeout for the operation, or , to specify no timeout. + public IterationGuard(int? maxIterations = null, TimeSpan? timeout = null) + { + if (maxIterations == null && timeout == null) throw Throw.Argument(nameof(maxIterations), $"At least one of {nameof(maxIterations)} or {nameof(timeout)} must be specified."); + if (maxIterations != null) Check.ArgumentOutOfRangeEx.Greater0(maxIterations.Value); + if (timeout != null) Check.ArgumentOutOfRangeEx.Greater0(timeout.Value); + + MaxIterations = maxIterations; + Timeout = timeout; + Stopwatch = Stopwatch.StartNew(); + } + + /// + /// Advances the iteration guard by one step and checks for iteration or timeout limits. + /// Call this method at the start of each iteration to enforce iteration and timeout constraints. + /// If either limit is exceeded, an infinite loop is assumed and an exception is thrown. + /// + public void Next() + { + if (Iterations++ > MaxIterations) + { + throw new IterationGuardException(IterationGuardErrorReason.MaxIterationsExceeded, Iterations, Elapsed, $"Maximum number of {MaxIterations} iterations exceeded."); + } + + if (Elapsed > Timeout) + { + throw new IterationGuardException(IterationGuardErrorReason.TimeoutExceeded, Iterations, Elapsed, $"Timeout of {Timeout} exceeded."); + } + } + /// + /// Resets the state of this instance. + /// + public void Reset() + { + Iterations = 0; + Stopwatch.Restart(); + } +} \ No newline at end of file diff --git a/BytecodeApi/IterationGuardErrorReason.cs b/BytecodeApi/IterationGuardErrorReason.cs new file mode 100644 index 0000000..857d37b --- /dev/null +++ b/BytecodeApi/IterationGuardErrorReason.cs @@ -0,0 +1,16 @@ +namespace BytecodeApi; + +/// +/// Specifies the reason why an iteration guard operation failed. +/// +public enum IterationGuardErrorReason +{ + /// + /// The maximum number of iterations was exceeded. + /// + MaxIterationsExceeded, + /// + /// The timeout was exceeded. + /// + TimeoutExceeded +} \ No newline at end of file diff --git a/BytecodeApi/IterationGuardException.cs b/BytecodeApi/IterationGuardException.cs new file mode 100644 index 0000000..4241d66 --- /dev/null +++ b/BytecodeApi/IterationGuardException.cs @@ -0,0 +1,34 @@ +namespace BytecodeApi; + +/// +/// The exception that is thrown when a loop guarded by an instance is assumed to have become an infinite loop by exceeding the specified maximum number of iterations or a timeout. +/// +public sealed class IterationGuardException : InvalidOperationException +{ + /// + /// Gets the reason for the iteration guard error. + /// + public IterationGuardErrorReason Reason { get; } + /// + /// Gets the number of iterations that have been executed. + /// + public int Iterations { get; } + /// + /// Gets the time elapsed since the creation of this instance, before the exception was thrown. + /// + public TimeSpan Elapsed { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The reason for the iteration guard error. + /// The number of iterations that have been executed. + /// The time elapsed since the creation of this instance, before the exception was thrown. + /// The message that describes the error. + public IterationGuardException(IterationGuardErrorReason reason, int iterations, TimeSpan elapsed, string message) : base(message) + { + Reason = reason; + Iterations = iterations; + Elapsed = elapsed; + } +} \ No newline at end of file diff --git a/Playground.Wpf.Cui/Properties/PublishProfiles/FolderProfile.pubxml b/Playground.Wpf.Cui/Properties/PublishProfiles/FolderProfile.pubxml new file mode 100644 index 0000000..ae7e5de --- /dev/null +++ b/Playground.Wpf.Cui/Properties/PublishProfiles/FolderProfile.pubxml @@ -0,0 +1,15 @@ + + + + Release + Any CPU + ..\$Build\BytecodeApi.Wpf.Cui Example + FileSystem + <_TargetId>Folder + win-x64 + false + false + false + false + + \ No newline at end of file