I recently ran into a race condition while accessing a configuration setting. After examining what I could of the code, I came to the conclusion that the
Configuration class' laziness1 was the source of the race condition. Pondering that lead me to me wonder if laziness naturally yields a greater chance of race conditions?
Here's how I came to the initial finding:
Configuration class is implemented as a static class, which means:
The program cannot specify exactly when the class is loaded. However, it is guaranteed to be loaded and to have its fields initialized and its static constructor called before the class is referenced for the first time in your program.
Which, if I read it correctly, is a long way of saying that the class is lazily loaded. I did not find anything referencing ordering of events when using additional resources. i.e. It didn't say when the configuration file (
app.config) would be read.
And while the class itself states the following for thread safety, it's not clear when it actually reads the configuration file.
Any public static members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
The bit about
Any instance members are not guaranteed to be thread safe is particularly confusing since a static class cannot be instantiated.
What I believe occurred is that my program wrote to a section of the
Configuration which was subsequently over-written as the static instance completed it's initialization and reading from the configuration file.
All of that led me to believe that the lazy loading of the static class with its indeterminate reading of the configuration file created the race condition I smacked into.
Which leads me to my broader question of: does laziness within an application naturally yield a greater chance of race conditions?
1 The particular class in question is System.Configuration.Configuration from the .NET framework which is defined as
public static class ConfigurationManager.
For the gory details, this is the abstract version of the code:
//This first line probably isn't required, but I inherited the code and didn't test removing it. System.Configuration.Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); config.ConnectionStrings.ConnectionStrings[DBName].ConnectionString = passedParam; config.Save(); ... ConnectionStringSettings connectionString = ConfigurationManager.ConnectionStrings[DBName];