Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions src/SampSharp.OpenMp.Entities/Timers/StopwatchTime.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Diagnostics;

namespace SampSharp.Entities;

internal static class StopwatchTime
{
/// <summary>
/// Converts a TimeSpan to Stopwatch ticks.
///
/// IMPORTANT:
/// - TimeSpan ticks are fixed (1 tick = 100ns, 10,000,000 per second)
/// - Stopwatch ticks depend on hardware (Stopwatch.Frequency)
///
/// This method converts a TimeSpan duration to the equivalent number
/// of Stopwatch ticks so both values can be used in the same time system.
/// </summary>
public static long ToStopwatchTicks(TimeSpan time)
=> (long)(time.TotalSeconds * Stopwatch.Frequency);

/// <summary>
/// Converts Stopwatch ticks to a TimeSpan.
///
/// This is the inverse operation of ToStopwatchTicks.
/// It converts a duration measured in Stopwatch ticks back into
/// a TimeSpan using Stopwatch.Frequency.
/// </summary>
public static TimeSpan ToTimeSpan(long stopwatchTicks)
=> TimeSpan.FromSeconds(stopwatchTicks / (double)Stopwatch.Frequency);
}
9 changes: 8 additions & 1 deletion src/SampSharp.OpenMp.Entities/Timers/TimerReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ internal TimerReference(TimerInfo info, object? target, MethodInfo? method)
/// <summary>
/// Gets the time span until the next tick of this timer.
/// </summary>
public TimeSpan NextTick => new(Info.NextTick - Stopwatch.GetTimestamp());
public TimeSpan NextTick
{
get
{
long remainingStopwatchTicks = Info.NextTick - Stopwatch.GetTimestamp();
return StopwatchTime.ToTimeSpan(remainingStopwatchTicks);
}
}

internal TimerInfo Info { get; set; }

Expand Down
22 changes: 18 additions & 4 deletions src/SampSharp.OpenMp.Entities/Timers/TimerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public void Tick()
{
var timer = _timers[i];

// IMPORTANT:
// Both NextTick and timestamp are in Stopwatch ticks, so the comparison is valid across platforms.
while (timer.NextTick <= timestamp)
{
try
Expand Down Expand Up @@ -97,7 +99,15 @@ public TimerReference Start(Action<IServiceProvider, TimerReference> action, Tim
throw new ArgumentOutOfRangeException(nameof(interval), interval, "The interval should be a nonzero positive value.");
}

var invoker = new TimerInfo(intervalTicks: interval.Ticks, nextTick: Stopwatch.GetTimestamp() + interval.Ticks, invoke: null!, true);
long intervalStopwatchTicks = StopwatchTime.ToStopwatchTicks(interval);
var invoker = new TimerInfo(
intervalTicks: intervalStopwatchTicks,
// IMPORTANT:
// Keeping everything in Stopwatch ticks ensures correct behavior cross-plataform.
nextTick: Stopwatch.GetTimestamp() + intervalStopwatchTicks,
invoke: null!,
isActive: true
);

var reference = new TimerReference(invoker, action.Target, action.Method);

Expand Down Expand Up @@ -157,11 +167,15 @@ private void CreateTimersFromAssemblies()
LogLowInterval(target, method.Name, attribute.IntervalTimeSpan);
}

long intervalStopwatchTicks = StopwatchTime.ToStopwatchTicks(attribute.IntervalTimeSpan);
var timer = new TimerInfo(
intervalTicks: attribute.IntervalTimeSpan.Ticks,
nextTick: tick + attribute.IntervalTimeSpan.Ticks,
intervalTicks: intervalStopwatchTicks,
// IMPORTANT:
// Keeping everything in Stopwatch ticks ensures correct behavior cross-plataform.
nextTick: tick + intervalStopwatchTicks,
invoke: () => compiled(service, null, _serviceProvider, null),
isActive: true);
isActive: true
);

timer.Reference = new TimerReference(timer, service, method);

Expand Down
Loading