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(); } } } } }