using System;
using System.Collections.Generic;
using UnityEngine.LowLevel;
using Unity.Services.Core.Internal;
using NotNull = JetBrains.Annotations.NotNullAttribute;
namespace Unity.Services.Core.Scheduler.Internal
{
class ActionScheduler : IActionScheduler
{
const long k_MinimumIdValue = 1;
internal readonly PlayerLoopSystem SchedulerLoopSystem;
readonly ITimeProvider m_TimeProvider;
///
/// Members requiring thread safety:
/// * .
/// * .
/// * .
///
readonly object m_Lock = new object();
readonly MinimumBinaryHeap m_ScheduledActions
= new MinimumBinaryHeap(new ScheduledInvocationComparer());
readonly Dictionary m_IdScheduledInvocationMap
= new Dictionary();
readonly List m_ExpiredActions = new List();
long m_NextId = k_MinimumIdValue;
public ActionScheduler()
: this(new UtcTimeProvider()) {}
public ActionScheduler(ITimeProvider timeProvider)
{
m_TimeProvider = timeProvider;
SchedulerLoopSystem = new PlayerLoopSystem
{
type = typeof(ActionScheduler),
updateDelegate = ExecuteExpiredActions
};
}
public int ScheduledActionsCount => m_ScheduledActions.Count;
public long ScheduleAction([NotNull] Action action, double delaySeconds = 0)
{
if (delaySeconds < 0)
{
throw new ArgumentException("delaySeconds can not be negative");
}
if (action is null)
{
throw new ArgumentNullException(nameof(action));
}
lock (m_Lock)
{
var scheduledInvocation = new ScheduledInvocation
{
Action = action,
InvocationTime = m_TimeProvider.Now.AddSeconds(delaySeconds),
ActionId = m_NextId++
};
if (m_NextId < k_MinimumIdValue)
{
m_NextId = k_MinimumIdValue;
}
m_ScheduledActions.Insert(scheduledInvocation);
m_IdScheduledInvocationMap.Add(scheduledInvocation.ActionId, scheduledInvocation);
return scheduledInvocation.ActionId;
}
}
public void CancelAction(long actionId)
{
lock (m_Lock)
{
if (!m_IdScheduledInvocationMap.TryGetValue(actionId, out var scheduledInvocation))
{
return;
}
m_ScheduledActions.Remove(scheduledInvocation);
m_IdScheduledInvocationMap.Remove(scheduledInvocation.ActionId);
}
}
internal void ExecuteExpiredActions()
{
lock (m_Lock)
{
m_ExpiredActions.Clear();
while (m_ScheduledActions.Count > 0
&& m_ScheduledActions.Min?.InvocationTime <= m_TimeProvider.Now)
{
var expiredAction = m_ScheduledActions.ExtractMin();
m_ExpiredActions.Add(expiredAction);
m_ScheduledActions.Remove(expiredAction);
m_IdScheduledInvocationMap.Remove(expiredAction.ActionId);
}
foreach (var expiredAction in m_ExpiredActions)
{
try
{
expiredAction.Action();
}
catch (Exception e)
{
CoreLogger.LogException(e);
}
}
}
}
internal static void UpdateCurrentPlayerLoopWith(
List subSystemList, PlayerLoopSystem currentPlayerLoop)
{
currentPlayerLoop.subSystemList = subSystemList.ToArray();
PlayerLoop.SetPlayerLoop(currentPlayerLoop);
}
public void JoinPlayerLoopSystem()
{
var currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
var currentSubSystems = new List(currentPlayerLoop.subSystemList);
if (!currentSubSystems.Contains(SchedulerLoopSystem))
{
currentSubSystems.Add(SchedulerLoopSystem);
UpdateCurrentPlayerLoopWith(currentSubSystems, currentPlayerLoop);
}
}
public void QuitPlayerLoopSystem()
{
var currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop();
var currentSubSystems = new List(currentPlayerLoop.subSystemList);
if (currentSubSystems.Remove(SchedulerLoopSystem))
{
UpdateCurrentPlayerLoopWith(currentSubSystems, currentPlayerLoop);
}
}
}
}