feat: port session 07 — Protocol Parser, Auth extras (TPM/certidp/certstore), Internal utilities & data structures
Session 07 scope (5 features, 17 tests, ~1165 Go LOC): - Protocol/ParserTypes.cs: ParserState enum (79 states), PublishArgument, ParseContext - Protocol/IProtocolHandler.cs: handler interface decoupling parser from client - Protocol/ProtocolParser.cs: Parse(), ProtoSnippet(), OverMaxControlLineLimit(), ProcessPub/HeaderPub/RoutedMsgArgs/RoutedHeaderMsgArgs, ClonePubArg(), GetHeader() - tests/Protocol/ProtocolParserTests.cs: 17 tests via TestProtocolHandler stub Auth extras from session 06 (committed separately): - Auth/TpmKeyProvider.cs, Auth/CertificateIdentityProvider/, Auth/CertificateStore/ Internal utilities & data structures (session 06 overflow): - Internal/AccessTimeService.cs, ElasticPointer.cs, SystemMemory.cs, ProcessStatsProvider.cs - Internal/DataStructures/GenericSublist.cs, HashWheel.cs - Internal/DataStructures/SubjectTree.cs, SubjectTreeNode.cs, SubjectTreeParts.cs All 461 tests pass (460 unit + 1 integration). DB updated for features 2588-2592 and tests 2598-2614.
This commit is contained in:
100
dotnet/src/ZB.MOM.NatsNet.Server/Internal/AccessTimeService.cs
Normal file
100
dotnet/src/ZB.MOM.NatsNet.Server/Internal/AccessTimeService.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
namespace ZB.MOM.NatsNet.Server.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// Provides an efficiently-cached Unix nanosecond timestamp updated every
|
||||
/// <see cref="TickInterval"/> by a shared background timer.
|
||||
/// Register before use and Unregister when done; the timer shuts down when all
|
||||
/// registrants have unregistered.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Mirrors the Go <c>ats</c> package. Intended for high-frequency cache
|
||||
/// access-time reads that do not need sub-100ms precision.
|
||||
/// </remarks>
|
||||
public static class AccessTimeService
|
||||
{
|
||||
/// <summary>How often the cached time is refreshed.</summary>
|
||||
public static readonly TimeSpan TickInterval = TimeSpan.FromMilliseconds(100);
|
||||
|
||||
private static long _utime;
|
||||
private static long _refs;
|
||||
private static Timer? _timer;
|
||||
private static readonly object _lock = new();
|
||||
|
||||
static AccessTimeService()
|
||||
{
|
||||
// Mirror Go's init(): nothing to pre-allocate in .NET.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a user. Starts the background timer when the first registrant calls this.
|
||||
/// Each call to <see cref="Register"/> must be paired with a call to <see cref="Unregister"/>.
|
||||
/// </summary>
|
||||
public static void Register()
|
||||
{
|
||||
var v = Interlocked.Increment(ref _refs);
|
||||
if (v == 1)
|
||||
{
|
||||
Interlocked.Exchange(ref _utime, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1_000_000L);
|
||||
lock (_lock)
|
||||
{
|
||||
_timer?.Dispose();
|
||||
_timer = new Timer(_ =>
|
||||
{
|
||||
Interlocked.Exchange(ref _utime, DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1_000_000L);
|
||||
}, null, TickInterval, TickInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a user. Stops the background timer when the last registrant calls this.
|
||||
/// </summary>
|
||||
/// <exception cref="InvalidOperationException">Thrown when unregister is called more times than register.</exception>
|
||||
public static void Unregister()
|
||||
{
|
||||
var v = Interlocked.Decrement(ref _refs);
|
||||
if (v == 0)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
else if (v < 0)
|
||||
{
|
||||
Interlocked.Exchange(ref _refs, 0);
|
||||
throw new InvalidOperationException("ats: unbalanced unregister for access time state");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the last cached Unix nanosecond timestamp.
|
||||
/// If no registrant is active, returns a fresh timestamp (avoids returning zero).
|
||||
/// </summary>
|
||||
public static long AccessTime()
|
||||
{
|
||||
var v = Interlocked.Read(ref _utime);
|
||||
if (v == 0)
|
||||
{
|
||||
v = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1_000_000L;
|
||||
Interlocked.CompareExchange(ref _utime, v, 0);
|
||||
v = Interlocked.Read(ref _utime);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all state. For testing only.
|
||||
/// </summary>
|
||||
internal static void Reset()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_timer?.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
Interlocked.Exchange(ref _refs, 0);
|
||||
Interlocked.Exchange(ref _utime, 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user