summaryrefslogtreecommitdiff
path: root/Server/Util.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Server/Util.cs')
-rw-r--r--Server/Util.cs120
1 files changed, 120 insertions, 0 deletions
diff --git a/Server/Util.cs b/Server/Util.cs
new file mode 100644
index 0000000..6af6c81
--- /dev/null
+++ b/Server/Util.cs
@@ -0,0 +1,120 @@
+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<Task> 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<Task> GetScheduledTasks() {
+ bool lockTaken = false;
+ try {
+ Monitor.TryEnter(tasks, ref lockTaken);
+ if(lockTaken)
+ return tasks;
+ else
+ throw new NotSupportedException();
+ } finally {
+ if(lockTaken)
+ Monitor.Exit(tasks);
+ }
+ }
+}