From d2e911ce21cf710245e1c54b445ee7affcc6bde3 Mon Sep 17 00:00:00 2001 From: Anairkoen Schno Date: Fri, 3 Apr 2020 22:27:32 -0500 Subject: [PATCH] Generated config store generation is now thread-safe (will only be created once for each type) --- .../GeneratedStoreImpl/GeneratedStoreImpl.cs | 142 ++++++++++-------- 1 file changed, 77 insertions(+), 65 deletions(-) diff --git a/IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs b/IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs index 11add0b6..f820202c 100644 --- a/IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs +++ b/IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs @@ -16,19 +16,18 @@ using Boolean = IPA.Config.Data.Boolean; using System.Collections; using IPA.Utilities; using System.ComponentModel; +using System.Collections.Concurrent; #if NET3 using Net3_Proxy; using Array = Net3_Proxy.Array; #endif - + [assembly: InternalsVisibleTo(IPA.Config.Stores.GeneratedStore.AssemblyVisibilityTarget)] namespace IPA.Config.Stores { internal static partial class GeneratedStoreImpl { - private static readonly Dictionary generatedCreators = new Dictionary(); - public static T Create() where T : class => (T)Create(typeof(T)); public static IConfigStore Create(Type type) => Create(type, null); @@ -39,31 +38,44 @@ namespace IPA.Config.Stores internal static T Create(IGeneratedStore parent) where T : class => (T)Create(typeof(T), parent); private static IConfigStore Create(Type type, IGeneratedStore parent) - => GetCreator(type)(parent); + => GetCreator(type)(parent); + + private static readonly ConcurrentDictionary generatedCreators + = new ConcurrentDictionary(); - internal static GeneratedStoreCreator GetCreator(Type t) - { - if (generatedCreators.TryGetValue(t, out var gen)) - return gen.ctor; - else - { - gen = MakeCreator(t); - generatedCreators.Add(t, gen); - return gen.ctor; - } + private static (GeneratedStoreCreator ctor, Type type) GetCreatorAndGeneratedType(Type t) + { + retry: + if (generatedCreators.TryGetValue(t, out var gen)) + { + if (gen.wh != null) + { + gen.wh.Wait(); + goto retry; // this isn't really a good candidate for a loop + // the loop condition will never be hit, and this should only + // jump back to the beginning in exceptional situations + } + return (gen.ctor, gen.type); + } + else + { + var wh = new ManualResetEventSlim(false); + var cmp = (wh, (GeneratedStoreCreator)null, (Type)null); + if (!generatedCreators.TryAdd(t, cmp)) + goto retry; // someone else beat us to the punch, retry getting their value and wait for them + var (ctor, type) = MakeCreator(t); + while (!generatedCreators.TryUpdate(t, (null, ctor, type), cmp)) + throw new InvalidOperationException("Somehow, multiple MakeCreators started running for the same target type!"); + wh.Set(); + return (ctor, type); + } } + internal static GeneratedStoreCreator GetCreator(Type t) + => GetCreatorAndGeneratedType(t).ctor; + internal static Type GetGeneratedType(Type t) - { - if (generatedCreators.TryGetValue(t, out var gen)) - return gen.type; - else - { - gen = MakeCreator(t); - generatedCreators.Add(t, gen); - return gen.type; - } - } + => GetCreatorAndGeneratedType(t).type; internal const string GeneratedAssemblyName = "IPA.Config.Generated"; @@ -97,47 +109,47 @@ namespace IPA.Config.Stores return module; } - } - - private static readonly Dictionary> TypeRequiredConverters = new Dictionary>(); - private static void CreateAndInitializeConvertersFor(Type type, IEnumerable structure) - { - if (!TypeRequiredConverters.TryGetValue(type, out var converters)) - { - var converterFieldType = Module.DefineType($"{type.FullName}", - TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.AnsiClass); // a static class - - var uniqueConverterTypes = structure.Where(m => m.HasConverter).Select(m => m.Converter).Distinct().ToArray(); - converters = new Dictionary(uniqueConverterTypes.Length); - - foreach (var convType in uniqueConverterTypes) - { - var field = converterFieldType.DefineField($"_{convType}", convType, - FieldAttributes.FamORAssem | FieldAttributes.InitOnly | FieldAttributes.Static); - converters.Add(convType, field); - } - - var cctor = converterFieldType.DefineConstructor(MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes); - { - var il = cctor.GetILGenerator(); - - foreach (var kvp in converters) - { - var typeCtor = kvp.Key.GetConstructor(Type.EmptyTypes); - il.Emit(OpCodes.Newobj, typeCtor); - il.Emit(OpCodes.Stsfld, kvp.Value); - } - - il.Emit(OpCodes.Ret); - } - - TypeRequiredConverters.Add(type, converters); - - converterFieldType.CreateType(); - } - - foreach (var member in structure.Where(m => m.HasConverter)) - member.ConverterField = converters[member.Converter]; + } + + private static readonly Dictionary> TypeRequiredConverters = new Dictionary>(); + private static void CreateAndInitializeConvertersFor(Type type, IEnumerable structure) + { + if (!TypeRequiredConverters.TryGetValue(type, out var converters)) + { + var converterFieldType = Module.DefineType($"{type.FullName}", + TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract | TypeAttributes.AnsiClass); // a static class + + var uniqueConverterTypes = structure.Where(m => m.HasConverter).Select(m => m.Converter).Distinct().ToArray(); + converters = new Dictionary(uniqueConverterTypes.Length); + + foreach (var convType in uniqueConverterTypes) + { + var field = converterFieldType.DefineField($"_{convType}", convType, + FieldAttributes.FamORAssem | FieldAttributes.InitOnly | FieldAttributes.Static); + converters.Add(convType, field); + } + + var cctor = converterFieldType.DefineConstructor(MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes); + { + var il = cctor.GetILGenerator(); + + foreach (var kvp in converters) + { + var typeCtor = kvp.Key.GetConstructor(Type.EmptyTypes); + il.Emit(OpCodes.Newobj, typeCtor); + il.Emit(OpCodes.Stsfld, kvp.Value); + } + + il.Emit(OpCodes.Ret); + } + + TypeRequiredConverters.Add(type, converters); + + converterFieldType.CreateType(); + } + + foreach (var member in structure.Where(m => m.HasConverter)) + member.ConverterField = converters[member.Converter]; } } }