Browse Source

Moved end-of-process saving into ConfigRuntime itself

pull/46/head
Anairkoen Schno 5 years ago
parent
commit
48b4819b95
3 changed files with 74 additions and 30 deletions
  1. +1
    -0
      IPA.Loader/Config/Config.cs
  2. +73
    -28
      IPA.Loader/Config/ConfigRuntime.cs
  3. +0
    -2
      IPA.Loader/Loader/PluginComponent.cs

+ 1
- 0
IPA.Loader/Config/Config.cs View File

@ -139,6 +139,7 @@ namespace IPA.Config
internal IConfigStore Store = null; internal IConfigStore Store = null;
internal readonly FileInfo File; internal readonly FileInfo File;
internal int Writes = 0;
/// <summary> /// <summary>
/// Sets this object's <see cref="IConfigStore"/>. Can only be called once. /// Sets this object's <see cref="IConfigStore"/>. Can only be called once.


+ 73
- 28
IPA.Loader/Config/ConfigRuntime.cs View File

@ -10,6 +10,10 @@ using IPA.Utilities.Async;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using IPA.Logging; using IPA.Logging;
#if NET4
using Task = System.Threading.Tasks.Task;
using TaskEx = System.Threading.Tasks.Task;
#endif
namespace IPA.Config namespace IPA.Config
{ {
@ -49,6 +53,24 @@ namespace IPA.Config
saveThread = new Thread(SaveThread); saveThread = new Thread(SaveThread);
saveThread.Start(); 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) 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) private static void FileChangedEvent(object sender, FileSystemEventArgs e)
{ {
var watcher = sender as FileSystemWatcher; var watcher = sender as FileSystemWatcher;
if (!watcherTrackConfigs.TryGetValue(watcher, out var bag)) return; if (!watcherTrackConfigs.TryGetValue(watcher, out var bag)) return;
var config = bag.FirstOrDefault(c => c.File.FullName == e.FullPath); 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); TriggerFileLoad(config);
}
} }
public static Task TriggerFileLoad(Config config) => loadFactory.StartNew(() => LoadTask(config)); public static Task TriggerFileLoad(Config config) => loadFactory.StartNew(() => LoadTask(config));
public static Task TriggerLoadAll() => public static Task TriggerLoadAll() =>
#if NET3
TaskEx
#else
Task
#endif
.WhenAll(configs.Select(TriggerFileLoad));
TaskEx.WhenAll(configs.Select(TriggerFileLoad));
/// <summary> /// <summary>
/// this is synchronous, unlike <see cref="TriggerFileLoad(Config)"/> /// this is synchronous, unlike <see cref="TriggerFileLoad(Config)"/>
@ -141,9 +169,15 @@ namespace IPA.Config
lock (config.Provider) lock (config.Provider)
{ {
config.Provider.File = config.File; config.Provider.File = config.File;
EnsureWritesSane(config);
Interlocked.Increment(ref config.Writes);
store.WriteTo(config.Provider); store.WriteTo(config.Provider);
} }
} }
catch (ThreadAbortException)
{
throw;
}
catch (Exception e) catch (Exception e)
{ {
Logger.config.Error($"{nameof(IConfigStore)} for {config.File} errored while writing to disk"); Logger.config.Error($"{nameof(IConfigStore)} for {config.File} errored while writing to disk");
@ -181,31 +215,42 @@ namespace IPA.Config
private static void SaveThread() 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 :(
} }
} }
} }


+ 0
- 2
IPA.Loader/Loader/PluginComponent.cs View File

@ -76,8 +76,6 @@ namespace IPA.Loader
bsPlugins.OnApplicationQuit(); bsPlugins.OnApplicationQuit();
ipaPlugins.OnApplicationQuit(); ipaPlugins.OnApplicationQuit();
ConfigRuntime.SaveAll();
quitting = true; quitting = true;
} }


Loading…
Cancel
Save