-
-
Notifications
You must be signed in to change notification settings - Fork 124
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
DryIoc Resolve with decorators goes wrong for parallel execution #116
Comments
Thanks for the detailed explanation! |
added: ImTools.Map with additional S state argument to prevent map lambda closure allocations added: Test for #116
Found (hopefully) a root cause, related to the cache of generated closed-generic factories. Good opportunity to get rid off this cache. |
…ndard 2.0 wip: fix for #116 added: initial thingy for https://stackoverflow.com/questions/55749917/decorator-not-being-constrained-correctly
added: ImTools.Map with additional S state argument to prevent map lambda closure allocations added: Test for dadhi#116
…T Standard 2.0 wip: fix for dadhi#116 added: initial thingy for https://stackoverflow.com/questions/55749917/decorator-not-being-constrained-correctly
Hi. It looks like this is still/again the case with DryIoc 5.3.2 (and 5.3.4) on .NET 4.8. My scenario: using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using DryIoc;
using NUnit.Framework;
namespace YadaYada.Tests
{
public class ContainerTests
{
interface/*abstract class*/ IQuery<T> { }
class Query<T> : IQuery<T> { };
class QueryDecorator<T> : IQuery<T>
{
public QueryDecorator(IQuery<T> decoratee) { }
}
[Test, Repeat(10)]
public async Task Resolve_WhenCallInParallelToResolveScopedDecoratedService_FailsToResolveDecorator()
{
var container = new Container(rules => rules.WithDefaultReuse(Reuse.InWebRequest));
container.Register(typeof(IQuery<string>), typeof(Query<string>));
container.Register(typeof(IQuery<string>), typeof(QueryDecorator<string>), setup: Setup.Decorator);
IQuery<string> ResolveInScope()
{
using (var scope = container.OpenScope(Reuse.WebRequestScopeName))
{
return scope.Resolve<IQuery<string>>();
}
}
IEnumerable<Task<IQuery<string>>> GetResolveTasks()
{
while (true)
{
yield return Task.Run(() => ResolveInScope());
}
}
var tasks = GetResolveTasks().Take(10).ToArray();
await Task.WhenAll(tasks);
foreach (var task in tasks)
{
Assert.That(task.Result, Is.InstanceOf<QueryDecorator<string>>());
}
}
}
} With VS "Running Until Failure" produces error right away. Advice on workaround for time being would be welcome. |
@Madajevas Thanks for the test. Did it reproduce for you before 5.3.2? |
It seems to be working with 4.8.8 (completed 200+ iterations successfully. With v5.0.0 it takes up to 10 to fail). This is still not conclusive. Will test further and post if results changes. |
I was able to reproduce the issue on my machine, it would not always fail within 10 iterations, but with more iterations I can reproduce it constantly, seems to be some kind of tight race condition. |
…prove; optimize size of the Resgitry by splitting the Keyed services and Registry change to separate objects
[Tested on DryIoc 4.0.3 against .NET Framework 4.7.2]
I observed strange behavior of DryIoc Resolve in parallel execution when decorators are being used: At some point the decorator will resolve itself instead of the object it decorates.
Test classes
I have an interface
IQuery<TRequest,TResponse>
and an implementation of itMyQuery
. There's also a decoratorQueryDecorator<TRequest, TResponse>
implementing the same interface.Note the
if (query.GetType() == this.GetType()) throw new Exception...
in the constructor of the decorator. Obviously that's not something that should be there in production code but it will make the code crash and fail the test further down. The code will only throw an exception ifquery
is resolved to the same type as the decorator itself (which should not happen).The types are registered as follows:
Test methods
✅ Sequential execution
First an example that works fine: When resolving sequentially everything works as expected. Don't mind the idiotic task based approach - it's just to have exactly the same code as the parallel example.
🔴 Parallel execution
Here's where things go wrong. The test fails and in my opinion it shouldn't.
IMPORTANT: When the parallel execution test is ran together with the other 2 tests, success or failure depends on the order of execution. If the parallel test is not the first, it may actually succeed. So it's best to run this test standalone if you can't reproduce it with the other tests present.
✅ Sequential then parallel execution
If the object is resolved once before, further parallel execution will resolve the object correctly. The test case below executes just fine. So there's no reason to assume the previous test case shouldn't work.
The text was updated successfully, but these errors were encountered: