namespace HyperBooru.Util; public static class Extensions { public static readonly string[] MagnitudeOrders = new[] { "K", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q" }; public static DateTime? TryParseDateTimeUtc(this string s) { bool success = DateTime.TryParse(s, out var dateTime); return success ? DateTime.SpecifyKind(dateTime, DateTimeKind.Utc) : null; } public static string ToBytesSI(this long x) { var exp = (int) Math.Log10(x); var suffix = MagnitudeOrders.ElementAtOrDefault(exp / 3 - 1); if(suffix is null) return x.ToString(); double n = x / Math.Pow(10, exp / 3 * 3); return $"{Math.Round(n, 2 - (exp % 3))} {suffix}B"; } public static string ToStringHumanReadable(this TimeSpan t) { if(t.TotalMilliseconds < 1000) return string.Format("{0:0}ms", t.TotalMilliseconds); if(t.TotalSeconds < 60) return string.Format("{0:0.00}s", t.TotalSeconds); if(t.TotalMinutes < 60) return string.Format("{0:0}m{0:0}s", t.TotalMinutes, t.Seconds); if(t.TotalHours < 24) return string.Format("{0:0}h{0:0}m", t.TotalHours, t.Minutes); return string.Format("{0:0.00}d", t.TotalDays); } } public class LimitedConcurrencyTaskScheduler : TaskScheduler { public sealed override int MaximumConcurrencyLevel => maxConcurrency; private int maxConcurrency; [ThreadStatic] private static bool threadIsProcessingItems; private readonly LinkedList tasks = new(); private int delegatesQueuedOrRunning = 0; public LimitedConcurrencyTaskScheduler() { maxConcurrency = Environment.ProcessorCount; } public LimitedConcurrencyTaskScheduler(int maxConcurrency) { if(maxConcurrency < 1) throw new ArgumentOutOfRangeException("maxConcurrency must be greater than 0"); this.maxConcurrency = (int) maxConcurrency; } protected sealed override void QueueTask(Task task) { lock(tasks) { tasks.AddLast(task); if(delegatesQueuedOrRunning < maxConcurrency) { delegatesQueuedOrRunning++; NotifyThreadPoolOfPendingWork(); } } } private void NotifyThreadPoolOfPendingWork() { ThreadPool.UnsafeQueueUserWorkItem(_ => { threadIsProcessingItems = true; try { while(true) { Task item; lock(tasks) { if(tasks.Count == 0) { delegatesQueuedOrRunning--; break; } else { item = tasks.First.Value; tasks.RemoveFirst(); } } TryExecuteTask(item); } } finally { threadIsProcessingItems = false; } }, null); } protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { if(!threadIsProcessingItems) return false; if(taskWasPreviouslyQueued) return TryDequeue(task) ? TryExecuteTask(task) : false; else return TryExecuteTask(task); } protected sealed override bool TryDequeue(Task task) { lock(tasks) { return tasks.Remove(task); } } protected sealed override IEnumerable GetScheduledTasks() { bool lockTaken = false; try { Monitor.TryEnter(tasks, ref lockTaken); if(lockTaken) return tasks; else throw new NotSupportedException(); } finally { if(lockTaken) Monitor.Exit(tasks); } } }