ninject / Ninject

the ninja of .net dependency injectors
http://ninject.org/
Other
2.67k stars 531 forks source link

Ninject caches an incomplete plan #360

Open Inok opened 5 years ago

Inok commented 5 years ago

I found an issue with Ninject. It caches a new plan before the actual initialization (src\Ninject\Planning\Planner.cs, method CreateNewPlan).

plan = this.CreateEmptyPlan(type);
this.plans.Add(type, plan);
// ThreadAbortException happens here
this.Strategies.Map(s => s.Execute(plan));

If the thread aborts on the first activation of a class right between plan caching and initialization, it leads to the following error on the next activation attempts:

ActivationException: Error activating Foo using self-binding of Foo No constructor was available to create an instance of the implementation type.

In our case, the issue happens with ASP.NET applications when the application was just started and a request was aborted during the first activation of a class. Since we have hundreds of registered classes and hundreds of application instances, the issue happens quite often.

Repro:

void Main()
{
    var rnd = new Random((int)DateTime.UtcNow.Ticks);
    while (true)
    {
        TryReproduce(rnd);
    }
}

void TryReproduce(Random rnd)
{
    var kernel = new StandardKernel();
    kernel.Bind<Dep>().ToSelf();
    kernel.Bind<Foo0>().ToSelf();
    kernel.Bind<Foo1>().ToSelf();
    kernel.Bind<Foo2>().ToSelf();
    kernel.Bind<Foo3>().ToSelf();
    kernel.Bind<Foo4>().ToSelf();

    int wait = 1;

    var t = new System.Threading.Thread(() =>
    {
        Volatile.Write(ref wait, 0);
        kernel.Get<Foo0>();
        kernel.Get<Foo1>();
        kernel.Get<Foo2>();
        kernel.Get<Foo3>();
        kernel.Get<Foo4>();

    });

    t.Start();

    while (Volatile.Read(ref wait) != 0) ;

    Thread.SpinWait(rnd.Next(10000, 500000));

    t.Abort();

    kernel.Get<Foo0>();
    kernel.Get<Foo1>();
    kernel.Get<Foo2>();
    kernel.Get<Foo3>();
    kernel.Get<Foo4>();

    Console.WriteLine("Ok");

}

public class Dep { public Dep() { } }
public class Foo0 { public Foo0(Dep dep) { } }
public class Foo1 { public Foo1(Dep dep) { } }
public class Foo2 { public Foo2(Dep dep) { } }
public class Foo3 { public Foo3(Dep dep) { } }
public class Foo4 { public Foo4(Dep dep) { } }