From 48b4819b9531b68ea7e2c468f341ebf58f5f8df7 Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Wed, 11 Dec 2019 21:52:38 -0600 Subject: [PATCH] Moved end-of-process saving into ConfigRuntime itself --- IPA.Loader/Config/Config.cs | 1 + IPA.Loader/Config/ConfigRuntime.cs | 101 +++++++++++++++++++-------- IPA.Loader/Loader/PluginComponent.cs | 2 - 3 files changed, 74 insertions(+), 30 deletions(-) diff --git a/IPA.Loader/Config/Config.cs b/IPA.Loader/Config/Config.cs index fd4b1610..e3860b9b 100644 --- a/IPA.Loader/Config/Config.cs +++ b/IPA.Loader/Config/Config.cs @@ -139,6 +139,7 @@ namespace IPA.Config internal IConfigStore Store = null; internal readonly FileInfo File; + internal int Writes = 0; /// /// Sets this object's . Can only be called once. diff --git a/IPA.Loader/Config/ConfigRuntime.cs b/IPA.Loader/Config/ConfigRuntime.cs index 7fb35ef3..2bd908bb 100644 --- a/IPA.Loader/Config/ConfigRuntime.cs +++ b/IPA.Loader/Config/ConfigRuntime.cs @@ -10,6 +10,10 @@ using IPA.Utilities.Async; using System.IO; using System.Runtime.CompilerServices; using IPA.Logging; +#if NET4 +using Task = System.Threading.Tasks.Task; +using TaskEx = System.Threading.Tasks.Task; +#endif namespace IPA.Config { @@ -49,6 +53,24 @@ namespace IPA.Config saveThread = new Thread(SaveThread); saveThread.Start(); } + + AppDomain.CurrentDomain.ProcessExit -= ShutdownRuntime; + AppDomain.CurrentDomain.ProcessExit += ShutdownRuntime; + } + + private static void ShutdownRuntime(object sender, EventArgs e) + { + watcherTrackConfigs.Clear(); + var watchList = watchers.ToArray(); + watchers.Clear(); + + foreach (var pair in watchList) + pair.Value.EnableRaisingEvents = false; + + loadScheduler.Join(); // we can wait for the loads to finish + saveThread.Abort(); // eww, but i don't like any of the other potential solutions + + SaveAll(); } public static void RegisterConfig(Config cfg) @@ -107,25 +129,31 @@ namespace IPA.Config } + private static void EnsureWritesSane(Config config) + { + // compare exchange loop to be sane + var writes = config.Writes; + while (writes < 0) + writes = Interlocked.CompareExchange(ref config.Writes, 0, writes); + } + 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) + if (config != null && Interlocked.Decrement(ref config.Writes) + 1 > 0) + { + EnsureWritesSane(config); TriggerFileLoad(config); + } } public static Task TriggerFileLoad(Config config) => loadFactory.StartNew(() => LoadTask(config)); public static Task TriggerLoadAll() => -#if NET3 - TaskEx -#else - Task -#endif - .WhenAll(configs.Select(TriggerFileLoad)); + TaskEx.WhenAll(configs.Select(TriggerFileLoad)); /// /// this is synchronous, unlike @@ -141,9 +169,15 @@ namespace IPA.Config lock (config.Provider) { config.Provider.File = config.File; + EnsureWritesSane(config); + Interlocked.Increment(ref config.Writes); store.WriteTo(config.Provider); } } + catch (ThreadAbortException) + { + throw; + } catch (Exception e) { Logger.config.Error($"{nameof(IConfigStore)} for {config.File} errored while writing to disk"); @@ -181,31 +215,42 @@ namespace IPA.Config private static void SaveThread() { - while (true) + try { - var configArr = configs.Where(c => c.Store != null).ToArray(); - int index = -1; - try - { - var waitHandles = configArr.Select(c => c.Store.SyncObject) - .Prepend(configsChangedWatcher) - .ToArray(); - index = WaitHandle.WaitAny(waitHandles); - } - catch (Exception e) + while (true) { - Logger.config.Error($"Error waiting for in-memory updates"); - Logger.config.Error(e); - Thread.Sleep(TimeSpan.FromSeconds(1)); - } + var configArr = configs.Where(c => c.Store != null).ToArray(); + int index = -1; + try + { + var waitHandles = configArr.Select(c => c.Store.SyncObject) + .Prepend(configsChangedWatcher) + .ToArray(); + index = WaitHandle.WaitAny(waitHandles); + } + catch (ThreadAbortException) + { + break; + } + catch (Exception e) + { + Logger.config.Error($"Error waiting for in-memory updates"); + Logger.config.Error(e); + Thread.Sleep(TimeSpan.FromSeconds(1)); + } - if (index <= 0) - { // we got a signal that the configs collection changed, loop around, or errored - continue; - } + if (index <= 0) + { // we got a signal that the configs collection changed, loop around, or errored + continue; + } - // otherwise, we have a thing that changed in a store - Save(configArr[index - 1]); + // otherwise, we have a thing that changed in a store + Save(configArr[index - 1]); + } + } + catch (ThreadAbortException) + { + // we got aborted :( } } } diff --git a/IPA.Loader/Loader/PluginComponent.cs b/IPA.Loader/Loader/PluginComponent.cs index 92bfe8b9..61a71001 100644 --- a/IPA.Loader/Loader/PluginComponent.cs +++ b/IPA.Loader/Loader/PluginComponent.cs @@ -76,8 +76,6 @@ namespace IPA.Loader bsPlugins.OnApplicationQuit(); ipaPlugins.OnApplicationQuit(); - ConfigRuntime.SaveAll(); - quitting = true; }