Browse Source

Generated config store generation is now thread-safe (will only be created once for each type)

pull/44/head
Anairkoen Schno 4 years ago
parent
commit
3bdc6706d6
1 changed files with 77 additions and 65 deletions
  1. +77
    -65
      IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs

+ 77
- 65
IPA.Loader/Config/Stores/GeneratedStoreImpl/GeneratedStoreImpl.cs View File

@ -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<Type, (GeneratedStoreCreator ctor, Type type)> generatedCreators = new Dictionary<Type, (GeneratedStoreCreator ctor, Type type)>();
public static T Create<T>() 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<T>(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<Type, (ManualResetEventSlim wh, GeneratedStoreCreator ctor, Type type)> generatedCreators
= new ConcurrentDictionary<Type, (ManualResetEventSlim wh, GeneratedStoreCreator ctor, Type type)>();
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<Type, Dictionary<Type, FieldInfo>> TypeRequiredConverters = new Dictionary<Type, Dictionary<Type, FieldInfo>>();
private static void CreateAndInitializeConvertersFor(Type type, IEnumerable<SerializedMemberInfo> structure)
{
if (!TypeRequiredConverters.TryGetValue(type, out var converters))
{
var converterFieldType = Module.DefineType($"{type.FullName}<Converters>",
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<Type, FieldInfo>(uniqueConverterTypes.Length);
foreach (var convType in uniqueConverterTypes)
{
var field = converterFieldType.DefineField($"<converter>_{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<Type, Dictionary<Type, FieldInfo>> TypeRequiredConverters = new Dictionary<Type, Dictionary<Type, FieldInfo>>();
private static void CreateAndInitializeConvertersFor(Type type, IEnumerable<SerializedMemberInfo> structure)
{
if (!TypeRequiredConverters.TryGetValue(type, out var converters))
{
var converterFieldType = Module.DefineType($"{type.FullName}<Converters>",
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<Type, FieldInfo>(uniqueConverterTypes.Length);
foreach (var convType in uniqueConverterTypes)
{
var field = converterFieldType.DefineField($"<converter>_{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];
}
}
}

Loading…
Cancel
Save