views:

322

answers:

2

Right now I do:

Util.AssertBackgroundThread();

or

Util.AssertUIThread();

at the start of the methods. This is not too bad, but it's runtime error checking. The reason we use static languages like C# is to move more of the error checking onto the compiler's shoulders.

Now I don't think this is generally easy, but if I restrict myself to only launching threads (or using ThreadPool.QueueUserWorkItem) from my own utility methods, then it seems to me that if I mark those methods, it should be possible to do static analysis to verify that methods intended only to be run on the UI thread are indeed run only on the UI thread?

So two questions in one here.

  1. Am I right that this can be checked at compile time?
  2. Is there any practical way to do this in Visual Studio 2008 (with latest ReSharper installed)
+2  A: 

The only thing I can think of to help you is to make your asserts use #if DEBUG in their bodies so that the methods are empty at release.

e.g.

public static void AssertUIThread()
{
#if DEBUG
   //the code goes here 
#endif
}

That way you can check during development if you're calling methods appropriately, and the JIT will remove the call entirely in your production code.

I don't see a way to do this at compile-time at the moment, but I'm favoriting this question in the hopes that it'll be answered.

Edit:

The more I think about it, the more I think you might be able to do what you want using a custom FxCop rule post-compilation. The thing is... I don't know the Introspection API that FxCop provides, and it's not well documented. Or rather, it's not documented at all. The best I can do for you is provide a tutorial or two that may or may not help you. I'm currently in the middle of reading them; if I find something interesting, I'll post it.

Edit 2:

Ahah! You can analyze the caller and the callees of a method. Using the tutorial specified there, create an attribute specifically for methods that should always be called from the UI thread, and another one for methods that should only be called from a separate thread. Your custom rule checks for one of these attributes and only runs if a method has the attribute. It then analyzes the callers of that method (and their callers, and so forth, recursively) until it can determine that the caller was either on the UI thread or from a new thread.

Now we've come to the tricky part. I haven't been able to figure this part out yet, and I leave it to you to see what you can come up with, since it's late and I can't devote much time to the problem but I'm very much interested in the solution. The problem I keep running into is that threads are all started using delegates, and I get the feeling there will be trouble going further up the caller chain that those delegates. I don't know if it'll be possible to get to the delegate; if it were possible, the delegate type could be compared to known threading delegates to determine if the call was made on a new thread or not.

Even if that's possible, there'd be the problem of going through the delegate. If you can't, you can only be certain up to the first delegate whether or not something is on a new thread.

So, problems to solve. But, hopefully, a first step for you.

Randolpho
I've done that, actually I have all Assert* methods call one Assert method that actually does the work, and it has the #if DEBUG around its body. I'm sure the JIT is smart enough to remove chains of empty calls.
Eloff
@Eloff: Mark the `Assert` method `[Conditional("DEBUG")]`. That's what it's there for ;)
280Z28
@280z28: A much better way of doing things. I'd forgotten about that one.
Randolpho
I was hoping to come back to this and try it, but life got more busy instead of less busy in the meantime, I'm still curious if it can be done, but for now I'll have to adopt a simpler solution.
Eloff
+5  A: 

I always liked the pattern:

public void GuiMethod(object param)
{
   if(this.InvokeRequired)
   {
      this.Invoke(delgateToGuiMethod, params,...)
   }
   else
   {
      //perform gui thread method
   }
}

You suffer the penalty to invoke and to check, but you can guarantee the method is either running on the gui thread, or will be invoked onto the gui thread using this pattern.

Spence