Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions src/LightInject.Tests/Issue614.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace LightInject.Tests
{
public class Issue614
{
[Fact]
public void ShouldResolveOpenGenericEnumerablesConcurrentlyWithCanGetInstance()
{
var closingTypes = typeof(string).Assembly.GetExportedTypes()
.Where(t => t.IsClass && !t.IsGenericTypeDefinition && !t.ContainsGenericParameters)
.Take(200)
.ToArray();

for (int iteration = 0; iteration < 20; iteration++)
{
var container = new ServiceContainer(new ContainerOptions { EnableMicrosoftCompatibility = true, EnableVariance = false });
container.Register(typeof(IBehavior<>), typeof(FirstBehavior<>));
container.Register(typeof(IBehavior<>), typeof(SecondBehavior<>));
container.Register(typeof(Handler<>), typeof(Handler<>));

using var barrier = new Barrier(Environment.ProcessorCount * 2);
var tasks = Enumerable.Range(0, Environment.ProcessorCount * 2).Select(taskIndex => Task.Run(() =>
{
barrier.SignalAndWait();
for (int i = taskIndex; i < closingTypes.Length; i += 2)
{
var closingType = closingTypes[i];
if (taskIndex % 2 == 0)
{
container.CanGetInstance(typeof(IEnumerable<>).MakeGenericType(typeof(IBehavior<>).MakeGenericType(closingType)), string.Empty);
}
else
{
var handler = (IHandler)container.GetInstance(typeof(Handler<>).MakeGenericType(closingType));
Assert.NotNull(handler);
}
}
})).ToArray();

Task.WaitAll(tasks);

Check warning on line 46 in src/LightInject.Tests/Issue614.cs

View workflow job for this annotation

GitHub Actions / build

Test methods should not use blocking task operations, as they can cause deadlocks. Use an async test method and await instead. (https://xunit.net/xunit.analyzers/rules/xUnit1031)

Check warning on line 46 in src/LightInject.Tests/Issue614.cs

View workflow job for this annotation

GitHub Actions / build

Test methods should not use blocking task operations, as they can cause deadlocks. Use an async test method and await instead. (https://xunit.net/xunit.analyzers/rules/xUnit1031)
}
}

public interface IBehavior<T>
{
}

public class FirstBehavior<T> : IBehavior<T>
{
}

public class SecondBehavior<T> : IBehavior<T>
{
}

public interface IHandler
{
int BehaviorCount { get; }
}

public class Handler<T> : IHandler
{
public Handler(IEnumerable<IBehavior<T>> behaviors)
{
BehaviorCount = behaviors.Count();
}

public int BehaviorCount { get; }
}
}
}
15 changes: 10 additions & 5 deletions src/LightInject/LightInject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2836,13 +2836,18 @@ public IEnumerable<ServiceRegistration> AvailableServices
/// <inheritdoc/>
public bool CanGetInstance(Type serviceType, string serviceName)
{
if (serviceType.IsFuncRepresentingService() || serviceType.IsFuncRepresentingNamedService() || serviceType.IsFuncWithParameters() || serviceType.IsLazy())
// Resolving the emit method might register services (e.g. closed generics), so we need to
// synchronize with CreateDelegate that reads the same registrations.
lock (lockObject)
{
var returnType = serviceType.GenericTypeArguments.Last();
return GetEmitMethod(returnType, serviceName) != null || availableServices.ContainsKey(serviceType);
}
if (serviceType.IsFuncRepresentingService() || serviceType.IsFuncRepresentingNamedService() || serviceType.IsFuncWithParameters() || serviceType.IsLazy())
{
var returnType = serviceType.GenericTypeArguments.Last();
return GetEmitMethod(returnType, serviceName) != null || availableServices.ContainsKey(serviceType);
}

return GetEmitMethod(serviceType, serviceName) != null;
return GetEmitMethod(serviceType, serviceName) != null;
}
}

/// <inheritdoc/>
Expand Down
Loading