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