|
|
- using System;
- using System.Collections.Generic;
- using System.Collections.Concurrent;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using System.Threading;
- using IPA.Utilities;
- using IPA.Utilities.Async;
- using System.IO;
- using System.Runtime.CompilerServices;
-
- namespace IPA.Config
- {
- internal static class ConfigRuntime
- {
- private class DirInfoEqComparer : IEqualityComparer<DirectoryInfo>
- {
- public bool Equals(DirectoryInfo x, DirectoryInfo y)
- => x?.FullName == y?.FullName;
-
- public int GetHashCode(DirectoryInfo obj)
- => obj?.GetHashCode() ?? 0;
- }
-
- private static readonly ConcurrentBag<Config> configs = new ConcurrentBag<Config>();
- private static readonly AutoResetEvent configsChangedWatcher = new AutoResetEvent(false);
- private static readonly ConcurrentDictionary<DirectoryInfo, FileSystemWatcher> watchers
- = new ConcurrentDictionary<DirectoryInfo, FileSystemWatcher>(new DirInfoEqComparer());
- private static readonly ConditionalWeakTable<FileSystemWatcher, ConcurrentBag<Config>> watcherTrackConfigs
- = new ConditionalWeakTable<FileSystemWatcher, ConcurrentBag<Config>>();
- private static SingleThreadTaskScheduler writeScheduler = null;
- private static TaskFactory writeFactory = null;
- private static Thread readThread = null;
-
- private static void TryStartRuntime()
- {
- if (writeScheduler == null || !writeScheduler.IsRunning)
- {
- writeFactory = null;
- writeScheduler = new SingleThreadTaskScheduler();
- writeScheduler.Start();
- }
- if (writeFactory == null)
- writeFactory = new TaskFactory(writeScheduler);
- if (readThread == null || !readThread.IsAlive)
- {
- readThread = new Thread(ReadThread);
- readThread.Start();
- }
- }
-
- public static void RegisterConfig(Config cfg)
- {
- lock (configs)
- { // we only lock this segment, so that this only waits on other calls to this
- if (configs.ToArray().Contains(cfg))
- throw new InvalidOperationException("Config already registered to runtime!");
-
- configs.Add(cfg);
- }
-
- TryStartRuntime();
-
- AddConfigToWatchers(cfg);
- }
-
- private static void AddConfigToWatchers(Config config)
- {
- var dir = config.File.Directory;
- if (!watchers.TryGetValue(dir, out var watcher))
- { // create the watcher
- watcher = new FileSystemWatcher(dir.FullName, "");
- var newWatcher = watchers.GetOrAdd(dir, watcher);
- if (watcher != newWatcher)
- { // if someone else beat us to adding, delete ours and switch to that new one
- watcher.Dispose();
- watcher = newWatcher;
- }
-
- watcher.NotifyFilter =
- NotifyFilters.FileName
- | NotifyFilters.LastWrite;
-
- watcher.Changed += FileChangedEvent;
- watcher.Created += FileChangedEvent;
- watcher.Renamed += FileChangedEvent;
- watcher.Deleted += FileChangedEvent;
- }
-
- TryStartRuntime();
-
- watcher.EnableRaisingEvents = false; // disable while we do shit
-
- var bag = watcherTrackConfigs.GetOrCreateValue(watcher);
- // we don't need to check containment because this function will only be called once per config ever
- bag.Add(config);
-
- watcher.EnableRaisingEvents = true;
-
- }
-
- private static void FileChangedEvent(object sender, FileSystemEventArgs e)
- {
- var watcher = sender as FileSystemWatcher;
- if (!watcherTrackConfigs.TryGetValue(watcher, out var bag)) return;
-
- var config = bag.FirstOrDefault(c => c.File.FullName == e.FullPath);
- if (config != null)
- writeFactory.StartNew(() => WriteTask(config).Wait());
- }
-
- private static async Task WriteTask(Config config)
- {
-
- }
-
- private static void ReadThread()
- {
-
- }
- }
- }
|