Search code examples
c#.netthread-safetyactivator

Testing Thread Safety With Activator C#


I am working on a little project at home trying to improve my coding skills. I am currently working on building a factory. I was telling some co-workers about the way it was implemented and they had informed me that it could have issues with threading and deadlocks. I created a unit test, but I am unsure if this is valid or would even catch the issue. Here is my setup.

public class InventoryFactory : BaseFactory, IInventoryFactory
{
    public IInventoryEngine Create(InventoryFactoryConfiguration configuration)
    {
        IInventoryRepository repository = BuildInventoryRepository(configuration.RepositoryConfiguration);
        IInventoryService service = BuildInventoryService(configuration.ServiceConfiguration);
        IInventoryEngine engine = BuildInventoryEngine(configuration.EngineConfiguration, repository, service);
        return engine;
    }

    private IInventoryRepository BuildInventoryRepository(FactoryConfigurationModel configuration)
    {
        IInventoryRepository repository =
            base.CreateInstance<IInventoryRepository>(configuration.AssemblyFile, configuration.FullyQualifiedClassName);

        return repository;
    }

    private IInventoryService BuildInventoryService(FactoryConfigurationModel configuration)
    {
        IInventoryService service =
            base.CreateInstance<IInventoryService>(configuration.AssemblyFile, configuration.FullyQualifiedClassName);

        return service;
    }

    private IInventoryEngine BuildInventoryEngine(FactoryConfigurationModel configuration, IInventoryRepository repository, IInventoryService service)
    {
        IInventoryEngine engine =
            base.CreateInstance<IInventoryEngine>(configuration.AssemblyFile, configuration.FullyQualifiedClassName, repository, service);

        return engine;
    }
}

public class BaseFactory
{
    protected T CreateInstance<T>(string assemblyFile, string fullyQualifiedClassName, params object[] arguments)
    {
        Assembly assembly = Assembly.LoadFrom(assemblyFile);
        Type classType = assembly.GetType(fullyQualifiedClassName);
        T instance = (T)Activator.CreateInstance(classType, arguments);
        return instance;
    }
}

This is my current unit test to try seeing if any issues would occur.

[TestMethod]
    public void InventoryFactoryThreadTest()
    {
        Task task1 = Task.Factory.StartNew(() =>
        {
            FactoryConfigurationModel repositoryConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel serviceConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel engineConfiguration = new FactoryConfigurationModel("foo", "bar");
            InventoryFactoryConfiguration factoryConfiguration = new InventoryFactoryConfiguration(repositoryConfiguration, serviceConfiguration, engineConfiguration);
            InventoryFactory factory = new InventoryFactory();

            try
            {
                for (int i = 0; i < 150000; i++)
                {
                    IInventoryEngine engine = factory.Create(factoryConfiguration);
                }
            }
            catch (System.Exception ex)
            {
                Assert.Fail(ex.StackTrace);
            }
        });

        Task task2 = Task.Factory.StartNew(() =>
        {
            FactoryConfigurationModel repositoryConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel serviceConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel engineConfiguration = new FactoryConfigurationModel("foo", "bar");
            InventoryFactoryConfiguration factoryConfiguration = new InventoryFactoryConfiguration(repositoryConfiguration, serviceConfiguration, engineConfiguration);
            InventoryFactory factory = new InventoryFactory();

            try
            {
                for (int i = 0; i < 150000; i++)
                {
                    IInventoryEngine engine = factory.Create(factoryConfiguration);
                }
            }
            catch (System.Exception ex)
            {
                Assert.Fail(ex.StackTrace);
            }
        });

        Task task3 = Task.Factory.StartNew(() =>
        {
            FactoryConfigurationModel repositoryConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel serviceConfiguration = new FactoryConfigurationModel("foo", "bar");
            FactoryConfigurationModel engineConfiguration = new FactoryConfigurationModel("foo", "bar");
            InventoryFactoryConfiguration factoryConfiguration = new InventoryFactoryConfiguration(repositoryConfiguration, serviceConfiguration, engineConfiguration);
            InventoryFactory factory = new InventoryFactory();

            try
            {
                for (int i = 0; i < 150000; i++)
                {
                    IInventoryEngine engine = factory.Create(factoryConfiguration);
                }
            }
            catch (System.Exception ex)
            {
                Assert.Fail(ex.StackTrace);
            }
        });

        Task.WaitAll(task1, task2, task3);
    }

From what I understand, the potential threading issue would be with the Activator.CreateInstance() method. My questions are: Is this a valid test to be able to see if threading is not safe. If this is not, could you tell me why?

Thank you ahead of time!


Solution

  • Calling Activator.CreateInstance is effectively the same as calling new Something(). Unless there's something in the class's constructor doing something really odd that affects some shared state, thread safety and deadlocks won't be a concern.

    Inventing wheels is fun, but I recommend looking at some that have already been invented. Windsor, Autofac, and other Ioc/DI containers are really good at creating object instances, including scanning specified assemblies for interface implementations. You can specify whether you want it to create a new class instance each time or return the same instance over and over. You don't have to call constructors, which makes it easier to create classes with lots of complex nested dependencies. That in turn frees you up to write smaller, more testable classes without having to worry about how you'll construct them. It's awesome. If you're at the point where you see a need to create factories like this then it's probably time to look at IoC containers.

    Also, there's a problem with testing to see if something is thread safe. Multithreading issues are unpredictable so it's hard to rule them out with tests. If something isn't thread safe it could pass all sorts of tests but blow up in another environment. That's not to say that the testing is bad. It's just never 100% conclusive.