Files
raven/server/AyaNova/util/ReportProcessManager.cs
2021-12-24 16:16:40 +00:00

116 lines
4.5 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using Microsoft.Extensions.Logging;
namespace AyaNova.Util
{
//Track processes and kill any that go past their expiry date
internal static class ReportRenderManager
{
//thread safe collection for unordered items, optimized for single thread produce/consume (which is the norm here) but supports multithread produce / consume (which is needed for separate cleanup job)
internal static ConcurrentBag<ReportRenderInstanceInfo> _baginstances;// = new ConcurrentBag<ReportRenderInstanceInfo>();
static ReportRenderManager()
{
_baginstances = new ConcurrentBag<ReportRenderInstanceInfo>();
}
internal class ReportRenderInstanceInfo
{
internal int ReporterProcessId { get; set; }
internal DateTime Expires { get; set; }
internal ReportRenderInstanceInfo(int processId, DateTime expires)
{
ReporterProcessId = processId;
Expires = expires;
}
}
internal static void KillExpiredRenders(ILogger log)
{
log.LogDebug("Clear potential expired render jobs check");
//check for expired and remove
var Instances = _baginstances.ToArray();
var dtNow = DateTime.UtcNow;
foreach (ReportRenderInstanceInfo i in Instances)
{
if (i.Expires < dtNow)
{
// #if (DEBUG)
// log.LogInformation($"DBG: KillExpiredRenders attempting kill of expired process {i.ReporterProcessId}");
// #endif
ForceCloseProcess(i, log);
}
}
}
internal static bool ForceCloseProcess(ReportRenderInstanceInfo instance, ILogger log)
{
log.LogDebug($"ForceCloseProcess on report render instance id {instance.ReporterProcessId} expired {instance.Expires.ToString()} utc");
try
{
var p = Process.GetProcessById(instance.ReporterProcessId);
if (p != null)
{
//we have an existing process
//try to kill it
p.Kill(true);
if (p.HasExited == false)
{
log.LogDebug($"Expired report render instance id {instance.ReporterProcessId} could not be force closed");
return false;//can't kill it so can't free up a slot
}
}
//remove it from the list, it's either gone or killed at this point
//this would not be unexpected since it will normally just close on it's own
//at the finally in render report
_baginstances.TryTake(out instance);
return true;//process that was there is now not there so while not perfect system we will consider it free
}
catch (ArgumentException)
{
//do nothing, this is normal, the process could not be found and this means it's already been removed:
//ArgumentException
//The process specified by the processId parameter is not running. The identifier might be expired.
_baginstances.TryTake(out instance);
return true;
}
}
internal static void AddProcess(int processId, DateTime expires, ILogger log)
{
// #if (DEBUG)
// log.LogInformation($"DBG: RenderSlotAvailable::AddProcess {processId} in the bag");
// #endif
_baginstances.Add(new ReportRenderInstanceInfo(processId, expires));
// #if (DEBUG)
// log.LogInformation($"DBG: RenderSlotAvailable::AddProcess, there are currently {_baginstances.Count} instances in the bag");
// #endif
}
internal static void RemoveProcess(int processId, ILogger log)
{
// #if (DEBUG)
// log.LogInformation($"DBG: RenderSlotAvailable::RemoveProcess {processId} from the bag");
// #endif
foreach (var i in _baginstances)
{
if (i.ReporterProcessId == processId)
{
ForceCloseProcess(i, log);
break;
}
}
}
}//eoc
}//eons