1

I came across some code like this.

public class ConnectionUtility
{
    private static SqlConnection con;

    public static SqlConnection GimmeConnection()
    {
        if(con==null)
            con = new SqlConnection();
        return con;
    }
}

This is in an ASP.NET web application. Can one expect there to be race conditions where one request/page tries to open/close execute things on the connection and other request tries to do those things as well?

MatthewMartin
  • 32,326
  • 33
  • 105
  • 164
  • 2
    At the very least provide code that actually compiles. Don't make it up. – H H Aug 14 '12 at 13:54
  • 3
    You shouldn't need a single static [`SqlConnection`](http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx), pooling/etc will be handled for you: "When you use the .NET Framework Data Provider for SQL Server, you do not have to enable connection pooling because the provider manages this automatically, although you can modify some settings." – user7116 Aug 14 '12 at 14:03

5 Answers5

4

Yes, a race condition is definitely possible there. Multiple threads might concurrently call GimmeConnection() and all create a new connection, so there is a race condition on the initialization of con.

The correct way to do this in .NET is actually fairly simple:

public class ConnectionUtility
{
    private static SqlConnection con = new SqlConnection();

    public static SqlConnection GimmeConnection()
    {
        return con;
    }
}

This is guaranteed to work, without any race conditions.

Of course, there is also another possible race condition, which is not fixed by this:

Because you create one single globally accessible connection, it is easy for multiple threads to use it concurrently, which is not safe.

If the connection is used by multiple threads, these accesses must be explicitly serialized, for example through locking.

Of course, the best course of action is still to just avoid singletons in the first place...

jalf
  • 243,077
  • 51
  • 345
  • 550
  • I don't understand your answer."This is guaranteed to work without any race conditions" vs "...isn't solved ...which is also a race condition" Are you using the phrase "race condition" to mean the same thing in both cases? – MatthewMartin Aug 14 '12 at 14:02
  • 1
    Yes. But I'm talking about two different race conditions. The one I showed how to fix is that multiple threads might both attempt to initialize `con`. But even when that is fixed, having a singleton SQL connection enables multiple threads to all use the same connection, which is unsafe. Sorry if that was unclear. :) – jalf Aug 14 '12 at 14:04
1

Yes... You shouldn't share a single static connection along multiple threads. This is not only a race condition issue - you will have many web pages trying to use the same connection at the same time which won't work.

You can use the [ThreadStatic] attribute on your "con" which will make it global on thread scope.

Alvaro Rodriguez
  • 2,820
  • 2
  • 33
  • 38
  • That though could tempt one to leave it open, which would make the pooling work poorly. – Jon Hanna Aug 14 '12 at 17:59
  • Why would that make pooling work poorly? IIS reuses threads for web requests too. – Alvaro Rodriguez Aug 14 '12 at 18:04
  • I don't mean thread-pooling, I mean the connection pooling done by SqlConnection. Open and close new connections as quickly as possible, and the internal object that manages the actual connection to a database is quickly returned to the thread-safe pool that the System.Data.SqlClient library maintains internally. – Jon Hanna Aug 14 '12 at 19:11
  • Yes, I know how ado.net connection pooling works. I also agree that the best practice should be to open and close the connection for every web request. However in the case of a web app, I don't think that leaving [ThreadStatic] connections opened should be much of a problem, because they are related to a thread and IIS will reuse those threads. Interesting test case maybe. – Alvaro Rodriguez Aug 14 '12 at 19:24
  • 1
    Oh, I think I get you now; pooled threads limits the total number of threads, hence limiting the total number of database connections? If so, I disagree. If you use thread-static, then you'll soon have one connection per thread, including idle threads. With most applications, that's the theoretical upper limit with connection pooling and in the few that open more than one connection on occasion it won't be much higher. That limit will only be even nearly approached if all threads are concurrently in db-communication, and will die down toward pool minimum after it. – Jon Hanna Aug 14 '12 at 19:33
1

Yes, you can expect such race conditions. Good spotting!

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
1

Two race conditions, one potentially harmless, one dreadful. There's also a logical error.

The potentially harmless one is in the constructor logic of:

if(con==null)
  con = new Thing();

This can be harmless if you want to use a single object as an optimisation, but having a period where more than one is used is merely sub-optimal rather than wrong (not every race is the end of the world). It depends on just what Thing is and how it's used.

The disastrous race is:

return con;

Because in this case the type is SqlConnection, which is not thread-safe, so every single use of the property is a race that is courting disaster.

The logical error is in having a singleton cache an object which is light to produce and which handles its own thread-safe pooling of the heavy part. Because of this pooling, you shouldn't hold on to an SqlConnection any longer than absolutely necessary. Indeed, if you've a gap between one use and another (a bad smell in itself though), you should close it and re-open it again. This makes the thread-safe pooling that SqlConnection provides for you, work at it most optimal between uses.

Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
0

Whenever writing singletons, you should make make them threadsafe:

  class Singleton 
  {
    private Singleton() { }
    private static volatile Singleton instance;

    public static Singleton GetInstance() 
    {
       // DoubleLock
       if (instance == null) 
       {
          lock(m_lock) {  
             if (instance == null) 
             { 
                instance = new Singleton();
             }   
          }
       }
       return instance;
    }

    // helper
    private static object m_lock = new object();
  }
oopbase
  • 11,157
  • 12
  • 40
  • 59
  • You do know that there is a much simpler, safer and less fragile way to do this, yes? You've shown us a nice way to do in 9 fragile and complex lines of code what could be done in one, very simple line. – jalf Aug 14 '12 at 14:05
  • Well this is the way I learned how to implement singletons, but I like learning... Thanks for the comment... – oopbase Aug 14 '12 at 14:09
  • No problem. And sorry if I came across as too snarky. Singletons always make my skin itch, and thread-safe singletons are a nasty minefield to navigate. As far as I'm aware, your implementation is one of the few correct ones, but like I said, it's much more complex than it needs to be. :) – jalf Aug 14 '12 at 14:11
  • It's okay (I saw the comment you deleted ;-)). I didn't know they can be implemented the way you did. But as another reference I will keep my answer the way it is :-). – oopbase Aug 14 '12 at 14:14