diff --git a/CSharp/FrameRateTask/Exceptions.cs b/CSharp/FrameRateTask/Exceptions.cs
index 306d7ad..350719b 100644
--- a/CSharp/FrameRateTask/Exceptions.cs
+++ b/CSharp/FrameRateTask/Exceptions.cs
@@ -14,40 +14,40 @@
namespace Timothy.FrameRateTask
{
- ///
- /// This exception will be thrown when the user gets the return value without the task finished.
- ///
- public class TaskNotFinishedException : Exception
- {
- ///
- public override string Message => "The task has not finished!";
- }
-
- ///
- /// This exception will be thrown when the time interval specified is invalid.
- ///
- public class IllegalTimeIntervalException : Exception
- {
- ///
- public override string Message => "The time interval should be positive and no more than 1000ms!";
- }
-
- ///
- /// This exception will be thrown when time exceeds but time exceeding is not allowed.
- ///
- public class TimeExceedException : Exception
- {
- ///
- public override string Message => "The loop runs too slow that it cannot finish the task in the given time!";
- }
-
- ///
- /// This exception will be thrown when the user trys to start a task which has started.
- ///
- public class TaskStartedMoreThanOnceException : Exception
- {
- ///
- public override string Message => "The task has started more than once!";
- }
+ ///
+ /// The exception that is thrown when the user gets the return value while the task has not yet finished.
+ ///
+ public class TaskNotFinishedException : InvalidOperationException
+ {
+ ///
+ public override string Message => "The task has not yet finished!";
+ }
+
+ ///
+ /// The exception that is thrown when the specified time interval is invalid.
+ ///
+ public class IllegalTimeIntervalException : ArgumentOutOfRangeException
+ {
+ ///
+ public override string Message => "The time interval should be a positive number and not exceed 1000ms!";
+ }
+
+ ///
+ /// The exception that is thrown when the task times out.
+ ///
+ public class TimeExceedException : TimeoutException
+ {
+ ///
+ public override string Message => "The loop runs so slowly that it cannot complete the task in the given time!";
+ }
+
+ ///
+ /// The exception that is thrown when the user tries to start a task that has already been started.
+ ///
+ public class TaskStartedMoreThanOnceException : InvalidOperationException
+ {
+ ///
+ public override string Message => "The task has been started more than once!";
+ }
}
diff --git a/CSharp/FrameRateTask/FrameRateTask.cs b/CSharp/FrameRateTask/FrameRateTask.cs
index 02db2e9..12f8386 100644
--- a/CSharp/FrameRateTask/FrameRateTask.cs
+++ b/CSharp/FrameRateTask/FrameRateTask.cs
@@ -16,239 +16,239 @@
namespace Timothy.FrameRateTask
{
- ///
- /// The class intends to execute a task which needs to be executed repeatedly every less than one second accurately.
- ///
- /// The type of the return value of the task.
- public class FrameRateTaskExecutor
- {
- ///
- /// The current actual frame rate.
- ///
- public uint FrameRate
- {
- get => (uint)Interlocked.CompareExchange(ref frameRate, 0, 0);
- private set => Interlocked.Exchange(ref frameRate, value);
- }
- private long frameRate;
-
- ///
- /// Gets a value indicating whether or not the task has finished.
- ///
- ///
- /// true if the task has finished; otherwise, false.
- ///
- public bool Finished
- {
- get => Interlocked.CompareExchange(ref finished, 0, 0) != 0;
- set => Interlocked.Exchange(ref finished, value ? 1 : 0);
- }
- private int finished = 0;
-
- ///
- /// Gets a value indicating whether or not the task has started.
- ///
- ///
- /// true if the task has started; otherwise, false.
- ///
- public bool HasExecuted { get => Interlocked.CompareExchange(ref hasExecuted, 0, 0) != 0; }
- private int hasExecuted = 0;
- private bool TrySetExecute()
- {
- if (Interlocked.Exchange(ref hasExecuted, 1) != 0)
- {
- return false;
- }
- return true;
- }
-
- private TResult result;
- ///
- /// Get the return value of the task.
- ///
- ///
- /// The task hasn't finished.
- ///
- public TResult Result
- {
- get
- {
- if (!Finished) throw new TaskNotFinishedException();
- return result;
- }
- private set => result = value;
- }
-
- ///
- /// Gets or sets whether it allows time exceeding.
- ///
- ///
- /// If it is set false, the task will throw Timothy.FrameRateTask.TimeExceedException when the task cannot finish in the given time.
- /// The default value is true.
- ///
- public bool AllowTimeExceed
- {
- get;
+ ///
+ /// This class intends to perform a task that needs to be executed repeatedly at exact intervals.
+ ///
+ /// The type of the return value of the task.
+ public class FrameRateTaskExecutor
+ {
+ ///
+ /// Gets the current actual frame rate.
+ ///
+ public uint FrameRate
+ {
+ get => (uint)Interlocked.CompareExchange(ref frameRate, 0, 0);
+ private set => Interlocked.Exchange(ref frameRate, value);
+ }
+ private long frameRate;
+
+ ///
+ /// Gets whether the task has finished.
+ ///
+ ///
+ /// true if the task has finished; otherwise, false.
+ ///
+ public bool Finished
+ {
+ get => Interlocked.CompareExchange(ref finished, 0, 0) != 0;
+ set => Interlocked.Exchange(ref finished, value ? 1 : 0);
+ }
+ private int finished = 0;
+
+ ///
+ /// Gets whether the task has started
+ ///
+ ///
+ /// true if the task has started; otherwise, false.
+ ///
+ public bool HasExecuted { get => Interlocked.CompareExchange(ref hasExecuted, 0, 0) != 0; }
+ private int hasExecuted = 0;
+ private bool TrySetExecute()
+ {
+ if (Interlocked.Exchange(ref hasExecuted, 1) != 0)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private TResult result;
+ ///
+ /// Gets the return value of the task.
+ ///
+ ///
+ /// This task has not yet finished.
+ ///
+ public TResult Result
+ {
+ get
+ {
+ if (!Finished) throw new TaskNotFinishedException();
+ return result;
+ }
+ private set => result = value;
+ }
+
+ ///
+ /// Gets or sets whether to allow timeout.
+ ///
+ ///
+ /// If it is set to false and the task fails to complete in the given time, the task will throw Timothy.FrameRateTask.TimeExceedException.
+ /// The default value is true.
+ ///
+ public bool AllowTimeExceed
+ {
+ get;
#if NET5_0_OR_GREATER
init;
#else
- set;
+ set;
#endif
- } = true;
-
- ///
- /// It will be called once time exceeds.
- ///
- ///
- /// parameter bool: If it is called because of the number of time exceeding is greater than MaxTolerantTimeExceedCount, the argument is true; if it is called because of exceeding once, the argument is false.
- ///
- public Action TimeExceedAction
- {
- get;
+ } = true;
+
+ ///
+ /// Sets the method to be called when the task execution times out.
+ ///
+ ///
+ /// Parameter bool: true if the timeout count is greater than MaxTolerantTimeExceedCount when called; otherwise, false.
+ ///
+ public Action TimeExceedAction
+ {
+ private get;
#if NET5_0_OR_GREATER
init;
#else
- set;
+ set;
#endif
- } = callByExceed => { };
-
- ///
- /// Gets or sets the maximum count of time exceeding continuously.
- ///
- ///
- /// The value is 5 for default.
- ///
- public ulong MaxTolerantTimeExceedCount
- {
- get;
+ } = callByExceed => { };
+
+ ///
+ /// Gets or sets the maximum number of consecutive timeouts.
+ ///
+ ///
+ /// The default value is 5.
+ ///
+ public ulong MaxTolerantTimeExceedCount
+ {
+ get;
#if NET5_0_OR_GREATER
init;
#else
- set;
+ set;
#endif
- } = 5;
-
- ///
- /// Start this task synchronously.
- ///
- ///
- /// the task has started.
- ///
- public void Start()
- {
- if (!TryStart()) throw new TaskStartedMoreThanOnceException();
- }
- ///
- /// Try to start this task synchronously.
- ///
- ///
- /// true if the task starts successfully; false if the task has started.
- ///
- public bool TryStart()
- {
- if (!TrySetExecute()) return false;
- loopFunc();
- return true;
- }
-
- private Action loopFunc;
-
- ///
- /// Constructor
- ///
- /// If you want to continue to loop, return true; otherwise, return false.
- /// If you want to break out, return false; otherwise, return true.
- /// The time interval between two execution.
- /// Used to set the return value. It will be called after the loop.
- /// The maximum time for the loop to run.
- public FrameRateTaskExecutor
- (
- Func loopCondition,
- Func loopToDo,
- long timeInterval,
- Func finallyReturn,
- long maxTotalDuration = long.MaxValue
- )
- {
-
- if (timeInterval <= 0L && timeInterval > 1000L)
- {
- throw new IllegalTimeIntervalException();
- }
- FrameRate = (uint)(1000L / timeInterval);
-
- loopFunc = () =>
- {
- ulong timeExceedCount = 0UL;
- long lastLoopEndingTickCount, beginTickCount;
-
- var nextTime = (lastLoopEndingTickCount = beginTickCount = Environment.TickCount64) + timeInterval;
- var endTime = beginTickCount < long.MaxValue - maxTotalDuration ? beginTickCount + maxTotalDuration : long.MaxValue;
-
- uint loopCnt = 0;
- var nextCntTime = beginTickCount + 1000L;
-
- while (loopCondition() && nextTime <= endTime)
- {
- if (!loopToDo()) break;
-
- var nowTime = Environment.TickCount64;
- if (nextTime >= nowTime)
- {
- timeExceedCount = 0UL;
- Thread.Sleep((int)(nextTime - nowTime));
- }
- else
- {
- ++timeExceedCount;
- if (timeExceedCount > MaxTolerantTimeExceedCount)
- {
- if (AllowTimeExceed)
- {
- TimeExceedAction(true);
- timeExceedCount = 0UL;
- nextTime = Environment.TickCount64;
- }
- else
- {
- throw new TimeExceedException();
- }
- }
- else if (AllowTimeExceed) TimeExceedAction(false);
- }
-
- lastLoopEndingTickCount = nextTime;
- nextTime += timeInterval;
- ++loopCnt;
- if (Environment.TickCount64 >= nextCntTime)
- {
- nextCntTime = Environment.TickCount64 + 1000L;
- FrameRate = loopCnt;
- loopCnt = 0;
- }
- }
-
- result = finallyReturn();
- Interlocked.MemoryBarrierProcessWide();
- Finished = true;
- };
- }
-
- ///
- /// Constructor
- ///
- /// If you want to continue to loop, return true; otherwise, return false.
- /// Loop to do.
- /// The time interval between two execution.
- /// Used to set the return value. It will be called after the loop.
- /// The maximum time for the loop to run.
- public FrameRateTaskExecutor
- (
- Func loopCondition,
- Action loopToDo,
- long timeInterval,
- Func finallyReturn,
- long maxTotalDuration = long.MaxValue
- ) : this(loopCondition, () => { loopToDo(); return true; }, timeInterval, finallyReturn, maxTotalDuration) { }
- }
+ } = 5;
+
+ ///
+ /// Starts this task synchronously.
+ ///
+ ///
+ /// The task has already started.
+ ///
+ public void Start()
+ {
+ if (!TryStart()) throw new TaskStartedMoreThanOnceException();
+ }
+ ///
+ /// Tries to start this task synchronously.
+ ///
+ ///
+ /// true if the task is started successfully; false if it has been started.
+ ///
+ public bool TryStart()
+ {
+ if (!TrySetExecute()) return false;
+ loopFunc();
+ return true;
+ }
+
+ private Action loopFunc;
+
+ ///
+ /// Constructor
+ ///
+ /// The judgment condition of the loop. Returns true if you want to continue the loop; otherwise, returns false.
+ /// The loop body. Returns false if you want to jump out of the loop; otherwise, returns true.
+ /// The time interval between two executions.
+ /// The method used to set the return value, which will be called after the loop.
+ /// The maximum time for which the loop will run.
+ public FrameRateTaskExecutor
+ (
+ Func loopCondition,
+ Func loopToDo,
+ long timeInterval,
+ Func finallyReturn,
+ long maxTotalDuration = long.MaxValue
+ )
+ {
+
+ if (timeInterval <= 0L && timeInterval > 1000L)
+ {
+ throw new IllegalTimeIntervalException();
+ }
+ FrameRate = (uint)(1000L / timeInterval);
+
+ loopFunc = () =>
+ {
+ ulong timeExceedCount = 0UL;
+ long lastLoopEndingTickCount, beginTickCount;
+
+ var nextTime = (lastLoopEndingTickCount = beginTickCount = Environment.TickCount64) + timeInterval;
+ var endTime = beginTickCount < long.MaxValue - maxTotalDuration ? beginTickCount + maxTotalDuration : long.MaxValue;
+
+ uint loopCnt = 0;
+ var nextCntTime = beginTickCount + 1000L;
+
+ while (loopCondition() && nextTime <= endTime)
+ {
+ if (!loopToDo()) break;
+
+ var nowTime = Environment.TickCount64;
+ if (nextTime >= nowTime)
+ {
+ timeExceedCount = 0UL;
+ Thread.Sleep((int)(nextTime - nowTime));
+ }
+ else
+ {
+ ++timeExceedCount;
+ if (timeExceedCount > MaxTolerantTimeExceedCount)
+ {
+ if (AllowTimeExceed)
+ {
+ TimeExceedAction(true);
+ timeExceedCount = 0UL;
+ nextTime = Environment.TickCount64;
+ }
+ else
+ {
+ throw new TimeExceedException();
+ }
+ }
+ else if (AllowTimeExceed) TimeExceedAction(false);
+ }
+
+ lastLoopEndingTickCount = nextTime;
+ nextTime += timeInterval;
+ ++loopCnt;
+ if (Environment.TickCount64 >= nextCntTime)
+ {
+ nextCntTime = Environment.TickCount64 + 1000L;
+ FrameRate = loopCnt;
+ loopCnt = 0;
+ }
+ }
+
+ result = finallyReturn();
+ Interlocked.MemoryBarrierProcessWide();
+ Finished = true;
+ };
+ }
+
+ ///
+ /// Constructor
+ ///
+ /// The judgment condition of the loop. Returns true if you want to continue the loop; otherwise, returns false.
+ /// The loop body.
+ /// The time interval between two executions.
+ /// The method used to set the return value, which will be called after the loop.
+ /// The maximum time for which the loop will run.
+ public FrameRateTaskExecutor
+ (
+ Func loopCondition,
+ Action loopToDo,
+ long timeInterval,
+ Func finallyReturn,
+ long maxTotalDuration = long.MaxValue
+ ) : this(loopCondition, () => { loopToDo(); return true; }, timeInterval, finallyReturn, maxTotalDuration) { }
+ }
}
diff --git a/README.md b/README.md
index 66cd6fa..e4720be 100644
--- a/README.md
+++ b/README.md
@@ -102,7 +102,7 @@ Please read [CONTRIBUTING](./CONTRIBUTING.md) carefully before contributing to t
Whether the engine allows time exceeding, `true` for default. See more details under `MaxTolerantTimeExceedCount`.
-+ `public Action TimeExceedAction { get; init; }`
++ `public Action TimeExceedAction { init; }`
It will be called when time exceeds. See more details under `MaxTolerantTimeExceedCount`.