Browse Source

Fix config 64 max limit

Stop using WaitHandle and use an action instead
pull/101/head
Arimodu 1 year ago
parent
commit
1ceae129a2
4 changed files with 54 additions and 34 deletions
  1. +22
    -24
      IPA.Loader/Config/ConfigRuntime.cs
  2. +4
    -3
      IPA.Loader/Config/IConfigStore.cs
  3. +11
    -4
      IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs
  4. +17
    -3
      IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs

+ 22
- 24
IPA.Loader/Config/ConfigRuntime.cs View File

@ -2,15 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Threading; using System.Threading;
using IPA.Utilities;
using IPA.Utilities.Async; using IPA.Utilities.Async;
using System.IO; using System.IO;
using System.Runtime.CompilerServices;
using IPA.Logging;
using UnityEngine;
using Logger = IPA.Logging.Logger; using Logger = IPA.Logging.Logger;
#if NET4 #if NET4
using Task = System.Threading.Tasks.Task; using Task = System.Threading.Tasks.Task;
@ -31,7 +26,13 @@ namespace IPA.Config
} }
private static readonly ConcurrentBag<Config> configs = new ConcurrentBag<Config>(); private static readonly ConcurrentBag<Config> configs = new ConcurrentBag<Config>();
private static readonly AutoResetEvent configsChangedWatcher = new AutoResetEvent(false);
private static readonly Action configsChangedWatcher = () =>
{
foreach (var config in configs.Where(c => c.Store != null).ToArray())
{
config.Store.SyncAction = () => RequiresSave.Add(() => Save(config));
}
};
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
@ -92,7 +93,7 @@ namespace IPA.Config
configs.Add(cfg); configs.Add(cfg);
} }
configsChangedWatcher.Set();
configsChangedWatcher?.Invoke();
TryStartRuntime(); TryStartRuntime();
@ -101,7 +102,7 @@ namespace IPA.Config
public static void ConfigChanged() public static void ConfigChanged()
{ {
configsChangedWatcher.Set();
configsChangedWatcher.Invoke();
} }
private static void AddConfigToWatchers(Config config) private static void AddConfigToWatchers(Config config)
@ -212,22 +213,23 @@ namespace IPA.Config
Logger.Config.Error($"{nameof(IConfigStore)} for {config.File} errored while reading from the {nameof(IConfigProvider)}"); Logger.Config.Error($"{nameof(IConfigStore)} for {config.File} errored while reading from the {nameof(IConfigProvider)}");
Logger.Config.Error(e); Logger.Config.Error(e);
} }
}
}
static readonly BlockingCollection<Action> RequiresSave = new();
private static void SaveThread() private static void SaveThread()
{ {
try try
{ {
while (true)
var configArr = configs.Where(c => c.Store != null).ToArray();
configsChangedWatcher.Invoke();
foreach (var item in RequiresSave.GetConsumingEnumerable())
{ {
var configArr = configs.Where(c => c.Store != null).ToArray();
int index = -1;
try try
{ {
var waitHandles = configArr.Select(c => c.Store.SyncObject)
.Prepend(configsChangedWatcher)
.ToArray();
index = WaitHandle.WaitAny(waitHandles);
item.Invoke();
} }
catch (ThreadAbortException) catch (ThreadAbortException)
{ {
@ -239,20 +241,16 @@ namespace IPA.Config
Logger.Config.Error(e); Logger.Config.Error(e);
Thread.Sleep(TimeSpan.FromSeconds(1)); 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) catch (ThreadAbortException)
{ {
// we got aborted :( // we got aborted :(
} }
finally
{
RequiresSave.Dispose();
}
} }
} }
} }

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

@ -5,6 +5,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using static UnityEngine.Random;
namespace IPA.Config namespace IPA.Config
{ {
@ -14,11 +15,11 @@ namespace IPA.Config
public interface IConfigStore public interface IConfigStore
{ {
/// <summary> /// <summary>
/// A synchronization object for the save thread to wait on for changes.
/// An action that is gonna be called when a save is required
/// 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 handle.
/// The writer will never signal this.
/// </summary> /// </summary>
WaitHandle SyncObject { get; }
public Action SyncAction { get; set; }
/// <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.


+ 11
- 4
IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs View File

@ -48,10 +48,17 @@ 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;
private readonly AutoResetEvent resetEvent = new(false);
public WaitHandle SyncObject => resetEvent;
public static WaitHandle? ImplGetSyncObject(IGeneratedStore s) => FindImpl(s)?.SyncObject;
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;
}
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;
@ -63,7 +70,7 @@ namespace IPA.Config.Stores
{ {
try try
{ {
_ = resetEvent.Set();
SyncAction?.Invoke();
} }
catch (ObjectDisposedException e) catch (ObjectDisposedException e)
{ {


+ 17
- 3
IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs View File

@ -430,15 +430,18 @@ namespace IPA.Config.Stores
typeBuilder.AddInterfaceImplementation(typeof(IConfigStore)); typeBuilder.AddInterfaceImplementation(typeof(IConfigStore));
var IConfigStore_t = typeof(IConfigStore); var IConfigStore_t = typeof(IConfigStore);
var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncObject)).GetGetMethod();
var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncAction)).GetGetMethod();
var IConfigStore_SetSyncAction = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncAction)).GetSetMethod();
var IConfigStore_GetWriteSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.WriteSyncObject)).GetGetMethod(); var IConfigStore_GetWriteSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.WriteSyncObject)).GetGetMethod();
var IConfigStore_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo)); var IConfigStore_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo));
var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom)); var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom));
#region IConfigStore.SyncObject #region IConfigStore.SyncObject
var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncObject), PropertyAttributes.None, IConfigStore_GetSyncObject.ReturnType, null);
var syncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.SyncObject)}", virtualPropertyMethodAttr, syncObjProp.PropertyType, Type.EmptyTypes);
var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncAction), PropertyAttributes.None, IConfigStore_GetSyncObject.ReturnType, null);
var syncObjPropGet = typeBuilder.DefineMethod($"get_{nameof(IConfigStore.SyncAction)}", virtualPropertyMethodAttr, syncObjProp.PropertyType, Type.EmptyTypes);
var syncObjPropSet = typeBuilder.DefineMethod($"set_{nameof(IConfigStore.SyncAction)}", virtualPropertyMethodAttr, null, new[] { syncObjProp.PropertyType });
syncObjProp.SetGetMethod(syncObjPropGet); syncObjProp.SetGetMethod(syncObjPropGet);
syncObjProp.SetSetMethod(syncObjPropSet);
typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject); typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject);
{ {
@ -449,7 +452,18 @@ namespace IPA.Config.Stores
il.Emit(OpCodes.Call, Impl.ImplGetSyncObjectMethod); il.Emit(OpCodes.Call, Impl.ImplGetSyncObjectMethod);
il.Emit(OpCodes.Ret); il.Emit(OpCodes.Ret);
} }
{
var il = syncObjPropSet.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Tailcall);
il.Emit(OpCodes.Call, Impl.ImplSetSyncActionMethod);
il.Emit(OpCodes.Ret);
}
#endregion #endregion
#region IConfigStore.WriteSyncObject #region IConfigStore.WriteSyncObject
var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, IConfigStore_GetWriteSyncObject.ReturnType, null); var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, IConfigStore_GetWriteSyncObject.ReturnType, null);
var writeSyncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes); var writeSyncObjPropGet = typeBuilder.DefineMethod($"<g>{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes);


Loading…
Cancel
Save