// /** // * File: SchedulingPool.cs // * Author: haraldwolff // * // * This file and it's content is copyrighted by the Author and / or copyright holder. // * Any use wihtout proper permission is illegal and may lead to legal actions. // * // * // **/ using System; using System.Collections.Generic; using System.Threading; using ln.collections; using ln.type; namespace ln.threading { public class SchedulingPool { static SchedulingPool defaultPool = null; static public SchedulingPool Default { get { if (defaultPool == null) defaultPool = new SchedulingPool(); return defaultPool; } set => defaultPool = value; } public Pool ThreadPool { get; private set; } public int ThreadCount => ThreadPool.PoolSize; BTreeValueList schedule = new BTreeValueList(); Thread schedulerThread; Dictionary scheduledJobs = new Dictionary(); public SchedulingPool() :this(Environment.ProcessorCount) { } public SchedulingPool(int nThreads) { ThreadPool = new Pool(nThreads); ThreadPool.Start(); lock (this) { schedulerThread = new Thread(scheduler); schedulerThread.Start(); Monitor.Wait(this); } } void scheduler() { lock (this) { Monitor.PulseAll(this); while (true) { double now = DateTime.Now.ToUnixTimeMilliseconds(); if (schedule.Empty) { Monitor.Wait(this); } else if (schedule.First <= now) { while (!schedule.Empty && (schedule.First <= now)) { ScheduledJob scheduledJob = schedule.Shift(); ThreadPool.Enqueue(scheduledJob); scheduledJob.Reschedule(); enqueue(scheduledJob); } } else { Monitor.Wait(this, (int)(schedule.First - now)); } } } } void enqueue(ScheduledJob job) { lock (this) { if (schedule.TryAdd(job.NextScheduledExecution, job)) Monitor.Pulse(this); } } void dequeue(ScheduledJob job) { lock (this) { if (schedule.TryRemove(job.NextScheduledExecution, job)) Monitor.Pulse(this); } } public void Schedule(Action action, double scheduledIntervall) { if (!scheduledJobs.TryGetValue(action, out ScheduledJob scheduledJob)) { scheduledJob = new ScheduledJob(action, scheduledIntervall); scheduledJobs.Add(action, scheduledJob); } else { scheduledJob.ScheduledInterval = scheduledIntervall; } enqueue(scheduledJob); } public void Unschedule(Action action) { if (scheduledJobs.TryGetValue(action, out ScheduledJob scheduledJob)) { dequeue(scheduledJob); } } public class ScheduledJob : PoolJob { public SchedulingPool Pool { get; private set; } public Action Action { get; } public double NextScheduledExecution { get; set; } public double ScheduledInterval { get; set; } public ScheduledJob(Action action) : this(action, 0, DateTime.Now.ToUnixTimeMilliseconds()) { } public ScheduledJob(Action action, double scheduledInterval) : this(action, scheduledInterval, DateTime.Now.ToUnixTimeMilliseconds() + scheduledInterval) { } public ScheduledJob(Action action, double scheduledInterval, double nextScheduledExecution) { Action = action; ScheduledInterval = scheduledInterval; NextScheduledExecution = nextScheduledExecution; } public override void RunJob() => Action.Invoke(); public void Reschedule() => Reschedule(ScheduledInterval); public void Reschedule(double interval) { double currentTime = DateTime.Now.ToUnixTimeMilliseconds(); while (NextScheduledExecution < currentTime) NextScheduledExecution += ScheduledInterval; } } } }