17. December 2013

Async/Await -- Through the Looking Glass

Update 2014-04-10: I wouldn’t read too much into my conclusion below.

Alice entering the Looking-glass World (http://www.victorianweb.org/art/illustration/tenniel/lookingglass/1.4.html)

I finally had a chance to try out the no longer very new async/await paradigm for asynchronous programming in .NET. My goal: make a long-running MVC4 action asynchronous to prevent thread starvation in ASP.NET.

When the call is made, the action fires off an executable to perform some work. This executable returns immediately and offers few hooks, so the only way I can be certain that the operation has completed is to watch for the appearance of a particular zero-byte file that it creates when it has finished.

The beauty of async/await is how it does away with the traditional async callback method. Instead of Begin/End pairs, or Async/Complete, or even callback events, you get code that looks something like this.

public async Task<Stuff> GetStuffAsync() {
    Stuff stuff = await DoTheAsyncThing();
    return stuff;
}

It all looks very synchronous and saves a lot of mental unwinding that would happen otherwise.

But as I learned today, once you do hit the await statement, you are still leaving the stack. And when the await returns, the place you come back to may not be identical to the place you left, depending on how it got there. Here’s a contrived example.

public async Task<Stuff> GetStuffAsync(string someRelativeFolder) {
    string completionFile = 
        HttpContext.Current.Server.MapPath(
            Path.Combine(someRelativeFolder, "file.txt"))
    await VerifyFileExists(completionFile);

    string absolutePath = HttpContext.Current.Server.MapPath(
        Path.Combine(someRelativeFolder, "anotherfile.xml"));

    return LoadStuffFromAbsolutePath(absolutePath);
}

public async Task VerifyFileExists(string path) {
    if (File.Exists(path)) return Task.FromResult(true);
    var tsc = new TaskCompletionSource<bool>();
    var fsw = new FileSystemWatcher(Path.GetDirectoryName(path));
    FileSystemEventHandler createdHandler = null;
    createdHandler = (s, e) => {
        if (e.Name == Path.GetFileName(path)) {
            tcs.SetResult(true);
            watcher.Created -= createdHandler;
            watcher.Dispose();
        }
    }
    watcher.Created += createdHandler;
    watcher.EnableRaisingEvents = true;
    return tcs.Task;
}

This throws a NullReferenceException the second time that you try to access HttpContext.Current. Why? The fsw.Created handler is called on a different thread than the one you just came from. That’s a pretty well-known fact and is why the FileSystemWatcher has a SynchronizingObject property to help WinForms programmers navigate their way back to the UI thread.

But it also exposes the “through the looking glass” nature of async/await. The simplified syntax obscures the true nature of the code — in one method, the same object goes from having a value to having no value. Watch out!

Comments

18. November 2013

When BadImageFormatException Is Not What It Seems

I hate this exception

Another exception that long-term C# developers are very familiar with is BadImageFormatException. When I see it, I think “there must be some kind of compilation mixup.” As in, a third-party DLL isn’t compiled as AnyCPU. Or a .NET 2.0 site is referencing a .NET 4.0 library. Generally it’s an annoyance but easy to suss out.

Today I received an error report from my colleague where the application was throwing this very exception. I couldn’t reproduce the problem on my machine, but he assured me it happened every time on his (a 32-bit workstation) and on the test server (64-bit).

The stack trace ended inside a call like this.

T CallWrapped<T>(IEnumerable<Func<T>> actions) {
    T output = default(T);
    foreach (var action in actions) {
        output = Combine(output, action());
    }
    return output;
}

On my machine, this executed fine. But on my colleague’s, it would fail at the second action() call. The actions looked something like this:

TRetVal WrappedDelete(Entity entity) {
    var actions = new [] {
        () => LogDelete(entity),
        () => base.Delete(entity)
    };
    return CallWrapped<TRetVal>(actions);
}

I set a breakpoint on base.Delete() to see what was going on and hit F5. The breakpoint wasn’t hit. What? Visual Studio won’t step into native methods that fail (unless you ask it nicely), but I’d never seen it refuse to step into a managed method declared within the solution! I checked the Delete method just to see if there were any clues there…

public virtual TRetVal Delete(Entity entity) {
    // simple NHibernate stuff in here, nothing worth mentioning!
}

… but it was all perfectly cromulent code that would have caused fury among users if it actually had any problems.

After numerous Google searches that went nowhere (“Make sure you’re targeting the right platform!” shutupshutup), I finally located the answer on StackOverflow. It turns to be a bug in the C# 4.0 compiler. To reproduce it:

  • Write a lambda (anonymous method, delegate, what have you)…
  • that calls a base method…
  • that is declared virtual
  • while running VS 2010 or below, or compiling with the C# 4.0 compiler

A workaround is to wrap the base method call in its own method, like so:

TRetVal WrappedDelete(Entity entity) {
    var actions = {
        () => LogDelete(entity),
        () => DoDelete(entity)
    };
    return CallWrapped<TRetVal>(actions);
}

private TRetVal DoDelete(Entity entity) {
    return base.Delete(entity);
}

Comments

07. November 2013

NHibernate can't short-circuit

Johnny Five Is Alive

While doing some refactoring on an NHibernate-driven site, I came across a nasty exception:

System.ArgumentNullException: Value cannot be null. Parameter name: source 
at System.Linq.Enumerable.Cast[TResult](IEnumerable source)

This was a bit confusing at first. The code was already checking for null’s. What gives?

List<int> list = null;
// ... something tries to populate the list ...
// If there's anything in the list, use that as a filter. 
// Otherwise return everything.
var q = Session.Query<Tbl>()
               .Where(t => list == null || list.Contains(t.IntProp));

Looking at the error message and the debugger it became clear that the list.Contains statement was being evaluated. That’s very unusual behavior in C# as the || operator is supposed to “short-circuit:” when the first operand evaluates to true, the second is not evaluated at all.

As it turns out, the parameter of the Where clause is actually an expression tree. NHibernate does not execute this expression directly. Instead, it parses it to generate a SQL statement it can run against the database.

To illustrate, suppose that there were no null check, and the list contained the values [1,2,3]. It might try to do something like this:

SELECT * FROM Tbl WHERE IntProp IN (1,2,3)

Now let’s assume the list is null, but there’s still no null-check in the expression. It would attempt to convert the list to its SQL equivalent and fail with the same exception as above.

Finally, we add the null check. Now NHibernate still is trying to generate a query. Perhaps it decides that “list IS NULL” is a perfectly reasonable SQL expression, or perhaps not. Regardless, it still has to iterate over the list to generate the IN clause, which it cannot do.

In essence, we are mixing a test that doesn’t belong in a query with a test that does, but NHibernate does not know that’s the case. The solution is to move the tests where they belong.

List<int> list = null;
// ... something tries to populate the list ...
var q = Session.Query<Tbl>();
// If there's anything in the list, use that as a filter. 
// Otherwise return everything.
if (list != null) {
    q = q.Where(t => list.Contains(t.IntProp));
}

It’s a bit clumsier without the fluent interface to lean on, but it does have the side benefit of making the resultant SQL query cleaner.

Comments