using System;
using System.Collections.Generic;
using System.Threading;
using Best.HTTP.Shared.Logger;
using Best.HTTP.Shared.PlatformSupport.Threading;
namespace Best.HTTP.Shared.Extensions
{
sealed class RunOnceOnMainThread : IHeartbeat
{
private Action _action;
private int _subscribed;
private LoggingContext _context;
public RunOnceOnMainThread(Action action, LoggingContext context)
{
this._action = action;
this._context = context;
}
public void Subscribe()
{
if (Interlocked.CompareExchange(ref this._subscribed, 1, 0) == 0)
HTTPManager.Heartbeats.Subscribe(this);
}
public void OnHeartbeatUpdate(DateTime now, TimeSpan dif)
{
try
{
this._action?.Invoke();
}
catch (Exception ex)
{
HTTPManager.Logger.Exception(nameof(RunOnceOnMainThread.OnHeartbeatUpdate), $"{nameof(_action)}", ex, this._context);
}
finally
{
HTTPManager.Heartbeats.Unsubscribe(this);
}
}
}
public interface IHeartbeat
{
void OnHeartbeatUpdate(DateTime now, TimeSpan dif);
}
///
/// A manager class that can handle subscribing and unsubscribeing in the same update.
///
public sealed class HeartbeatManager
{
private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
private List Heartbeats = new List();
private IHeartbeat[] UpdateArray;
private DateTime LastUpdate = DateTime.MinValue;
public void Subscribe(IHeartbeat heartbeat)
{
using var _ = new WriteLock(rwLock);
if (!Heartbeats.Contains(heartbeat))
Heartbeats.Add(heartbeat);
}
public void Unsubscribe(IHeartbeat heartbeat)
{
using var _ = new WriteLock(rwLock);
Heartbeats.Remove(heartbeat);
}
public void Update()
{
var now = HTTPManager.CurrentFrameDateTime;
if (LastUpdate == DateTime.MinValue)
LastUpdate = now;
else
{
TimeSpan dif = now - LastUpdate;
LastUpdate = now;
int count = 0;
using (var _ = new ReadLock(rwLock))
{
if (UpdateArray == null || UpdateArray.Length < Heartbeats.Count)
Array.Resize(ref UpdateArray, Heartbeats.Count);
Heartbeats.CopyTo(0, UpdateArray, 0, Heartbeats.Count);
Array.Clear(UpdateArray, Heartbeats.Count, UpdateArray.Length - Heartbeats.Count);
count = Heartbeats.Count;
}
for (int i = 0; i < count; ++i)
{
try
{
UpdateArray[i].OnHeartbeatUpdate(now, dif);
}
catch
{ }
}
}
}
public void Clear()
{
using var _ = new WriteLock(rwLock);
Heartbeats.Clear();
}
}
}