using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Web; using System.Threading.Tasks; using C4IT.FASD.Base; using C4IT.Logging; using static C4IT.Logging.cLogManager; using static C4IT.FASD.Base.cF4SDHealthCardRawData; using System.Threading; namespace C4IT.DataHistoryProvider { public class cSuccessorJob { public List Tables = new List(); public cF4SDHealthCardRawData data = null; public Task task = null; public CancellationToken Token = CancellationToken.None; public bool Finished = false; public DateTime FinishedTime; public List Usage = null; } public class SuccessorEntry : List { public List UsageInfo = null; } public static class SuccessorCache { private static Timer _timerCache = new Timer(callbackTimerCache, null, 1000, 10000); private static void callbackTimerCache(object state) { var myNow = DateTime.UtcNow; lock (Cache) { var lstIds = new List(); foreach (var Entry in Cache) { var lstJobs = new List(); foreach (var Job in Entry.Value) { if (Job.Finished && (myNow - Job.FinishedTime) > TimeSpan.FromSeconds(60)) lstJobs.Add(Job); } foreach (var Job in lstJobs) Entry.Value.Remove(Job); if (Entry.Value.Count == 0) lstIds.Add(Entry.Key); } foreach (var id in lstIds) Cache.Remove(id); } } private static Dictionary Cache = new Dictionary(); public static bool AddTask(Guid Id, List Tables, Task> task, List UsageInfo, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var Job = new cSuccessorJob(); lock (Cache) { if (!Cache.TryGetValue(Id, out var lst)) { lst = new SuccessorEntry() { Job }; Cache.Add(Id, lst); } else lst.Add(Job); if (UsageInfo != null) lst.UsageInfo = UsageInfo; } Job.Tables = Tables; Job.task = Task.Run(async () => { try { var res = await task; Dictionary tbls = null; if (res != null) tbls = res.ToDictionary(x => x.Name, x => x); lock (Cache) { Job.task = null; Job.data = new cF4SDHealthCardRawData() { Id = Id, Tables = tbls }; Job.Finished = true; Job.FinishedTime = DateTime.UtcNow; } return true; } catch (Exception E) { LogException(E); } return false; }, Token); return true; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return false; } public static async Task GetNextResult(cDataHistoryCollector Collector, Guid Id, bool noAwait, cF4sdWebRequestInfo requestInfo, int LogDeep) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var lstResults = new SuccessorEntry(); var lstTasks = new SuccessorEntry(); var lstWaitingTables = new List(); lock (Cache) { if (!Cache.TryGetValue(Id, out var lstObj)) return null; foreach (var Entry in lstObj) { if (Entry.Finished) lstResults.Add(Entry); else { lstTasks.Add(Entry); lstWaitingTables.AddRange(Entry.Tables); } } foreach (var Entry in lstResults) lstObj.Remove(Entry); } if (lstTasks.Count == 0) Cache.Remove(Id); if (lstResults.Count > 0) { var RetVal = lstResults[0]; for (int i = 1; i < lstResults.Count; i++) { var res = lstResults[i]; if (res?.data?.Tables != null) { foreach (var Entry in res.data.Tables) { if (RetVal?.data?.Tables != null) RetVal.data.Tables[Entry.Key] = Entry.Value; else { } } } else { } } foreach (var Table in lstWaitingTables) { var tbl = new cF4SDHealthCardRawData.cHealthCardTable() { Name = Table, IsIncomplete = true, }; if (RetVal?.data?.Tables != null) RetVal.data.Tables[Table] = tbl; } // correct the result due to usage information if (RetVal.data != null && lstTasks.UsageInfo != null) cDataHistoryCollector.CorrectUsage(Collector, RetVal.data, lstTasks.UsageInfo); return RetVal.data; } if (noAwait) return null; var arrTask = new Task[lstTasks.Count]; for (int i = 0; i < lstTasks.Count; i++) arrTask[i] = lstTasks[i].task; var tskFinished = await Task.WhenAny(arrTask); return await GetNextResult(Collector, Id, true, requestInfo, LogDeep+1); } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } } }