From 1ceae129a24a96be97d9e67e0e6cf0192b4e3c86 Mon Sep 17 00:00:00 2001 From: Arimodu Date: Tue, 19 Sep 2023 09:03:41 +0200 Subject: [PATCH 1/5] Fix config 64 max limit Stop using WaitHandle and use an action instead --- IPA.Loader/Config/ConfigRuntime.cs | 46 +++++++++---------- IPA.Loader/Config/IConfigStore.cs | 7 +-- .../GeneratedStoreImpl/IGeneratedStore.cs | 15 ++++-- .../Stores/GeneratedStoreImpl/MakeCreator.cs | 20 ++++++-- 4 files changed, 54 insertions(+), 34 deletions(-) diff --git a/IPA.Loader/Config/ConfigRuntime.cs b/IPA.Loader/Config/ConfigRuntime.cs index baf5138f..8f8bf640 100644 --- a/IPA.Loader/Config/ConfigRuntime.cs +++ b/IPA.Loader/Config/ConfigRuntime.cs @@ -2,15 +2,10 @@ using System.Collections.Generic; using System.Collections.Concurrent; using System.Linq; -using System.Text; using System.Threading.Tasks; using System.Threading; -using IPA.Utilities; using IPA.Utilities.Async; using System.IO; -using System.Runtime.CompilerServices; -using IPA.Logging; -using UnityEngine; using Logger = IPA.Logging.Logger; #if NET4 using Task = System.Threading.Tasks.Task; @@ -31,7 +26,13 @@ namespace IPA.Config } private static readonly ConcurrentBag configs = new ConcurrentBag(); - 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 watchers = new ConcurrentDictionary(new DirInfoEqComparer()); private static readonly ConcurrentDictionary> watcherTrackConfigs @@ -92,7 +93,7 @@ namespace IPA.Config configs.Add(cfg); } - configsChangedWatcher.Set(); + configsChangedWatcher?.Invoke(); TryStartRuntime(); @@ -101,7 +102,7 @@ namespace IPA.Config public static void ConfigChanged() { - configsChangedWatcher.Set(); + configsChangedWatcher.Invoke(); } 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(e); } - } + } + + static readonly BlockingCollection RequiresSave = new(); private static void SaveThread() { 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 { - var waitHandles = configArr.Select(c => c.Store.SyncObject) - .Prepend(configsChangedWatcher) - .ToArray(); - index = WaitHandle.WaitAny(waitHandles); + item.Invoke(); } catch (ThreadAbortException) { @@ -239,20 +241,16 @@ namespace IPA.Config 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 :( } + finally + { + RequiresSave.Dispose(); + } } } } diff --git a/IPA.Loader/Config/IConfigStore.cs b/IPA.Loader/Config/IConfigStore.cs index 240ecf04..270773bf 100644 --- a/IPA.Loader/Config/IConfigStore.cs +++ b/IPA.Loader/Config/IConfigStore.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using static UnityEngine.Random; namespace IPA.Config { @@ -14,11 +15,11 @@ namespace IPA.Config public interface IConfigStore { /// - /// 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. - /// The writer will never signal this handle. + /// The writer will never signal this. /// - WaitHandle SyncObject { get; } + public Action SyncAction { get; set; } /// /// A synchronization object for the load thread and accessors to maintain safe synchronization. diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs index 28620626..39162cf4 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs @@ -48,10 +48,17 @@ namespace IPA.Config.Stores internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) }); 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 ImplSetSyncActionMethod = typeof(Impl).GetMethod(nameof(ImplSetSyncAction)); + public ReaderWriterLockSlim WriteSyncObject { get; } = new(); public static ReaderWriterLockSlim? ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject; @@ -63,7 +70,7 @@ namespace IPA.Config.Stores { try { - _ = resetEvent.Set(); + SyncAction?.Invoke(); } catch (ObjectDisposedException e) { diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs index 001e0393..514f6bce 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs @@ -430,15 +430,18 @@ namespace IPA.Config.Stores typeBuilder.AddInterfaceImplementation(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_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo)); var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom)); #region IConfigStore.SyncObject - var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncObject), PropertyAttributes.None, IConfigStore_GetSyncObject.ReturnType, null); - var syncObjPropGet = typeBuilder.DefineMethod($"{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.SetSetMethod(syncObjPropSet); typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject); { @@ -449,7 +452,18 @@ namespace IPA.Config.Stores il.Emit(OpCodes.Call, Impl.ImplGetSyncObjectMethod); 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 + #region IConfigStore.WriteSyncObject var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, IConfigStore_GetWriteSyncObject.ReturnType, null); var writeSyncObjPropGet = typeBuilder.DefineMethod($"{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes); From 52e64e591b9d71aecd94a58ee18dc965866046cd Mon Sep 17 00:00:00 2001 From: Arimodu Date: Tue, 19 Sep 2023 22:48:27 +0200 Subject: [PATCH 2/5] Cleanup --- IPA.Loader/Config/ConfigRuntime.cs | 16 +++++----------- .../Stores/GeneratedStoreImpl/IGeneratedStore.cs | 2 -- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/IPA.Loader/Config/ConfigRuntime.cs b/IPA.Loader/Config/ConfigRuntime.cs index 8f8bf640..6033c181 100644 --- a/IPA.Loader/Config/ConfigRuntime.cs +++ b/IPA.Loader/Config/ConfigRuntime.cs @@ -26,13 +26,12 @@ namespace IPA.Config } private static readonly ConcurrentBag configs = new ConcurrentBag(); - private static readonly Action configsChangedWatcher = () => + 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 RequiresSave = new(); private static readonly ConcurrentDictionary watchers = new ConcurrentDictionary(new DirInfoEqComparer()); private static readonly ConcurrentDictionary> watcherTrackConfigs @@ -93,7 +92,7 @@ namespace IPA.Config configs.Add(cfg); } - configsChangedWatcher?.Invoke(); + configsChangedAction.Invoke(); TryStartRuntime(); @@ -102,7 +101,7 @@ namespace IPA.Config public static void ConfigChanged() { - configsChangedWatcher.Invoke(); + configsChangedAction.Invoke(); } private static void AddConfigToWatchers(Config config) @@ -215,16 +214,11 @@ namespace IPA.Config } } - static readonly BlockingCollection RequiresSave = new(); - private static void SaveThread() { try { - var configArr = configs.Where(c => c.Store != null).ToArray(); - - configsChangedWatcher.Invoke(); - + configsChangedAction.Invoke(); foreach (var item in RequiresSave.GetConsumingEnumerable()) { try diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs index 39162cf4..c28e8455 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs @@ -55,11 +55,9 @@ namespace IPA.Config.Stores var impl = FindImpl(s); if (impl != null) impl.SyncAction = value; } - internal static MethodInfo ImplGetSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetSyncObject)); internal static MethodInfo ImplSetSyncActionMethod = typeof(Impl).GetMethod(nameof(ImplSetSyncAction)); - public ReaderWriterLockSlim WriteSyncObject { get; } = new(); public static ReaderWriterLockSlim? ImplGetWriteSyncObject(IGeneratedStore s) => FindImpl(s)?.WriteSyncObject; internal static MethodInfo ImplGetWriteSyncObjectMethod = typeof(Impl).GetMethod(nameof(ImplGetWriteSyncObject)); From f631f0826fb1b72c44e853682242b26d746be57c Mon Sep 17 00:00:00 2001 From: Arimodu Date: Wed, 27 Sep 2023 21:39:36 +0200 Subject: [PATCH 3/5] Avoid breaking change by marking obsolete instead --- IPA.Loader/Config/ConfigRuntime.cs | 70 +++++++++++++++---- IPA.Loader/Config/IConfigStore.cs | 13 ++-- .../GeneratedStoreImpl/IGeneratedStore.cs | 21 +----- 3 files changed, 63 insertions(+), 41 deletions(-) diff --git a/IPA.Loader/Config/ConfigRuntime.cs b/IPA.Loader/Config/ConfigRuntime.cs index 6033c181..609e579a 100644 --- a/IPA.Loader/Config/ConfigRuntime.cs +++ b/IPA.Loader/Config/ConfigRuntime.cs @@ -25,20 +25,17 @@ namespace IPA.Config => obj?.GetHashCode() ?? 0; } - private static readonly ConcurrentBag configs = new ConcurrentBag(); - 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 RequiresSave = new(); + private static readonly ConcurrentBag configs = new(); + private static readonly AutoResetEvent configsChangedWatcher = new(false); + public static readonly BlockingCollection RequiresSave = new(); private static readonly ConcurrentDictionary watchers = new ConcurrentDictionary(new DirInfoEqComparer()); private static readonly ConcurrentDictionary> watcherTrackConfigs = new ConcurrentDictionary>(); - 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() { @@ -55,6 +52,11 @@ namespace IPA.Config saveThread = new Thread(SaveThread); saveThread.Start(); } + if (legacySaveThread == null || !legacySaveThread.IsAlive) + { + legacySaveThread = new Thread(LegacySaveThread); + legacySaveThread.Start(); + } AppDomain.CurrentDomain.ProcessExit -= ShutdownRuntime; AppDomain.CurrentDomain.ProcessExit += ShutdownRuntime; @@ -92,7 +94,7 @@ namespace IPA.Config configs.Add(cfg); } - configsChangedAction.Invoke(); + configsChangedWatcher.Set(); TryStartRuntime(); @@ -101,7 +103,7 @@ namespace IPA.Config public static void ConfigChanged() { - configsChangedAction.Invoke(); + configsChangedWatcher.Set(); } private static void AddConfigToWatchers(Config config) @@ -218,12 +220,11 @@ namespace IPA.Config { try { - configsChangedAction.Invoke(); foreach (var item in RequiresSave.GetConsumingEnumerable()) { try { - item.Invoke(); + Save(configs.First((c) => ReferenceEquals(c.Store.WriteSyncObject, item.WriteSyncObject))); } catch (ThreadAbortException) { @@ -246,5 +247,46 @@ namespace IPA.Config 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 :( + } + } } } diff --git a/IPA.Loader/Config/IConfigStore.cs b/IPA.Loader/Config/IConfigStore.cs index 270773bf..fee24f36 100644 --- a/IPA.Loader/Config/IConfigStore.cs +++ b/IPA.Loader/Config/IConfigStore.cs @@ -1,11 +1,5 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; using System.Threading; -using System.Threading.Tasks; -using static UnityEngine.Random; namespace IPA.Config { @@ -15,11 +9,12 @@ namespace IPA.Config public interface IConfigStore { /// - /// 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. - /// The writer will never signal this. + /// The writer will never signal this handle. /// - public Action SyncAction { get; set; } + [Obsolete("Add a message here...")] // Do this later + WaitHandle SyncObject { get; } /// /// A synchronization object for the load thread and accessors to maintain safe synchronization. diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs index c28e8455..ee8bc0d8 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/IGeneratedStore.cs @@ -47,16 +47,9 @@ namespace IPA.Config.Stores internal static ConstructorInfo Ctor = typeof(Impl).GetConstructor(new[] { typeof(IGeneratedStore) }); 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 ImplSetSyncActionMethod = typeof(Impl).GetMethod(nameof(ImplSetSyncAction)); public ReaderWriterLockSlim WriteSyncObject { get; } = new(); 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 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)); From de70afe5096a75344bbe6330c8d67386b7dd8c4c Mon Sep 17 00:00:00 2001 From: Arimodu Date: Thu, 28 Sep 2023 10:39:00 +0200 Subject: [PATCH 4/5] Revert MakeCreator.cs --- .../Stores/GeneratedStoreImpl/MakeCreator.cs | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs index 514f6bce..001e0393 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/MakeCreator.cs @@ -430,18 +430,15 @@ namespace IPA.Config.Stores typeBuilder.AddInterfaceImplementation(typeof(IConfigStore)); var IConfigStore_t = typeof(IConfigStore); - var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncAction)).GetGetMethod(); - var IConfigStore_SetSyncAction = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncAction)).GetSetMethod(); + var IConfigStore_GetSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.SyncObject)).GetGetMethod(); var IConfigStore_GetWriteSyncObject = IConfigStore_t.GetProperty(nameof(IConfigStore.WriteSyncObject)).GetGetMethod(); var IConfigStore_WriteTo = IConfigStore_t.GetMethod(nameof(IConfigStore.WriteTo)); var IConfigStore_ReadFrom = IConfigStore_t.GetMethod(nameof(IConfigStore.ReadFrom)); #region IConfigStore.SyncObject - 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 }); + var syncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.SyncObject), PropertyAttributes.None, IConfigStore_GetSyncObject.ReturnType, null); + var syncObjPropGet = typeBuilder.DefineMethod($"{nameof(IConfigStore.SyncObject)}", virtualPropertyMethodAttr, syncObjProp.PropertyType, Type.EmptyTypes); syncObjProp.SetGetMethod(syncObjPropGet); - syncObjProp.SetSetMethod(syncObjPropSet); typeBuilder.DefineMethodOverride(syncObjPropGet, IConfigStore_GetSyncObject); { @@ -452,18 +449,7 @@ namespace IPA.Config.Stores il.Emit(OpCodes.Call, Impl.ImplGetSyncObjectMethod); 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 - #region IConfigStore.WriteSyncObject var writeSyncObjProp = typeBuilder.DefineProperty(nameof(IConfigStore.WriteSyncObject), PropertyAttributes.None, IConfigStore_GetWriteSyncObject.ReturnType, null); var writeSyncObjPropGet = typeBuilder.DefineMethod($"{nameof(IConfigStore.WriteSyncObject)}", virtualPropertyMethodAttr, writeSyncObjProp.PropertyType, Type.EmptyTypes); From a9f56d360945290ee78faa86f4b3b3f8434ac233 Mon Sep 17 00:00:00 2001 From: Arimodu Date: Sun, 1 Oct 2023 11:16:26 +0200 Subject: [PATCH 5/5] Add SyncObject notice Null for internally-implemented providers --- IPA.Loader/Config/IConfigStore.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/IPA.Loader/Config/IConfigStore.cs b/IPA.Loader/Config/IConfigStore.cs index fee24f36..912e77f8 100644 --- a/IPA.Loader/Config/IConfigStore.cs +++ b/IPA.Loader/Config/IConfigStore.cs @@ -1,5 +1,4 @@ -using System; -using System.Threading; +using System.Threading; namespace IPA.Config { @@ -12,8 +11,8 @@ namespace IPA.Config /// 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. /// The writer will never signal this handle. + /// This will be null for internally-implemented providers /// - [Obsolete("Add a message here...")] // Do this later WaitHandle SyncObject { get; } ///