Browse Source

Fix thread-safety issue causing infinite loops in TypeHelper cache (#20113)

* Initial plan

* Fix thread-safety issue by replacing Dictionary with ConcurrentDictionary

Co-authored-by: grokys <1775141+grokys@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: grokys <1775141+grokys@users.noreply.github.com>
fixes/effect-clip-rect
Copilot 2 months ago
committed by GitHub
parent
commit
406925a344
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      src/Avalonia.Remote.Protocol/MetsysBson.cs
  2. 42
      tests/Avalonia.DesignerSupport.Tests/RemoteProtocolTests.cs

11
src/Avalonia.Remote.Protocol/MetsysBson.cs

@ -30,6 +30,7 @@ Code imported from https://github.com/elaberge/Metsys.Bson without any changes
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
@ -695,7 +696,7 @@ namespace Metsys.Bson
[UnconditionalSuppressMessage("Trimming", "IL3050", Justification = "Bson uses reflection")]
internal class TypeHelper
{
private static readonly IDictionary<Type, TypeHelper> _cachedTypeLookup = new Dictionary<Type, TypeHelper>();
private static readonly ConcurrentDictionary<Type, TypeHelper> _cachedTypeLookup = new ConcurrentDictionary<Type, TypeHelper>();
private static readonly BsonConfiguration _configuration = BsonConfiguration.Instance;
private readonly IDictionary<string, MagicProperty> _properties;
@ -725,13 +726,7 @@ namespace Metsys.Bson
public static TypeHelper GetHelperForType(Type type)
{
TypeHelper helper;
if (!_cachedTypeLookup.TryGetValue(type, out helper))
{
helper = new TypeHelper(type);
_cachedTypeLookup[type] = helper;
}
return helper;
return _cachedTypeLookup.GetOrAdd(type, t => new TypeHelper(t));
}
public static string FindProperty(LambdaExpression lambdaExpression)

42
tests/Avalonia.DesignerSupport.Tests/RemoteProtocolTests.cs

@ -162,6 +162,48 @@ namespace Avalonia.DesignerSupport.Tests
}
[Fact]
[SuppressMessage("Usage", "xUnit1031:Do not use blocking task operations in test method", Justification = "Sync context is explicitly disabled")]
void BsonSerializationIsThreadSafe()
{
Init();
// This test verifies that concurrent serialization doesn't cause infinite loops
// or corruption in the TypeHelper cache
var messages = Enumerable.Range(0, 100).Select(i => new MeasureViewportMessage
{
Width = i,
Height = i * 2
}).ToArray();
var tasks = new List<Task>();
var exceptions = new ConcurrentBag<Exception>();
// Spawn multiple threads that all try to serialize messages concurrently
for (int i = 0; i < 10; i++)
{
var task = Task.Run(() =>
{
try
{
foreach (var message in messages)
{
_client.Send(message).Wait(TimeoutInMs);
}
}
catch (Exception ex)
{
exceptions.Add(ex);
}
});
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray(), TimeoutInMs * messages.Length * 10);
// Verify no exceptions occurred
Assert.Empty(exceptions);
}
public void Dispose()
{
_disposables.ForEach(d => d.Dispose());

Loading…
Cancel
Save