//----------------------------------------------------------------------- // ----------------------------------------------------------------------- // file="TimeSpanAwaitingToken.cs" // Project: StgSharp // AuthorGroup: Nitload Space // Copyright (c) Nitload Space. All rights reserved. // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the “Software”), to deal in the Software without restriction, // including without limitation the rights to use, copy, modify, merge, // publish, distribute, sublicense, and/or sell copies of the Software, // and to permit persons to whom the Software is furnished to do so, // subject to the following conditions: // // The above copyright notice and // this permission notice shall be included in all copies // or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED “AS IS”, // WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH // THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- using StgSharp.HighPerformance.Memory; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; namespace StgSharp.Timing { public sealed class TimeSpanAwaitingToken : IDisposable { private static readonly ConcurrentStackBuffer s_unusedID = new(1); private static int s_maxID; private EventHandler? _refreshHandler,_missHandler; private int _size, _id; private readonly object _participantObject = new(); private SemaphoreSlim _awaitingSemaphore; private TimeSpanProvider _provider; internal TimeSpanAwaitingToken(TimeSpanProvider provider) { if (!s_unusedID.TryPop(out int id)) { id = Interlocked.Increment(ref s_maxID); } TokenID = id; _provider = provider; _size = 1; _awaitingSemaphore = new SemaphoreSlim(0, 1); } internal TimeSpanAwaitingToken(TimeSpanProvider provider, int count) { if (!s_unusedID.TryPop(out int id)) { id = Interlocked.Increment(ref s_maxID); } _provider = provider; _size = count; _awaitingSemaphore = new SemaphoreSlim(0, _size); } public event EventHandler MissTimeSpanRefreshed { add => _missHandler += value; remove => _missHandler -= value; } public event EventHandler TimeSpanRefreshed { add => _refreshHandler += value; remove => _refreshHandler -= value; } public int TokenID { get => _id; private set => _id = value; } internal SemaphoreSlim AwaitingSemaphoreSlim { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _awaitingSemaphore; } public void Dispose() { _provider.QuitSpanAwaiting(this); s_unusedID.Push(TokenID); _awaitingSemaphore.Dispose(); GC.SuppressFinalize(this); } public override int GetHashCode() { return _id; } public void Participant() { lock (_participantObject) { _size++; SemaphoreSlim newSemaphore = new(_awaitingSemaphore.CurrentCount, _size); Interlocked.Exchange(ref _awaitingSemaphore, newSemaphore).Dispose(); } } public void WaitNextSpan() { _awaitingSemaphore.Wait(); } internal void MissRefresh() { _missHandler?.Invoke(this, EventArgs.Empty); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void Refresh() { _refreshHandler?.Invoke(this, EventArgs.Empty); } ~TimeSpanAwaitingToken() { Dispose(); } } }