Browse Source

Finished up the ConfigRuntime

pull/46/head
Anairkoen Schno 5 years ago
parent
commit
11bfbeaafb
3 changed files with 75 additions and 22 deletions
  1. +9
    -5
      IPA.Loader/Config/Config.cs
  2. +65
    -16
      IPA.Loader/Config/ConfigRuntime.cs
  3. +1
    -1
      IPA.Loader/Config/Stores/GeneratedStore.cs

+ 9
- 5
IPA.Loader/Config/Config.cs View File

@ -136,16 +136,20 @@ namespace IPA.Config
/// </summary> /// </summary>
public IConfigProvider Provider { get; private set; } public IConfigProvider Provider { get; private set; }
internal readonly HashSet<IConfigStore> Stores = new HashSet<IConfigStore>();
internal IConfigStore Store = null;
internal readonly FileInfo File; internal readonly FileInfo File;
/// <summary> /// <summary>
/// Adds an <see cref="IConfigStore"/> to this <see cref="Config"/> object.
/// Sets this object's <see cref="IConfigStore"/>. Can only be called once.
/// </summary> /// </summary>
/// <param name="store">the <see cref="IConfigStore"/> to add to this instance</param> /// <param name="store">the <see cref="IConfigStore"/> to add to this instance</param>
/// <returns><see langword="true"/> if the <see cref="IConfigStore"/> was not already registered to this <see cref="Config"/> object,
/// otherwise <see langword="false"/></returns>
public bool AddStore(IConfigStore store) => Stores.Add(store);
/// <exception cref="InvalidOperationException">If this was called before.</exception>
public void SetStore(IConfigStore store)
{
if (Store != null)
throw new InvalidOperationException($"{nameof(SetStore)} can only be called once");
Store = store;
}
private Config(string name, IConfigProvider provider, FileInfo file) private Config(string name, IConfigProvider provider, FileInfo file)
{ {


+ 65
- 16
IPA.Loader/Config/ConfigRuntime.cs View File

@ -9,6 +9,7 @@ using IPA.Utilities;
using IPA.Utilities.Async; using IPA.Utilities.Async;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using IPA.Logging;
namespace IPA.Config namespace IPA.Config
{ {
@ -29,24 +30,24 @@ namespace IPA.Config
= new ConcurrentDictionary<DirectoryInfo, FileSystemWatcher>(new DirInfoEqComparer()); = new ConcurrentDictionary<DirectoryInfo, FileSystemWatcher>(new DirInfoEqComparer());
private static readonly ConditionalWeakTable<FileSystemWatcher, ConcurrentBag<Config>> watcherTrackConfigs private static readonly ConditionalWeakTable<FileSystemWatcher, ConcurrentBag<Config>> watcherTrackConfigs
= new ConditionalWeakTable<FileSystemWatcher, ConcurrentBag<Config>>(); = new ConditionalWeakTable<FileSystemWatcher, ConcurrentBag<Config>>();
private static SingleThreadTaskScheduler writeScheduler = null;
private static TaskFactory writeFactory = null;
private static Thread readThread = null;
private static SingleThreadTaskScheduler loadScheduler = null;
private static TaskFactory loadFactory = null;
private static Thread saveThread = null;
private static void TryStartRuntime() private static void TryStartRuntime()
{ {
if (writeScheduler == null || !writeScheduler.IsRunning)
if (loadScheduler == null || !loadScheduler.IsRunning)
{ {
writeFactory = null;
writeScheduler = new SingleThreadTaskScheduler();
writeScheduler.Start();
loadFactory = null;
loadScheduler = new SingleThreadTaskScheduler();
loadScheduler.Start();
} }
if (writeFactory == null)
writeFactory = new TaskFactory(writeScheduler);
if (readThread == null || !readThread.IsAlive)
if (loadFactory == null)
loadFactory = new TaskFactory(loadScheduler);
if (saveThread == null || !saveThread.IsAlive)
{ {
readThread = new Thread(ReadThread);
readThread.Start();
saveThread = new Thread(SaveThread);
saveThread.Start();
} }
} }
@ -59,6 +60,7 @@ namespace IPA.Config
configs.Add(cfg); configs.Add(cfg);
} }
configsChangedWatcher.Set();
TryStartRuntime(); TryStartRuntime();
@ -107,17 +109,64 @@ namespace IPA.Config
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)
writeFactory.StartNew(() => WriteTask(config).Wait());
TriggerFileLoad(config);
} }
private static async Task WriteTask(Config config)
{
public static Task TriggerFileLoad(Config config) => loadFactory.StartNew(() => LoadTask(config));
private static void LoadTask(Config config)
{ // these tasks will always be running in the same thread as each other
try
{
var store = config.Store;
using var writeLock = Synchronization.LockWrite(store.WriteSyncObject);
lock (config.Provider)
{
config.Provider.File = config.File;
store.ReadFrom(config.Provider);
}
}
catch (Exception e)
{
Logger.config.Error($"{nameof(IConfigStore)} for {config.File} errored while reading from the {nameof(IConfigProvider)}");
Logger.config.Error(e);
}
} }
private static void ReadThread()
private static void SaveThread()
{ {
while (true)
{
var configArr = configs.ToArray();
var waitHandles = configArr.Select(c => c.Store.SyncObject)
.Prepend(configsChangedWatcher)
.ToArray();
var index = WaitHandle.WaitAny(waitHandles);
if (index == 0)
{ // we got a signal that the configs collection changed, loop around
continue;
}
// otherwise, we have a thing that changed in a store
var config = configArr[index - 1];
var store = config.Store;
try
{
using var readLock = Synchronization.LockRead(store.WriteSyncObject);
lock (config.Provider)
{
config.Provider.File = config.File;
store.WriteTo(config.Provider);
}
}
catch (Exception e)
{
Logger.config.Error($"{nameof(IConfigStore)} for {config.File} errored while writing to disk");
Logger.config.Error(e);
}
}
} }
} }
} }

+ 1
- 1
IPA.Loader/Config/Stores/GeneratedStore.cs View File

@ -40,7 +40,7 @@ namespace IPA.Config.Stores
public static T Generated<T>(this Config cfg) where T : class public static T Generated<T>(this Config cfg) where T : class
{ {
var ret = GeneratedStore.Create<T>(); var ret = GeneratedStore.Create<T>();
cfg.AddStore(ret as IConfigStore);
cfg.SetStore(ret as IConfigStore);
return ret; return ret;
} }
} }


Loading…
Cancel
Save