using DTS.Common.Utilities.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace DTS.Common.Classes
{
///
/// This class represents work requested of the services framework
///
public class ServiceCall
{
public bool Started { get; set; } = false;
///
/// the work to be done when service call is made
///
public Action WorkAction{ get; set; }
///
/// indicates the service call is complete
///
public void MarkDone()
{
ServiceQueue.MarkFinished(this);
}
public string Name { get; set; }
public ServiceCall(string name)
{
Name = name;
}
}
///
/// this class handles calls of the services framework by doing a little managing of who gets to run and when
///
public class ServiceQueue
{
///
/// just a lock to keep resource access thread safe
///
private static readonly object QueueLock = new object();
///
/// this is sort of a Queue, actions are enqueued when the are instantiated,
/// but using a list allows us to re-order then when needed and to remove items that are no longer needed and haven't been
/// started yet
///
private readonly List ServiceCalls = new List();
///
/// the singleton/the instance of the queue
///
private static ServiceQueue _serviceQueue = new ServiceQueue();
///
/// adds a new item for the services framework to handle
///
///
public static void Enqueue(ServiceCall call)
{
lock (QueueLock)
{
//adds work item to the queue
_serviceQueue.ServiceCalls.Add(call);
//start the item if it's the only item in the queue
if (1 == _serviceQueue.ServiceCalls.Count)
{
StartWork();
}
}
}
///
/// starts a call into the services framework
///
private static void StartWork()
{
lock (QueueLock)
{
//if there's any calls in the queue, launch the first
if (_serviceQueue.ServiceCalls.Any())
{
var work = _serviceQueue.ServiceCalls.First();
//to prevent multiple startworks from starting the same work, just make sure it's not started
if( work.Started ){ return; }
work.Started = true;
Task.Run(() => { work.WorkAction(); });
}
}
}
///
/// marks a servicecall as finished, if it's the current work item the next work item will be started if any
///
///
public static void MarkFinished(ServiceCall call)
{
lock (QueueLock)
{
if (!_serviceQueue.ServiceCalls.Any() || !_serviceQueue.ServiceCalls.Contains(call))
{
return;
}
if (_serviceQueue.ServiceCalls.First() != call)
{
//it wasn't being actively worked on, just remove
_serviceQueue.ServiceCalls.Remove(call);
return;
}
//this was the current work item, so we are free to start a new item
_serviceQueue.ServiceCalls.RemoveAt(0);
if (_serviceQueue.ServiceCalls.Any())
{
StartWork();
}
}
}
}
}