public async Task GetRequiredService_BiggerObjectGraphWithOpenGenerics_NoDeadlock()
        {
            // Test is similar to GetRequiredService_UsesSingletonAndLazyLocks_NoDeadlock (but for open generics and a larger object graph)
            using (var mreForThread1 = new ManualResetEvent(false))
                using (var mreForThread2 = new ManualResetEvent(false))
                {
                    // Arrange
                    List <IFakeOpenGenericService <Thing4> > constrainedThing4Services = null;
                    List <IFakeOpenGenericService <Thing5> > constrainedThing5Services = null;

                    Thing3           thing3 = null;
                    IServiceProvider sp     = null;
                    var sb = new StringBuilder();

                    var services = new ServiceCollection();

                    services.AddSingleton <Thing0>();
                    services.AddSingleton <Thing1>();
                    services.AddSingleton <Thing2>();
                    services.AddSingleton <Thing3>();
                    services.AddTransient(typeof(IFakeOpenGenericService <>), typeof(FakeOpenGenericService <>));

                    var lazy = new Lazy <Thing4>(() =>
                    {
                        sb.Append("3");
                        mreForThread2.Set(); // Now that thread 1 holds lazy lock, allow thread 2 to continue

                        thing3 = sp.GetRequiredService <Thing3>();
                        return(new Thing4(thing3));
                    });

                    services.AddTransient(sp =>
                    {
                        if (ThreadId == 2)
                        {
                            sb.Append("1");
                            mreForThread1.Set();     // [b] Allow thread 1 to continue execution and take the lazy lock
                            mreForThread2.WaitOne(); // [c] Wait until thread 1 takes the lazy lock

                            sb.Append("4");
                        }

                        // Let Thread 1 over take Thread 2
                        Thing4 value = lazy.Value;
                        return(value);
                    });
                    services.AddSingleton <Thing5>();

                    sp = services.BuildServiceProvider();

                    // Act
                    var t1 = Task.Run(() =>
                    {
                        ThreadId         = 1;
                        using var scope1 = sp.CreateScope();
                        mreForThread1.WaitOne(); // Waits until thread 2 reaches the transient call to ensure it holds Thing4 singleton lock

                        sb.Append("2");
                        constrainedThing4Services = sp.GetServices <IFakeOpenGenericService <Thing4> >().ToList();
                    });

                    var t2 = Task.Run(() =>
                    {
                        ThreadId                  = 2;
                        using var scope2          = sp.CreateScope();
                        constrainedThing5Services = sp.GetServices <IFakeOpenGenericService <Thing5> >().ToList();
                    });

                    // Act
                    await t1;
                    await t2;

                    Assert.Equal("1234", sb.ToString()); // Expected order of execution

                    var thing4 = sp.GetRequiredService <Thing4>();
                    var thing5 = sp.GetRequiredService <Thing5>();

                    // Assert
                    Assert.NotNull(thing3);
                    Assert.NotNull(thing4);
                    Assert.NotNull(thing5);
                    Assert.Equal(1, constrainedThing4Services.Count);
                    Assert.Equal(1, constrainedThing5Services.Count);
                    Assert.Same(thing4, constrainedThing4Services[0].Value);
                    Assert.Same(thing5, constrainedThing5Services[0].Value);
                }
        }
 public Thing5(Thing4 thing)
 {
 }
Exemplo n.º 3
0
    public void ConcurrentMap()
    {
        var definitions = new MapDefinitionCollection(() => new IMapDefinition[]
        {
            new MapperDefinition1(),
            new MapperDefinition3(),
        });
        var mapper = new UmbracoMapper(definitions, _scopeProvider);

        // the mapper currently has a map from Thing1 to Thing2
        // because Thing3 inherits from Thing1, it will map a Thing3 instance,
        // and register a new map from Thing3 to Thing2,
        // thus modifying its internal dictionaries

        // if timing is good, and mapper does have non-concurrent dictionaries, it fails
        // practically, to reproduce, one needs to add a 1s sleep in the mapper's loop
        // hence, this test is explicit
        var thing3 = new Thing3 {
            Value = "value"
        };
        var       thing4 = new Thing4();
        Exception caught = null;

        void ThreadLoop()
        {
            // keep failing at mapping - and looping through the maps
            for (var i = 0; i < 10; i++)
            {
                try
                {
                    mapper.Map <Thing2>(thing4);
                }
                catch (Exception e)
                {
                    caught = e;
                    Console.WriteLine($"{e.GetType().Name} {e.Message}");
                }
            }

            Console.WriteLine("done");
        }

        var thread = new Thread(ThreadLoop);

        thread.Start();
        Thread.Sleep(1000);

        try
        {
            Console.WriteLine($"{DateTime.Now:O} mapping");
            var thing2 = mapper.Map <Thing2>(thing3);
            Console.WriteLine($"{DateTime.Now:O} mapped");

            Assert.IsNotNull(thing2);
            Assert.AreEqual("value", thing2.Value);
        }
        finally
        {
            thread.Join();
        }
    }