Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce contention for Datacontract Serialization #70668

Closed
Original file line number Diff line number Diff line change
Expand Up @@ -345,15 +345,14 @@ internal virtual bool IsValidContract(SerializationMode mode)

internal class DataContractCriticalHelper
{
private static readonly Dictionary<TypeHandleRef, IntRef> s_typeToIDCache = new Dictionary<TypeHandleRef, IntRef>(new TypeHandleRefEqualityComparer());
private static readonly ConcurrentDictionary<RuntimeTypeHandle, int> s_typeToIDCache = new();
private static DataContract[] s_dataContractCache = new DataContract[32];
private static int s_dataContractID;
private static Dictionary<Type, DataContract?>? s_typeToBuiltInContract;
private static readonly ConcurrentDictionary<Type, DataContract?> s_typeToBuiltInContract = new();
private static Dictionary<XmlQualifiedName, DataContract?>? s_nameToBuiltInContract;
private static Dictionary<string, string>? s_namespaces;
private static Dictionary<string, XmlDictionaryString>? s_clrTypeStrings;
private static XmlDictionary? s_clrTypeStringsDictionary;
private static readonly TypeHandleRef s_typeHandleRef = new TypeHandleRef();

private static readonly object s_cacheLock = new object();
private static readonly object s_createDataContractLock = new object();
Expand Down Expand Up @@ -441,35 +440,35 @@ private static bool ContractMatches(DataContract contract, DataContract cachedCo

internal static int GetId(RuntimeTypeHandle typeHandle)
{
lock (s_cacheLock)
typeHandle = GetDataContractAdapterTypeHandle(typeHandle);

if (s_typeToIDCache.TryGetValue(typeHandle, out int id))
return id;

try
{
IntRef? id;
typeHandle = GetDataContractAdapterTypeHandle(typeHandle);
s_typeHandleRef.Value = typeHandle;
if (!s_typeToIDCache.TryGetValue(s_typeHandleRef, out id))
lock (s_cacheLock)
{
int value = s_dataContractID++;
if (value >= s_dataContractCache.Length)
return s_typeToIDCache.GetOrAdd(typeHandle, static _ =>
{
int newSize = (value < int.MaxValue / 2) ? value * 2 : int.MaxValue;
if (newSize <= value)
int nextId = s_dataContractID++;
if (nextId >= s_dataContractCache.Length)
{
DiagnosticUtility.DebugAssert("DataContract cache overflow");
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.DataContractCacheOverflow));
int newSize = (nextId < int.MaxValue / 2) ? nextId * 2 : int.MaxValue;
if (newSize <= nextId)
{
DiagnosticUtility.DebugAssert("DataContract cache overflow");
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SerializationException(SR.DataContractCacheOverflow));
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
Array.Resize<DataContract>(ref s_dataContractCache, newSize);
}
id = new IntRef(value);
try
{
s_typeToIDCache.Add(new TypeHandleRef(typeHandle), id);
}
catch (Exception ex)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex);
}
return nextId;
});
}
return id.Value;
}
catch (Exception ex)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex);
}
}

Expand Down Expand Up @@ -634,18 +633,11 @@ private static RuntimeTypeHandle GetDataContractAdapterTypeHandle(RuntimeTypeHan
if (type.IsInterface && !CollectionDataContract.IsCollectionInterface(type))
type = Globals.TypeOfObject;

lock (s_initBuiltInContractsLock)
return s_typeToBuiltInContract.GetOrAdd(type, static (Type key) =>
{
s_typeToBuiltInContract ??= new Dictionary<Type, DataContract?>();

DataContract? dataContract;
if (!s_typeToBuiltInContract.TryGetValue(type, out dataContract))
{
TryCreateBuiltInDataContract(type, out dataContract);
s_typeToBuiltInContract.Add(type, dataContract);
}
TryCreateBuiltInDataContract(key, out DataContract? dataContract);
return dataContract;
}
});
}

[RequiresUnreferencedCode(DataContract.SerializerTrimmerWarning)]
Expand Down Expand Up @@ -1009,17 +1001,14 @@ internal static void ThrowInvalidDataContractException(string? message, Type? ty
{
if (type != null)
{
lock (s_cacheLock)
RuntimeTypeHandle typeHandle = GetDataContractAdapterTypeHandle(type.TypeHandle);
try
{
s_typeHandleRef.Value = GetDataContractAdapterTypeHandle(type.TypeHandle);
try
{
s_typeToIDCache.Remove(s_typeHandleRef);
}
catch (Exception ex)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex);
}
s_typeToIDCache.TryRemove(typeHandle, out _);
}
catch (Exception ex)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperFatal(ex.Message, ex);
}
}

Expand Down