views:

65

answers:

3

The ApplicationException constructor is an instance member so it is not guaranteed to be thread-safe. How do you know if the following code is completely unnecessary? When I say know, I mean is there something in the documentation that says this is unnecessary or have you seen the .NET source code and so you know it is unnecessary?

// thread-safe
internal static class TsApplicationException
{
    private readonly static object myLock = new object();

    internal static void Throw(string msg, Exception e)
    {
        lock (myLock)
        {
            throw new ApplicationException(msg, e);
        } 
        // the implementation of lock() ensures that the unlock will happen
        // even if there is an exception thrown
    }
}
+1  A: 

This is unnecessary because properties within the ApplicationException object aren't going to be changed, and I believe all properies in exception objects are read-only.

Therefore thread-safety is irrelevant.

Russ
Properties in the ApplicationException object are not going to be changed by other threads, but what about .NET properties and Windows properties?
broiyan
What do you mean by ".NET properties and Windows properties"? What's going to happen is the exception is created, and then it will pass up the stack of the thread it was thrown on until it reaches a handler (or ultimately the handler-of-doom that displays an error to the user, if it isn't handled before then). There's nothing happening that isn't relevant solely to that thread (there may be some logging happening if it reaches the EventLog, but the threading of that is EventLog's concern, and one it can handle).
Jon Hanna
By ".NET and Windows properties" I mean variables in the runtime framework and the operating system.
broiyan
Unless you go digging at them in a very determined manner, they'll be fine. Even if you do, you would generally access them in a manner that's wrapped by an API offering further protection to the system. There's no code in your example above that even hints at touching on such matters so this isn't a concern.
Jon Hanna
+2  A: 

Firstly, instance members can be threadsafe. It's just common to make sure ones static members are threadsafe but not provide the same guarantees for instance members for the reasons we discussed previously.

Secondly, the constructor is not being called in a context where more than one thread can access the same instance. The only reference to the newly constructed ApplicationException is, at that point, local to the calling method, and hence only visible to one thread. If two threads both hit the Throw method at the same time, they will have two separate instances created.

Therefore while it is not thread-safe per se, it is not used in a context accessible to multiple threads, and hence there is no need to lock on it.

A more important case is:

void MyFunctionOfThreadSafety(string someStr, int someInt, bool someBool)
{
  if(someBool)// №1
  {
    var dict = new Dictionary<string, int>();
    dict[someStr] = someInt; // №2
    int test = 0;
    if(!dict.TryGetValue(someStr, out test))// №3
      throw new Exception("really unexpected");
    dict[someStr] = test + someInt; // №4
  }
}

In this code, the lines commented with №1 through №4 are places where if the objects in question where accessible by more than one thread, then thread-safety issues could cause problems (indeed, all but №1 offer more than one point at which threads could switch and things start being weird).

This code though is entirely thread-safe. Although it is doing several potentially unsafe things with objects, not a single one of those objects is changeable by another thread.

someInt and someBool are value-type parameters that were not passed byref or out. They therefore only exist in the context of that method, (and methods it calls if it passes them on byref or out).

someStr is a reference type passed as a parameter, which means it could potentially be also stored somewhere that another thread can get at it. However, because it is immutable, there is no danger of another thread writing to it (since no thread can write to it at all). Reads do not have to be protected from reads, just from writes (though when you do have reads and writes you may have to lock both the read and the write). Since there can only be read operations on someStr it's safe.

dict and test are created in this method, so they are safe. If dict were returned from the method then as a mutable reference type it could, if stored somewhere visible to multiple threads, become a thread-safety concern. But (a) it has not been and (b) it won't be a concern until after this has happened; here it is thread-safe no matter what.

Jon Hanna
Thanks Jon, I appreciate these points but I guess here I see the risk being in code that I did not write, that is the runtime framework or operating system code.
broiyan
You're managing to fire up a browser, possibly a few other applications going (mail client in case someone has sent you a mail, IDE for writing your example for your question, maybe get winamp going so you can listen to some tunes in the meantime, and an IM so you can chat to friends and lovers at the same time) and write your question, and then get back to the page to read the response. Think about how many threads are involved in that (I've got over 800 going between 4 cores right now).While multithreading can be hard, people do get it right. Part of the toolset given to us to do so...
Jon Hanna
"Unless you go digging at them in a very determined manner, they'll be fine. Even if you do, you would generally access them in a manner that's wrapped by an API offering further protection to the system. There's no code in your example above that even hints at touching on such matters so this isn't a concern."I accept this as the answer, thanks.
broiyan
... is that we are kept separated from each others concerns (this happens at a variety of levels and for other reasons besides this one). As long as the framework and system hasn't done anything really bad (it does happen, but not that often) and you don't do anything to deliberately mess with them (and that's not easy) you just need to worry about your own code.
Jon Hanna
I trust that the application programmer need only concern himself with his/her own application. My question was in regard to the risks of the runtime framework only in respect to my own application. Thanks.
broiyan
A: 

The only time constructors are not thread-safe is when they alter static data in their class. This is extremely rare, so the locking is unnecessary. Indeed, you are only locking your usage of the constructor, so any other thread not running in TsApplicationException can throw an ApplicationException simultaneously anyway.

Gabe
Yes, what I was proposing is universal usage of TsApplicationException.Throw.Thanks for the answer.
broiyan
Worth adding, that if a construct alters static data, and doesn't handle the thread safety itself, it really is something that needs justification as well as documentation (i.e., if there was a good reason for it, explain it, as otherwise it'll just look like the constructor was really bad code).
Jon Hanna