Multithread SerializationManager initialize.

This commit is contained in:
Pieter-Jan Briers
2021-09-01 13:38:34 +02:00
parent 74eea847e2
commit 0fd210481a
17 changed files with 161 additions and 110 deletions

View File

@@ -20,7 +20,7 @@ namespace Robust.Client.ResourceManagement
{
var sawmill = _logManager.GetSawmill("res.preload");
if (!_configurationManager.GetCVar(CVars.TexturePreloadingEnabled))
if (!_configurationManager.GetCVar(CVars.ResTexturePreloadingEnabled))
{
sawmill.Debug($"Skipping texture preloading due to CVar value.");
return;

View File

@@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="DiscordRichPresence" Version="1.0.175" />
<PackageReference Include="JetBrains.Annotations" Version="2020.3.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.Data.Sqlite" Version="5.0.3" />
<PackageReference Include="nfluidsynth" Version="0.3.1" />
<PackageReference Include="NVorbis" Version="0.10.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />

View File

@@ -518,17 +518,6 @@ namespace Robust.Shared
public static readonly CVarDef<float> AngularSlop =
CVarDef.Create("physics.angularslop", 2.0f / 180.0f * MathF.PI);
/// <summary>
/// The radius of the polygon/edge shape skin. This should not be modified. Making
/// this smaller means polygons will have an insufficient buffer for continuous collision.
/// Making it larger may create artifacts for vertex collision.
/// </summary>
/// <remarks>
/// Default is set to be 2 x linearslop. TODO Should we listen to linearslop changes?
/// </remarks>
public static readonly CVarDef<float> PolygonRadius =
CVarDef.Create("physics.polygonradius", 2 * 0.005f);
/// <summary>
/// If true, it will run a GiftWrap convex hull on all polygon inputs.
/// This makes for a more stable engine when given random input,
@@ -583,9 +572,12 @@ namespace Robust.Shared
public static readonly CVarDef<bool> ResCheckPathCasing =
CVarDef.Create("res.checkpathcasing", false);
public static readonly CVarDef<bool> TexturePreloadingEnabled =
public static readonly CVarDef<bool> ResTexturePreloadingEnabled =
CVarDef.Create("res.texturepreloadingenabled", true, CVar.CLIENTONLY);
public static readonly CVarDef<bool> ResTexturePreloadCache =
CVarDef.Create("res.texture_preload_cache", true, CVar.CLIENTONLY);
/*
* DEBUG

View File

@@ -349,7 +349,7 @@ namespace Robust.Shared.Physics.Collision
internal EPCollider(IConfigurationManager configManager)
{
_polygonRadius = configManager.GetCVar(CVars.PolygonRadius);
_polygonRadius = PhysicsConstants.PolygonRadius;
_angularSlop = configManager.GetCVar(CVars.AngularSlop);
_polygonB = new TempPolygon(configManager);
}

View File

@@ -92,7 +92,7 @@ namespace Robust.Shared.Physics.Collision.Shapes
public EdgeShape(Vector2 start, Vector2 end)
{
Set(start, end);
_radius = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.PolygonRadius);
_radius = PhysicsConstants.PolygonRadius;
}
/// <summary>

View File

@@ -69,7 +69,7 @@ namespace Robust.Shared.Physics.Collision.Shapes
public PhysShapeAabb()
{
_radius = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.PolygonRadius);
_radius = PhysicsConstants.PolygonRadius;
}
public Box2 ComputeAABB(Transform transform, int childIndex)

View File

@@ -153,7 +153,7 @@ namespace Robust.Shared.Physics.Collision.Shapes
public PolygonShape()
{
_radius = IoCManager.Resolve<IConfigurationManager>().GetCVar(CVars.PolygonRadius);
_radius = PhysicsConstants.PolygonRadius;
}
public PolygonShape(float radius)

View File

@@ -0,0 +1,15 @@
namespace Robust.Shared.Physics
{
internal static class PhysicsConstants
{
/// <summary>
/// The radius of the polygon/edge shape skin. This should not be modified. Making
/// this smaller means polygons will have an insufficient buffer for continuous collision.
/// Making it larger may create artifacts for vertex collision.
/// </summary>
/// <remarks>
/// Default is set to be 2 x linearslop. TODO Should we listen to linearslop changes?
/// </remarks>
public const float PolygonRadius = 2 * 0.005f;
}
}

View File

@@ -6,10 +6,10 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
namespace Robust.Shared.Prototypes
@@ -18,9 +18,9 @@ namespace Robust.Shared.Prototypes
/// Prototype that represents game entities.
/// </summary>
[Prototype("entity", -1)]
public class EntityPrototype : IPrototype, IInheritingPrototype
public class EntityPrototype : IPrototype, IInheritingPrototype, ISerializationHooks
{
private readonly ILocalizationManager _loc = IoCManager.Resolve<ILocalizationManager>();
private ILocalizationManager _loc = default!;
private static readonly Dictionary<string, string> LocPropertiesDefault = new();
@@ -155,6 +155,11 @@ namespace Robust.Shared.Prototypes
Components.Add("MetaData", new MetaDataComponent());
}
void ISerializationHooks.AfterDeserialization()
{
_loc = IoCManager.Resolve<ILocalizationManager>();
}
public bool TryGetComponent<T>(string name, [NotNullWhen(true)] out T? component) where T : IComponent
{
if (!Components.TryGetValue(name, out var componentUnCast))

View File

@@ -193,7 +193,7 @@ namespace Robust.Shared.Prototypes
}
}
public class PrototypeManager : IPrototypeManager, IPostInjectInit
public class PrototypeManager : IPrototypeManager
{
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] protected readonly IResourceManager Resources = default!;
@@ -223,6 +223,7 @@ namespace Robust.Shared.Prototypes
}
_initialized = true;
ReloadPrototypeTypes();
}
public IEnumerable<T> EnumeratePrototypes<T>() where T : class, IPrototype
@@ -605,12 +606,6 @@ namespace Robust.Shared.Prototypes
#endregion IPrototypeManager members
public void PostInject()
{
_reflectionManager.OnAssemblyAdded += (_, _) => ReloadPrototypeTypes();
ReloadPrototypeTypes();
}
private void ReloadPrototypeTypes()
{
Clear();

View File

@@ -111,5 +111,6 @@ namespace Robust.Shared.Reflection
bool TryParseEnumReference(string reference, [NotNullWhen(true)] out Enum? @enum);
Type? YamlTypeTagLookup(Type baseType, string typeName);
IEnumerable<Type> FindAllTypes();
}
}
}

View File

@@ -33,6 +33,8 @@ namespace Robust.Shared.Reflection
private readonly Dictionary<string, Enum> _enumCache = new();
private readonly List<Type> _getAllTypesCache = new();
/// <inheritdoc />
public IEnumerable<Type> GetAllChildren<T>(bool inclusive = false)
{
@@ -42,56 +44,47 @@ namespace Robust.Shared.Reflection
/// <inheritdoc />
public IEnumerable<Type> GetAllChildren(Type baseType, bool inclusive = false)
{
var typeLists = new List<Type[]>(Assemblies.Count);
try
{
// There's very little assemblies, so storing these temporarily is cheap.
// We need to do it ahead of time so that we can catch ReflectionTypeLoadException HERE,
// so whoever is using us doesn't have to handle them.
foreach (var assembly in Assemblies)
{
typeLists.Add(assembly.GetTypes());
}
}
catch (ReflectionTypeLoadException e)
{
Logger.Error("Caught ReflectionTypeLoadException! Dumping child exceptions:");
if (e.LoaderExceptions != null)
{
foreach (var inner in e.LoaderExceptions)
{
if (inner != null)
{
Logger.Error(inner.ToString());
}
}
}
EnsureGetAllTypesCache();
throw;
foreach (var type in _getAllTypesCache)
{
if (!baseType.IsAssignableFrom(type) || type.IsAbstract)
continue;
if (baseType == type && !inclusive)
continue;
yield return type;
}
}
private void EnsureGetAllTypesCache()
{
if (_getAllTypesCache.Count != 0)
return;
var totalLength = 0;
var typeSets = new List<Type[]>();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
typeSets.Add(types);
totalLength += types.Length;
}
foreach (var t in typeLists)
{
foreach (var type in t)
{
if (!baseType.IsAssignableFrom(type) || type.IsAbstract)
{
continue;
}
_getAllTypesCache.Capacity = totalLength;
foreach (var typeSet in typeSets)
{
foreach (var type in typeSet)
{
var attribute = (ReflectAttribute?) Attribute.GetCustomAttribute(type, typeof(ReflectAttribute));
if (!(attribute?.Discoverable ?? ReflectAttribute.DEFAULT_DISCOVERABLE))
{
continue;
}
if (baseType == type && !inclusive)
{
continue;
}
yield return type;
_getAllTypesCache.Add(type);
}
}
}
@@ -101,6 +94,7 @@ namespace Robust.Shared.Reflection
public void LoadAssemblies(IEnumerable<Assembly> assemblies)
{
this.assemblies.AddRange(assemblies);
_getAllTypesCache.Clear();
OnAssemblyAdded?.Invoke(this, new ReflectionUpdateEventArgs(this));
}
@@ -167,14 +161,14 @@ namespace Robust.Shared.Reflection
/// <inheritdoc />
public IEnumerable<Type> FindTypesWithAttribute(Type attributeType)
{
var types = new List<Type>();
EnsureGetAllTypesCache();
return _getAllTypesCache.Where(type => Attribute.IsDefined(type, attributeType));
}
foreach (var assembly in Assemblies)
{
types.AddRange(assembly.GetTypes().Where(type => Attribute.IsDefined(type, attributeType)));
}
return types;
public IEnumerable<Type> FindAllTypes()
{
EnsureGetAllTypesCache();
return _getAllTypesCache;
}
/// <inheritdoc />

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -12,9 +13,9 @@ namespace Robust.Shared.Serialization.Manager
private readonly Dictionary<Type, Type> _constantsMapping = new();
private void InitializeFlagsAndConstants()
private void InitializeFlagsAndConstants(IEnumerable<Type> flags, IEnumerable<Type> constants)
{
foreach (Type constType in _reflectionManager.FindTypesWithAttribute<ConstantsForAttribute>())
foreach (Type constType in constants)
{
if (!constType.IsEnum)
{
@@ -37,7 +38,7 @@ namespace Robust.Shared.Serialization.Manager
}
}
foreach (var bitflagType in _reflectionManager.FindTypesWithAttribute<FlagsForAttribute>())
foreach (var bitflagType in flags)
{
if (!bitflagType.IsEnum)
{

View File

@@ -14,9 +14,9 @@ namespace Robust.Shared.Serialization.Manager
private readonly Dictionary<Type, Type> _genericCopierTypes = new();
private readonly Dictionary<(Type Type, Type DataNodeType), Type> _genericValidatorTypes = new();
private void InitializeTypeSerializers()
private void InitializeTypeSerializers(IEnumerable<Type> typeSerializers)
{
foreach (var type in _reflectionManager.FindTypesWithAttribute<TypeSerializerAttribute>())
foreach (var type in typeSerializers)
{
RegisterSerializer(type);
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
@@ -6,6 +7,7 @@ using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Robust.Shared.IoC;
using Robust.Shared.Log;
@@ -41,59 +43,79 @@ namespace Robust.Shared.Serialization.Manager
public void Initialize()
{
if (_initializing)
{
throw new InvalidOperationException($"{nameof(SerializationManager)} is already being initialized.");
}
if (_initialized)
{
throw new InvalidOperationException($"{nameof(SerializationManager)} has already been initialized.");
}
_initializing = true;
DependencyCollection = IoCManager.Instance ?? throw new NullReferenceException();
InitializeFlagsAndConstants();
InitializeTypeSerializers();
var flagsTypes = new ConcurrentBag<Type>();
var constantsTypes = new ConcurrentBag<Type>();
var typeSerializers = new ConcurrentBag<Type>();
var meansDataDef = new ConcurrentBag<Type>();
var implicitDataDefForInheritors = new ConcurrentBag<Type>();
var registrations = new HashSet<Type>();
CollectAttributedTypes(flagsTypes, constantsTypes, typeSerializers, meansDataDef, implicitDataDefForInheritors);
foreach (var baseType in _reflectionManager.FindTypesWithAttribute<ImplicitDataDefinitionForInheritorsAttribute>())
InitializeFlagsAndConstants(flagsTypes, constantsTypes);
InitializeTypeSerializers(typeSerializers);
// This is a bag, not a hash set.
// Duplicates are fine since the CWT<,> won't re-run the constructor if it's already in there.
var registrations = new ConcurrentBag<Type>();
foreach (var baseType in implicitDataDefForInheritors)
{
if (!baseType.IsAbstract && !baseType.IsInterface && !baseType.IsGenericTypeDefinition) registrations.Add(baseType);
foreach (var child in _reflectionManager.GetAllChildren(baseType))
// Inherited attributes don't work with interfaces.
if (baseType.IsInterface)
{
if (child.IsAbstract || child.IsInterface || child.IsGenericTypeDefinition) continue;
registrations.Add(child);
foreach (var child in _reflectionManager.GetAllChildren(baseType))
{
if (child.IsAbstract || child.IsGenericTypeDefinition)
continue;
registrations.Add(child);
}
}
else if (!baseType.IsAbstract && !baseType.IsGenericTypeDefinition)
{
registrations.Add(baseType);
}
}
foreach (var meansAttr in _reflectionManager.FindTypesWithAttribute<MeansDataDefinitionAttribute>())
Parallel.ForEach(_reflectionManager.FindAllTypes(), type =>
{
foreach (var type in _reflectionManager.FindTypesWithAttribute(meansAttr))
foreach (var meansDataDefAttr in meansDataDef)
{
registrations.Add(type);
if (type.IsDefined(meansDataDefAttr))
registrations.Add(type);
}
}
});
foreach (var type in registrations)
var sawmill = Logger.GetSawmill(LogCategory);
Parallel.ForEach(registrations, type =>
{
if (type.IsAbstract || type.IsInterface || type.IsGenericTypeDefinition)
{
Logger.DebugS(LogCategory, $"Skipping registering data definition for type {type} since it is abstract or an interface");
continue;
sawmill.Debug(
$"Skipping registering data definition for type {type} since it is abstract or an interface");
return;
}
if (!type.IsValueType && type.GetConstructors(BindingFlags.Instance | BindingFlags.Public)
.FirstOrDefault(m => m.GetParameters().Length == 0) == null)
{
Logger.DebugS(LogCategory, $"Skipping registering data definition for type {type} since it has no parameterless ctor");
continue;
sawmill.Debug(
$"Skipping registering data definition for type {type} since it has no parameterless ctor");
return;
}
DataDefinitions.GetValue(type, CreateDefinitionCallback);
}
});
var error = new StringBuilder();
@@ -110,17 +132,42 @@ namespace Robust.Shared.Serialization.Manager
throw new ArgumentException($"Duplicate data field tags found in:\n{error}");
}
foreach (var type in _reflectionManager.FindTypesWithAttribute<CopyByRefAttribute>())
{
_copyByRefRegistrations.Add(type);
}
_copyByRefRegistrations.Add(typeof(Type));
_initialized = true;
_initializing = false;
}
private void CollectAttributedTypes(
ConcurrentBag<Type> flagsTypes,
ConcurrentBag<Type> constantsTypes,
ConcurrentBag<Type> typeSerializers,
ConcurrentBag<Type> meansDataDef,
ConcurrentBag<Type> implicitDataDefForInheritors)
{
// IsDefined is extremely slow. Great.
Parallel.ForEach(_reflectionManager.FindAllTypes(), type =>
{
if (type.IsDefined(typeof(FlagsForAttribute), false))
flagsTypes.Add(type);
if (type.IsDefined(typeof(ConstantsForAttribute), false))
constantsTypes.Add(type);
if (type.IsDefined(typeof(TypeSerializerAttribute)))
typeSerializers.Add(type);
if (type.IsDefined(typeof(MeansDataDefinitionAttribute)))
meansDataDef.Add(type);
if (type.IsDefined(typeof(ImplicitDataDefinitionForInheritorsAttribute), true))
implicitDataDefForInheritors.Add(type);
if (type.IsDefined(typeof(CopyByRefAttribute)))
_copyByRefRegistrations.Add(type);
});
}
private static readonly ConditionalWeakTable<Type, DataDefinition>.CreateValueCallback
CreateDefinitionCallback = CreateDataDefinition;

View File

@@ -13,7 +13,7 @@ using Robust.Shared.Serialization.TypeSerializers.Interfaces;
namespace Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary
{
[TypeSerializer]
// [TypeSerializer]
public class PrototypeIdDictionarySerializer<TValue, TPrototype> :
ITypeSerializer<Dictionary<string, TValue>, MappingDataNode>,
ITypeSerializer<SortedDictionary<string, TValue>, MappingDataNode>,

View File

@@ -590,7 +590,7 @@ namespace Robust.UnitTesting
(CVars.DiscordEnabled.Name, "false"),
// Avoid preloading textures.
(CVars.TexturePreloadingEnabled.Name, "false"),
(CVars.ResTexturePreloadingEnabled.Name, "false"),
});
GameLoop = new IntegrationGameLoop(DependencyCollection.Resolve<IGameTiming>(),