Skip to content

Commit 4223d10

Browse files
authored
P/Invoke TickCount64 for .NET Standard on Windows (#578)
* use pinvoke * fix tests * add warning ---------
1 parent a825808 commit 4223d10

File tree

3 files changed

+66
-18
lines changed

3 files changed

+66
-18
lines changed

BitFaster.Caching.UnitTests/DurationTests.cs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,16 @@ public void SinceEpoch()
3434
Duration.SinceEpoch().raw.Should().BeCloseTo(Environment.TickCount64, 15);
3535
}
3636
#else
37-
// eps is 1/200 of a second
38-
ulong eps = (ulong)(Stopwatch.Frequency / 200);
39-
Duration.SinceEpoch().raw.Should().BeCloseTo(Stopwatch.GetTimestamp(), eps);
37+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
38+
{
39+
Duration.SinceEpoch().raw.Should().BeCloseTo(Duration.GetTickCount64(), 15);
40+
}
41+
else
42+
{
43+
// eps is 1/200 of a second
44+
ulong eps = (ulong)(Stopwatch.Frequency / 200);
45+
Duration.SinceEpoch().raw.Should().BeCloseTo(Stopwatch.GetTimestamp(), eps);
46+
}
4047
#endif
4148
}
4249

@@ -53,8 +60,15 @@ public void ToTimeSpan()
5360
new Duration(1000).ToTimeSpan().Should().BeCloseTo(TimeSpan.FromMilliseconds(1000), TimeSpan.FromMilliseconds(10));
5461
}
5562
#else
56-
// for Stopwatch.GetTimestamp() this is number of ticks
57-
new Duration(1 * Stopwatch.Frequency).ToTimeSpan().Should().BeCloseTo(TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(10));
63+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
64+
{
65+
new Duration(1000).ToTimeSpan().Should().BeCloseTo(TimeSpan.FromMilliseconds(1000), TimeSpan.FromMilliseconds(10));
66+
}
67+
else
68+
{
69+
// for Stopwatch.GetTimestamp() this is number of ticks
70+
new Duration(1 * Stopwatch.Frequency).ToTimeSpan().Should().BeCloseTo(TimeSpan.FromSeconds(1), TimeSpan.FromMilliseconds(10));
71+
}
5872
#endif
5973
}
6074

@@ -73,8 +87,16 @@ public void FromTimeSpan()
7387
.Should().Be((long)TimeSpan.FromSeconds(1).TotalMilliseconds);
7488
}
7589
#else
76-
Duration.FromTimeSpan(TimeSpan.FromSeconds(1)).raw
90+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
91+
{
92+
Duration.FromTimeSpan(TimeSpan.FromSeconds(1)).raw
93+
.Should().Be((long)TimeSpan.FromSeconds(1).TotalMilliseconds);
94+
}
95+
else
96+
{
97+
Duration.FromTimeSpan(TimeSpan.FromSeconds(1)).raw
7798
.Should().Be(Stopwatch.Frequency);
99+
}
78100
#endif
79101
}
80102

BitFaster.Caching.UnitTests/Lru/TlruStopwatchPolicyTests.cs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ public void WhenTtlIsTimeSpanMaxThrow()
3232
[Fact]
3333
public void WhenTtlIsCloseToMaxAllow()
3434
{
35-
double maxTicks = long.MaxValue / 100.0d;
36-
var ttl = TimeSpan.FromTicks((long)maxTicks) - TimeSpan.FromTicks(10);
35+
var ttl = Duration.MaxRepresentable;
3736

38-
new TLruLongTicksPolicy<int, int>(ttl).TimeToLive.Should().BeCloseTo(ttl, TimeSpan.FromTicks(20));
37+
new TLruLongTicksPolicy<int, int>(ttl).TimeToLive.Should().BeCloseTo(ttl, TimeSpan.FromSeconds(1));
3938
}
4039

