(61d00a474) v0.9.7.1

This commit is contained in:
Regalis
2020-03-04 13:04:10 +01:00
parent 3c50efa5c9
commit 3c09ebe02f
5086 changed files with 786063 additions and 295871 deletions
@@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
#if WINDOWS_WSA || WINDOWS_UWP
using Windows.System.Threading;
using Windows.Foundation;
using System.Threading.Tasks;
#else
using System.Threading;
#endif
using GameAnalyticsSDK.Net.Logging;
namespace GameAnalyticsSDK.Net.Threading
{
public class GAThreading
{
private static bool endThread = false;
private static DateTime threadDeadline;
private static readonly GAThreading _instance = new GAThreading ();
private const int ThreadWaitTimeInMs = 1000;
private readonly PriorityQueue<long, TimedBlock> blocks = new PriorityQueue<long, TimedBlock>();
private readonly object threadLock = new object();
private TimedBlock scheduledBlock;
private bool hasScheduledBlockRun;
#if WINDOWS_WSA || WINDOWS_UWP
private IAsyncAction thread;
#else
private Thread thread;
#endif
private GAThreading()
{
threadDeadline = DateTime.Now;
hasScheduledBlockRun = true;
}
~GAThreading()
{
StopThread();
}
private static GAThreading Instance
{
get
{
return _instance;
}
}
private static void RunBlocks()
{
TimedBlock timedBlock;
while ((timedBlock = GetNextBlock()) != null)
{
timedBlock.block();
}
if ((timedBlock = GetScheduledBlock()) != null)
{
timedBlock.block();
}
}
public static void Run()
{
GALogger.D("Starting GA thread");
try
{
while(!endThread && threadDeadline.CompareTo(DateTime.Now) > 0)
{
RunBlocks();
#if WINDOWS_WSA || WINDOWS_UWP
Task.Delay(ThreadWaitTimeInMs).Wait();
#else
Thread.Sleep(ThreadWaitTimeInMs);
#endif
}
// run any last blocks added
RunBlocks();
if (!endThread)
{
GALogger.D("Ending GA thread");
}
}
catch(Exception)
{
//GALogger.E("Error on GA thread");
//GALogger.E(e.ToString());
}
}
public static void PerformTaskOnGAThread(string blockName, Action taskBlock)
{
PerformTaskOnGAThread(blockName, taskBlock, 0);
}
public static void PerformTaskOnGAThread(string blockName, Action taskBlock, long delayInSeconds)
{
if(endThread)
{
return;
}
lock(Instance.threadLock)
{
DateTime time = DateTime.Now;
time = time.AddSeconds(delayInSeconds);
TimedBlock timedBlock = new TimedBlock(time, taskBlock, blockName);
Instance.AddTimedBlock(timedBlock);
threadDeadline = time.AddSeconds(10);
#if WINDOWS_WSA || WINDOWS_UWP
if (IsThreadFinished())
{
StartThread();
}
#else
if (IsThreadFinished())
{
if(Instance.thread != null)
{
Instance.thread.Join();
}
StartThread();
}
#endif
}
}
public static void ScheduleTimer(double interval, string blockName, Action callback)
{
if (endThread)
{
return;
}
lock (Instance.threadLock)
{
if(Instance.hasScheduledBlockRun)
{
DateTime time = DateTime.Now;
time = time.AddSeconds(interval);
Instance.scheduledBlock = new TimedBlock(time, callback, blockName);
Instance.hasScheduledBlockRun = false;
threadDeadline = time.AddSeconds(2);
#if WINDOWS_WSA || WINDOWS_UWP
if (IsThreadFinished())
{
StartThread();
}
#else
if (IsThreadFinished())
{
if(Instance.thread != null)
{
Instance.thread.Join();
}
StartThread();
}
#endif
}
}
}
private void AddTimedBlock(TimedBlock timedBlock)
{
this.blocks.Enqueue(timedBlock.deadline.Ticks, timedBlock);
}
private static TimedBlock GetNextBlock()
{
lock(Instance.threadLock)
{
DateTime now = DateTime.Now;
if(Instance.blocks.HasItems && Instance.blocks.Peek().deadline.CompareTo(now) <= 0)
{
return Instance.blocks.Dequeue();
}
return null;
}
}
private static TimedBlock GetScheduledBlock()
{
lock (Instance.threadLock)
{
DateTime now = DateTime.Now;
if (!Instance.hasScheduledBlockRun && Instance.scheduledBlock != null && Instance.scheduledBlock.deadline.CompareTo(now) <= 0)
{
Instance.hasScheduledBlockRun = true;
return Instance.scheduledBlock;
}
return null;
}
}
public static void StartThread()
{
#if WINDOWS_WSA || WINDOWS_UWP
Instance.thread = ThreadPool.RunAsync(o => Run());
#else
Instance.thread = new Thread(new ThreadStart(Run));
Instance.thread.Priority = ThreadPriority.Lowest;
Instance.thread.Start();
#endif
}
public static void StopThread()
{
endThread = true;
}
public static bool IsThreadFinished()
{
#if WINDOWS_WSA || WINDOWS_UWP
return Instance.thread == null || Instance.thread.Status != AsyncStatus.Started;
#else
return Instance.thread == null || !Instance.thread.IsAlive;
#endif
}
}
}
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace GameAnalyticsSDK.Net.Threading
{
internal class PriorityQueue<TPriority, TItem>
{
readonly SortedDictionary<TPriority, Queue<TItem>> _subqueues;
public PriorityQueue(IComparer<TPriority> priorityComparer)
{
_subqueues = new SortedDictionary<TPriority, Queue<TItem>>(priorityComparer);
}
public PriorityQueue() : this(Comparer<TPriority>.Default) { }
public void Enqueue(TPriority priority, TItem item)
{
if (!_subqueues.ContainsKey(priority))
{
AddQueueOfPriority(priority);
}
_subqueues[priority].Enqueue(item);
}
private void AddQueueOfPriority(TPriority priority)
{
_subqueues.Add(priority, new Queue<TItem>());
}
public TItem Peek()
{
if (HasItems)
return _subqueues.First().Value.Peek();
else
throw new InvalidOperationException("The queue is empty");
}
public bool HasItems
{
get { return _subqueues.Any(); }
}
public TItem Dequeue()
{
if (_subqueues.Any())
return DequeueFromHighPriorityQueue();
else
throw new InvalidOperationException("The queue is empty");
}
private TItem DequeueFromHighPriorityQueue()
{
KeyValuePair<TPriority, Queue<TItem>> first = _subqueues.First();
TItem nextItem = first.Value.Dequeue();
if (!first.Value.Any())
{
_subqueues.Remove(first.Key);
}
return nextItem;
}
public int Count
{
get { return _subqueues.Sum(q => q.Value.Count); }
}
}
}
@@ -0,0 +1,29 @@
using System;
namespace GameAnalyticsSDK.Net
{
internal class TimedBlock : IComparable<TimedBlock>
{
public readonly DateTime deadline;
public readonly Action block;
public readonly string blockName;
public TimedBlock (DateTime deadline, Action block, string blockName)
{
this.deadline = deadline;
this.block = block;
this.blockName = blockName;
}
public int CompareTo(TimedBlock other)
{
return this.deadline.CompareTo (other.deadline);
}
public override string ToString ()
{
return "{TimedBlock: deadLine=" + this.deadline.Ticks + ", block=" + this.blockName + "}";
}
}
}