Skip to content

Commit

Permalink
Merge pull request #59 from Timothy-Liuxf/dev
Browse files Browse the repository at this point in the history
chore: 🔖 upgrade to v1.2.0
  • Loading branch information
Timothy-Liuxf authored Dec 4, 2022
2 parents 97067c5 + 0091809 commit 6b1f70a
Show file tree
Hide file tree
Showing 13 changed files with 65 additions and 77 deletions.
2 changes: 1 addition & 1 deletion CSharp/FrameRateTask/Exceptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public class TaskNotFinishedException : InvalidOperationException
public class IllegalTimeIntervalException : ArgumentOutOfRangeException
{
///
public override string Message => "The time interval should be a positive number and not exceed 1000ms!";
public override string Message => "The time interval should be a positive number!";
}

/// <summary>
Expand Down
18 changes: 10 additions & 8 deletions CSharp/FrameRateTask/FrameRateTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace Timothy.FrameRateTask
public class FrameRateTaskExecutor<TResult>
{
/// <summary>
/// Gets the current actual frame rate.
/// Gets the real-time frame rate.
/// </summary>
public uint FrameRate
{
Expand Down Expand Up @@ -171,7 +171,7 @@ public FrameRateTaskExecutor
)
{

if (timeInterval <= 0L && timeInterval > 1000L)
if (timeInterval <= 0L)
{
throw new IllegalTimeIntervalException();
}
Expand All @@ -181,7 +181,7 @@ public FrameRateTaskExecutor
{
ulong timeExceedCount = 0UL;
long lastLoopEndingTickCount, beginTickCount;
var nextTime = (lastLoopEndingTickCount = beginTickCount = Environment.TickCount64) + timeInterval;
var endTime = beginTickCount < long.MaxValue - maxTotalDuration ? beginTickCount + maxTotalDuration : long.MaxValue;
Expand All @@ -192,11 +192,11 @@ public FrameRateTaskExecutor
{
if (!loopToDo()) break;
var nowTime = Environment.TickCount64;
if (nextTime >= nowTime)
var currentTime = Environment.TickCount64;
if (nextTime >= currentTime)
{
timeExceedCount = 0UL;
Thread.Sleep((int)(nextTime - nowTime));
Thread.Sleep((int)(nextTime - currentTime));
}
else
{
Expand All @@ -220,9 +220,11 @@ public FrameRateTaskExecutor
lastLoopEndingTickCount = nextTime;
nextTime += timeInterval;
++loopCnt;
if (Environment.TickCount64 >= nextCntTime)
currentTime = Environment.TickCount64;
if (currentTime >= nextCntTime)
{
nextCntTime = Environment.TickCount64 + 1000L;
nextCntTime = currentTime + 1000L;
FrameRate = loopCnt;
loopCnt = 0;
}
Expand Down
6 changes: 3 additions & 3 deletions CSharp/FrameRateTask/FrameRateTask.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
<FileVersion>1.1.2.0</FileVersion>
<AssemblyVersion>1.1.2.0</AssemblyVersion>
<VersionPrefix>1.1.2</VersionPrefix>
<FileVersion>1.2.0.0</FileVersion>
<AssemblyVersion>1.2.0.0</AssemblyVersion>
<VersionPrefix>1.2.0</VersionPrefix>
<RepositoryUrl>https://github.com/Timothy-Liuxf/FrameRateTask</RepositoryUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
<Authors>Timothy Liu</Authors>
Expand Down
4 changes: 2 additions & 2 deletions CSharp/Test/TestBasic/TestBasic.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions CSharp/Test/TestConcurrency/TestConcurrency.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Example/CSharp/BreakOut/BreakOut.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FrameRateTask" Version="1.1.2" />
<PackageReference Include="FrameRateTask" Version="1.2.0" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion Example/CSharp/Congested/Congested.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FrameRateTask" Version="1.1.2" />
<PackageReference Include="FrameRateTask" Version="1.2.0" />
</ItemGroup>

</Project>
4 changes: 2 additions & 2 deletions Example/CSharp/CongestionRelief/CongestionRelief.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
Expand All @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FrameRateTask" Version="1.1.2" />
<PackageReference Include="FrameRateTask" Version="1.2.0" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion Example/CSharp/Hello/Hello.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FrameRateTask" Version="1.1.2" />
<PackageReference Include="FrameRateTask" Version="1.2.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FrameRateTask" Version="1.1.2" />
<PackageReference Include="FrameRateTask" Version="1.2.0" />
</ItemGroup>

</Project>
13 changes: 7 additions & 6 deletions FrameRateTask.sln
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.gitignore = .gitignore
LICENSE.txt = LICENSE.txt
README.md = README.md
README.zh-CN.md = README.zh-CN.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FrameRateTask", "CSharp\FrameRateTask\FrameRateTask.csproj", "{07261E94-73FD-43C5-AAAA-8C61E7576E47}"
Expand All @@ -19,13 +20,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Example", "Example", "{3524
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CSharp", "CSharp", "{47DC06C9-8A99-436D-B114-06EBA0FEF260}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hello", "Example\CSharp\Hello\Hello.csproj", "{EB55C6F7-C336-4A59-9588-11F6943E9A62}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hello", "Example\CSharp\Hello\Hello.csproj", "{EB55C6F7-C336-4A59-9588-11F6943E9A62}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BreakOut", "Example\CSharp\BreakOut\BreakOut.csproj", "{5EBEE786-AE2E-4417-83CF-0468BFCBDC76}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BreakOut", "Example\CSharp\BreakOut\BreakOut.csproj", "{5EBEE786-AE2E-4417-83CF-0468BFCBDC76}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Congested", "Example\CSharp\Congested\Congested.csproj", "{CA4633C1-3423-4774-8575-58D1A69B9249}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Congested", "Example\CSharp\Congested\Congested.csproj", "{CA4633C1-3423-4774-8575-58D1A69B9249}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CongestionRelief", "Example\CSharp\CongestionRelief\CongestionRelief.csproj", "{B548ECDD-C64F-44A1-8D3B-8708129D423C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CongestionRelief", "Example\CSharp\CongestionRelief\CongestionRelief.csproj", "{B548ECDD-C64F-44A1-8D3B-8708129D423C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TemporaryCongestion", "Example\CSharp\TemporaryCongestion\TemporaryCongestion.csproj", "{C0F0BB0E-07FE-4D5B-84D6-99B390FEE480}"
EndProject
Expand All @@ -39,9 +40,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{2F814C2B-9C02-4188-AFBB-50572539ABCD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConcurrency", "CSharp\Test\TestConcurrency\TestConcurrency.csproj", "{FE78004B-F053-4C75-8427-8D17115FCA5C}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestConcurrency", "CSharp\Test\TestConcurrency\TestConcurrency.csproj", "{FE78004B-F053-4C75-8427-8D17115FCA5C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestBasic", "CSharp\Test\TestBasic\TestBasic.csproj", "{FF1EBDDC-F5B6-4304-AC05-7F325C21D10D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestBasic", "CSharp\Test\TestBasic\TestBasic.csproj", "{FF1EBDDC-F5B6-4304-AC05-7F325C21D10D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
53 changes: 23 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,18 @@

## Introduction

`FrameRateTask`, a frame rate stabilizer, a task executor which executes tasks at a stable frame rate.
`FrameRateTask`, a frame rate stabilizer, a tool to perform tasks at a stable frame rate.

This project intends to build an engine to support executing tasks which need to be executed repeatedly and frequently in a single thread, and the time interval between two execution needs to be accurately stable. And this engine can also provide the real-time frame rate. Up to now, this time interval should be no more than 1 second. It can be used to control the frame rate of a game, or communication through network, etc.

The source code is in the project `FrameRateTask`, and the example of usage is in the project `Test`.

> This project is initially written for [THUAI4](https://github.com/eesast/THUAI4) to support some functions.
This project aims to build an engine that can execute a task repeatedly and at high frequency in a single thread with a precise and stable time interval between every two executions as required. Also, this engine can provide real-time frame rate. This engine can be used to control game frame rate, network communication frame rate, etc.

## Get This Package

To get this package, please enter the nuget package page: [https://www.nuget.org/packages/FrameRateTask/](https://www.nuget.org/packages/FrameRateTask/)

or use .NET CLI:
or use .NET CLI:

```shell
$ dotnet add package FrameRateTask
dotnet add package FrameRateTask
```

## Author
Expand All @@ -35,80 +31,77 @@ Copyright (C) 2022 Timothy-LiuXuefeng

## LICENSE

[MIT license](./LICENSE.txt)
[MIT license](https://github.com/Timothy-Liuxf/FrameRateTask/blob/master/LICENSE.txt)

## Contributing to this repository

Please read [CONTRIBUTING](./CONTRIBUTING.md) carefully before contributing to this repository.
Please read [CONTRIBUTING](https://github.com/Timothy-Liuxf/FrameRateTask/blob/master/CONTRIBUTING.md) carefully before contributing to this repository.

## Interfaces

### CSharp
### CSharp

#### `class FrameRateTaskExecutor<TResult>`

`TResult`: The return value of the task.

+ Constructor:

+ `FrameRateTaskExecutor(Func<bool> loopCondition, Func<bool> loopToDo, long timeInterval, Func<TResult> finallyReturn, long maxTotalDuration = long.MaxValue)`
+ `FrameRateTaskExecutor(Func<bool> loopCondition, Func<bool> loopToDo, long timeInterval, Func<TResult> finallyReturn, long maxTotalDuration = long.MaxValue)`

If the object is constructed by this constructor, when calling its the `Start` or `TryStart` method, the program behaves as below:
If the object is constructed with this constructor, when the `Start` or `TryStart` method of the object is called, the procedure is equivalent to the following code:

```c#
while (loopCondition && time <= maxTotalDuration)
{
if (!loopToDo()) break;
/* Do something to delay until next frame comes. */
/* Delay until the next frame arrives */
}
return finallyReturn;
```

+ `loopCondition`: The condition to judge if the loop will continue.
+ `loopCondition`: The judgment condition of whether to continue the loop.
+ `loopToDo`: The loop body. If it returns false, jump out of the loop.
+ `timeInterval`: Time interval between two executing in milliseconds.
+ `finallyReturn`: Specify the last thing to do and the return value.
+ `maxTotalDuration`:The maximum time in total for this task in milliseconds, `long.MaxValue` for default.
+ `timeInterval`: The time interval between two executions of the loop body in milliseconds.
+ `finallyReturn`: Specify the last code to be executed and the return value.
+ `maxTotalDuration`:The maximum time in milliseconds for the whole task, `long.MaxValue` by default.

+ `FrameRateTaskExecutor(Func<bool> loopCondition, Action loopToDo, long timeInterval, Func<TResult> finallyReturn, long maxTotalDuration = long.MaxValue)`

The only thing different from the last constructor is that `loopToDo` has no return value so that you cannot jump out of the loop through `loopToDo`.
The only difference with the previous constructor is that `loopToDo` has no return value so that you cannot jump out of the loop through `loopToDo`.

+ `public void Start()`

Start executing the task. If the task has started, it will throw `Timothy.FrameRateTask.TaskStartedMoreThanOnceException`. Otherwise, it will execute the task and return after the task finishes.
Start the task. If the task has already started, a `Timothy.FrameRateTask.TaskStartedMoreThanOnceException` exception will be thrown. Otherwise, it will start the task and return when the task finishes.

+ `public bool TryStart()`

Try to start executing the task. If the task has started, it will return `false`. Otherwise, it will execute the task and return `true` after the task finishes.
Try to start the task. Returns `false` if the task has already started, otherwise it will start the task and return `true` when the task finishes.

+ `public uint FrameRate { get; }`

The real-time frame rate, initialized with the expected frame rate, and will be changed when the execution of the task.
The real-time frame rate of the loop body execution, initialized to the expected frame rate, and will be changed during the execution of the task.

+ `public bool Finished { get; }`

Whether the task has finished.

+ `public bool HasExecuted { get; }`

Whether the task has begun to execute.
Whether the task has been started.

+ `public TResult Result { get; }`

The return value of the task. If the task has not finished, it will throw `Timothy.FrameRateTask.TaskNotFinishedException`.
The return value of the task. A `Timothy.FrameRateTask.TaskNotFinishedException` exception will be thrown if the task has not finished.

+ `public bool AllowTimeExceed { get; init; }`

Whether the engine allows time exceeding, `true` for default. See more details under `MaxTolerantTimeExceedCount`.
Whether to allow the execution timeouts, `true` by default. See the description of `MaxTolerantTimeExceedCount` for more details.

+ `public Action<bool> TimeExceedAction { init; }`

It will be called when time exceeds. See more details under `MaxTolerantTimeExceedCount`.
It will be called after the execution timeouts. See the description of `MaxTolerantTimeExceedCount` for more details.

+ `public ulong MaxTolerantTimeExceedCount { get; init; }`

The maximum number of time exceeding in a series, `5` for default. Once time exceeds, if the number of time exceeding in a series is no more than `MaxTolerantTimeExceedCount`, `TimeExceedAction` will be called with argument `false`。Otherwise, if `AllowTimeExceed` is set `true`, `TimeExceedAction` will be called with argument `true`, otherwise, that is, `AllowTimeExceed` is set `false`, it will throw `Timothy.FrameRateTask.TimeExceedException`.

Once more than `MaxTolerantTimeExceedCount`, it will automatically abandon unfinished loops, and reset the loop counter. The example `TemporaryCongestion` is a sample code of this.

The maximum number of consecutive timeouts allowed, `5` by default. Once a loop has timed out, if the number of consecutive timeouts does not exceed `MaxTolerantTimeExceedCount`, `TimeExceedAction` will be called with argument `false`. Otherwise, if `AllowTimeExceed` is set to `false`, a `Timothy.FrameRateTask.TimeExceedException` exception will be thrown; if `AllowTimeExceed` is set to `true`, `TimeExceedAction` will be called with argument `true`, and the incomplete loop will be discarded and the loop count will be reset (see the example `TemporaryCongestion`)
Loading

0 comments on commit 6b1f70a

Please sign in to comment.