4140
[Fact]
@@ -58,9 +57,7 @@ public void CreateItemInitializesTimestampToNow()
5857
{
5958
var item = this.policy.CreateItem(1, 2);
6059

61-
// seconds = ticks / Stopwatch.Frequency
62-
ulong epsilon = (ulong)(TimeSpan.FromMilliseconds(20).TotalSeconds * Stopwatch.Frequency);
63-
item.TickCount.Should().BeCloseTo(Stopwatch.GetTimestamp(), epsilon);
60+
item.TickCount.Should().BeCloseTo(Duration.SinceEpoch().raw, DurationTests.epsilon);
6461
}
6562

6663
[Fact]
@@ -91,7 +88,7 @@ public async Task UpdateUpdatesTickCount()
9188
public void WhenItemIsExpiredShouldDiscardIsTrue()
9289
{
9390
var item = this.policy.CreateItem(1, 2);
94-
item.TickCount = Stopwatch.GetTimestamp() - TLruLongTicksPolicy<int, int>.ToTicks(TimeSpan.FromSeconds(11));
91+
item.TickCount = Duration.SinceEpoch().raw - Duration.FromSeconds(11).raw;
9592

9693
this.policy.ShouldDiscard(item).Should().BeTrue();
9794
}
@@ -100,7 +97,7 @@ public void WhenItemIsExpiredShouldDiscardIsTrue()
10097
public void WhenItemIsNotExpiredShouldDiscardIsFalse()
10198
{
10299
var item = this.policy.CreateItem(1, 2);
103-
item.TickCount = Stopwatch.GetTimestamp() - TLruLongTicksPolicy<int, int>.ToTicks(TimeSpan.FromSeconds(9));
100+
item.TickCount = Duration.SinceEpoch().raw - Duration.FromSeconds(9).raw;
104101

105102
this.policy.ShouldDiscard(item).Should().BeFalse();
106103
}
@@ -155,7 +152,7 @@ private LongTickCountLruItem<int, int> CreateItem(bool wasAccessed, bool isExpir
155152

156153
if (isExpired)
157154
{
158-
item.TickCount = Stopwatch.GetTimestamp() - TLruLongTicksPolicy<int, int>.ToTicks(TimeSpan.FromSeconds(11));
155+
item.TickCount = Duration.SinceEpoch().raw - Duration.FromSeconds(11).raw;
159156
}
160157

161158
return item;

BitFaster.Caching/Duration.cs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ public readonly struct Duration
2828

2929
#if NETCOREAPP3_0_OR_GREATER
3030
private static readonly bool IsMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
31+
#else
32+
private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
33+
34+
[DllImport("kernel32")]
35+
internal static extern long GetTickCount64();
3136
#endif
3237

3338
internal Duration(long raw)
@@ -52,7 +57,15 @@ public static Duration SinceEpoch()
5257
return new Duration(Environment.TickCount64);
5358
}
5459
#else
55-
return new Duration(Stopwatch.GetTimestamp());
60+
if (IsWindows)
61+
{
62+
return new Duration(GetTickCount64());
63+
}
64+
else
65+
{
66+
// Warning: not currently covered by unit tests
67+
return new Duration(Stopwatch.GetTimestamp());
68+
}
5669
#endif
5770
}
5871

@@ -73,7 +86,15 @@ public TimeSpan ToTimeSpan()
7386
return TimeSpan.FromMilliseconds(raw);
7487
}
7588
#else
76-
return StopwatchTickConverter.FromTicks(raw);
89+
if (IsWindows)
90+
{
91+
return TimeSpan.FromMilliseconds(raw);
92+
}
93+
else
94+
{
95+
// Warning: not currently covered by unit tests
96+
return StopwatchTickConverter.FromTicks(raw);
97+
}
7798
#endif
7899
}
79100

@@ -95,7 +116,15 @@ public static Duration FromTimeSpan(TimeSpan timeSpan)
95116
return new Duration((long)timeSpan.TotalMilliseconds);
96117
}
97118
#else
98-
return new Duration(StopwatchTickConverter.ToTicks(timeSpan));
119+
if (IsWindows)
120+
{
121+
return new Duration((long)timeSpan.TotalMilliseconds);
122+
}
123+
else
124+
{
125+
// Warning: not currently covered by unit tests
126+
return new Duration(StopwatchTickConverter.ToTicks(timeSpan));
127+
}
99128
#endif
100129
}
101130

0 commit comments

Comments
 (0)