Browse Source

Avoid breaking change by marking obsolete instead

pull/101/head
Arimodu 1 year ago
parent
commit
f631f0826f
3 changed files with 63 additions and 41 deletions
  1. +56
    -14
      IPA.Loader/Config/ConfigRuntime.cs
  2. +4
    -9
      IPA.Loader/Config/IConfigStore.cs
  3. +3
    -18
      IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs

+ 56
- 14
IPA.Loader/Config/ConfigRuntime.cs View File

@ -25,20 +25,17 @@ namespace IPA.Config
=> obj?.GetHashCode() ?? 0; => obj?.GetHashCode() ?? 0;
} }
private static readonly ConcurrentBag<Config> configs = new ConcurrentBag<Config>();
private static readonly Action configsChangedAction = () =>
{
foreach (var config in configs.Where(c => c.Store != null).ToArray())
config.Store.SyncAction = () => RequiresSave.Add(() => Save(config));
};
private static readonly BlockingCollection<Action> RequiresSave = new();
private static readonly ConcurrentBag<Config> configs = new();
private static readonly AutoResetEvent configsChangedWatcher = new(false);
public static readonly BlockingCollection<IConfigStore> RequiresSave = new();
private static readonly ConcurrentDictionary<DirectoryInfo, FileSystemWatcher> watchers private static readonly ConcurrentDictionary<DirectoryInfo, FileSystemWatcher> watchers
= new ConcurrentDictionary<DirectoryInfo, FileSystemWatcher>(new DirInfoEqComparer()); = new ConcurrentDictionary<DirectoryInfo, FileSystemWatcher>(new DirInfoEqComparer());
private static readonly ConcurrentDictionary<FileSystemWatcher, ConcurrentBag<Config>> watcherTrackConfigs private static readonly ConcurrentDictionary<FileSystemWatcher, ConcurrentBag<Config>> watcherTrackConfigs
= new ConcurrentDictionary<FileSystemWatcher, ConcurrentBag<Config>>(); = new ConcurrentDictionary<FileSystemWatcher, ConcurrentBag<Config>>();
private static SingleThreadTaskScheduler loadScheduler = null;
private static TaskFactory loadFactory = null;
private static Thread saveThread = null;
private static SingleThreadTaskScheduler loadScheduler;
private static TaskFactory loadFactory;
private static Thread saveThread;
private static Thread legacySaveThread;
private static void TryStartRuntime() private static void TryStartRuntime()
{ {
@ -55,6 +52,11 @@ namespace IPA.Config
saveThread = new Thread(SaveThread); saveThread = new Thread(SaveThread);
saveThread.Start(); saveThread.Start();
} }
if (legacySaveThread == null || !legacySaveThread.IsAlive)
{
legacySaveThread = new Thread(LegacySaveThread);
legacySaveThread.Start();
}
AppDomain.CurrentDomain.ProcessExit -= ShutdownRuntime; AppDomain.CurrentDomain.ProcessExit -= ShutdownRuntime;
AppDomain.CurrentDomain.ProcessExit += ShutdownRuntime; AppDomain.CurrentDomain.ProcessExit += ShutdownRuntime;
@ -92,7 +94,7 @@ namespace IPA.Config
configs.Add(cfg); configs.Add(cfg);
} }
configsChangedAction.Invoke();
configsChangedWatcher.Set();
TryStartRuntime(); TryStartRuntime();
@ -101,7 +103,7 @@ namespace IPA.Config
public static void ConfigChanged() public static void ConfigChanged()
{ {
configsChangedAction.Invoke();
configsChangedWatcher.Set();
} }
private static void AddConfigToWatchers(Config config) private static void AddConfigToWatchers(Config config)
@ -218,12 +220,11 @@ namespace IPA.Config
{ {
try try
{ {
configsChangedAction.Invoke();
foreach (var item in RequiresSave.GetConsumingEnumerable()) foreach (var item in RequiresSave.GetConsumingEnumerable())
{ {
try try
{ {
item.Invoke();
Save(configs.First((c) => ReferenceEquals(c.Store.WriteSyncObject, item.WriteSyncObject)));
} }
catch (ThreadAbortException) catch (ThreadAbortException)
{ {
@ -246,5 +247,46 @@ namespace IPA.Config
RequiresSave.Dispose(); RequiresSave.Dispose();
} }
} }
private static void LegacySaveThread()
{
try
{
while (true)
{
var configArr = configs.Where(c => c.Store != null).Where(c => c.Store.SyncObject != 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;
}
// otherwise, we have a thing that changed in a store
Save(configArr[index - 1]);
}
}
catch (ThreadAbortException)
{
// we got aborted :(
}
}
} }
} }

+ 4
- 9
IPA.Loader/Config/IConfigStore.cs View File

@ -1,11 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using static UnityEngine.Random;
namespace IPA.Config namespace IPA.Config
{ {
@ -15,11 +9,12 @@ namespace IPA.Config
public interface IConfigStore public interface IConfigStore
{ {
/// <summary> /// <summary>
/// An action that is gonna be called when a save is required
/// A synchronization object for the save thread to wait on for changes.
/// It should be signaled whenever the internal state of the object is changed. /// It should be signaled whenever the internal state of the object is changed.
/// The writer will never signal this.
/// The writer will never signal this handle.
/// </summary> /// </summary>
public Action SyncAction { get; set; }
[Obsolete("Add a message here...")] // Do this later
WaitHandle SyncObject { get; }
/// <summary> /// <summary>
/// A synchronization object for the load thread and accessors to maintain safe synchronization. /// A synchronization object for the load thread and accessors to maintain safe synchronization.


+ 3
- 18
IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs View File

@ -47,16 +47,9 @@ namespace IPA.Config.Stores
internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) }); internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) });
public Impl(IGeneratedStore store) => generated = store; public Impl(IGeneratedStore store) => generated = store;
public Action? SyncAction { get; set; }
public static Action? ImplGetSyncObject(IGeneratedStore s) => FindImpl(s)?.SyncAction;
public static void ImplSetSyncAction(IGeneratedStore s, Action? value)
{
var impl = FindImpl(s);
if (impl != null) impl.SyncAction = value;
}
public WaitHandle? SyncObject => null;
public static WaitHandle? ImplGetSyncObject(IGeneratedStore s) => FindImpl(s)?.SyncObject;
internal static MethodInfo ImplGetSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetSyncObject)); internal static MethodInfo ImplGetSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetSyncObject));
internal static MethodInfo ImplSetSyncActionMethod = typeof(Impl).GetMethod(nameof(ImplSetSyncAction));
public ReaderWriterLockSlim WriteSyncObject { get; } = new(); public ReaderWriterLockSlim WriteSyncObject { get; } = new();
public static ReaderWriterLockSlim? ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject; public static ReaderWriterLockSlim? ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject;
@ -66,15 +59,7 @@ namespace IPA.Config.Stores
public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s)?.SignalChanged(); public static void ImplSignalChanged(IGeneratedStore s) => FindImpl(s)?.SignalChanged();
public void SignalChanged() public void SignalChanged()
{ {
try
{
SyncAction?.Invoke();
}
catch (ObjectDisposedException e)
{
Logger.Config.Error($"ObjectDisposedException while signalling a change for generated store {generated?.GetType()}");
Logger.Config.Error(e);
}
ConfigRuntime.RequiresSave.Add(this);
} }
internal static MethodInfo ImplInvokeChangedMethod = typeof(Impl).GetMethod(nameof(ImplInvokeChanged)); internal static MethodInfo ImplInvokeChangedMethod = typeof(Impl).GetMethod(nameof(ImplInvokeChanged));


Loading…
Cancel
Save