Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singlestepping this program is wierd - Compiler error? [duplicate]

When I stepped over breakpoints in my code I have encountered strange behaviour of debugger:

public async Task DoSomeWork()
{
     await Task.Run(() => { Thread.Sleep(1000); });

     var test = false;
     if (test)
     {
          throw new Exception("Im in IF body!");
     }
}

Debugger goes into if body. It's remarkable that the exception is not really thrown but just looks like it is. So you can't reproduce that if you place breakpoint right on throw. You must place it above and step down to the if body to catch it. The same works on any kind of exception instance (as well as explicit null) and even on return instead of throw.

Besides that it works even if I remove line with await.

I tried to run this code snippet from different PCs so its not a PC trouble. Also I have thought it is bug in VS code and tried to run it in Rider from JetBrains - the same result.

I'm sure it's the async thing but how it explicitly works?

like image 726
Altav1sta Avatar asked Nov 18 '22 09:11

Altav1sta


1 Answers

Your code reproduces the issue easily, in a "Debug" build, using Visual Studio 2015. I had only to add in Program.Main(), with a call to DoSomeWork().Wait();, set a breakpoint in the method and step through it.

As for why it happens, this is undoubtedly due to the combination of the async method being rewritten and the debugging database (.pdb) generated. Similar to iterator methods, adding async to the method causes the compiler to change your method into a state machine. The actual IL that's generated looks only a little bit like the original method. That is, if you look at it, you can identify the key components of the original code, but it's now in a big switch statement that handles what happens as the method returns at each await statement, and then is re-entered with the completion of each awaited expression.

When the program statement appears to be on the throw, it's really at the implicit return statement in the method. It's just that the debugging database for the executable doesn't provide a program statement for that line.

There's a hint, when debugging, that that's what's happening. When you step over the if statement, you'll notice it goes straight to the throw statement. If the if statement block were really being entered, the next program statement line would actually be the opening brace for the block, not the program statement.

You can also add e.g. a Console.WriteLine() at the end of the method, and that will give the debugger enough information to sync up and not show you at the wrong line number.

For additional information on how async methods are handled by the compiler, see Is the new C# async feature implemented strictly in the compiler, and the links provided there (including Jon's series of articles on the topic).

like image 165
Peter Duniho Avatar answered Dec 06 '22 16:12

Peter Duniho