ghi-electronics / Endpoint-Libraries

0 stars 0 forks source link

Code blocks on first statement when using async Main #52

Open Korporal opened 8 months ago

Korporal commented 8 months ago

This code hangs inside the property getter for OSVersion:


static async Task Main(string[] args)
{
    var os = Environment.OSVersion;
}

It behaves the same way when there are additional statements too (including await statements). If one runs this under debug and waits for s few seconds, then breaks the code, it is stuck inside the property getter...very very odd.

image

The same code does not exhibit this behavior when run directly on Windows. If you break execution then resume it, it runs fine and terminates, so the act of interrupting it with debug seems to sidestep the issue.

Oh and one more detail, it seems to be the very first statement in the Main function, not just that OSVersion property. Multiple async awaits in the code seem to work fine too, it is simply the first statement in Main that gets impacted.

A board reset and power restart does not fix this.

Korporal commented 8 months ago

I can see a pattern, it actually seems to "hang" the first time it makes calls that invoke an interop Sys.GetUnixRelease. That itself then calls various managed string related functions.

The code transitions from managed, then to the interop native Sys.GetUnixRelease and then that calls back into managed code like Marsha.PtrToString.

That suggests to me that something odd is happening to do with the transition from native back to managed. Here's a different hang, different test app:

image

That's what the stack looks like after I forcibly break during debug.

Also if I detach the debugger while the code is "hanging" it continues fine, so this is something to do with a debugger being attached, calling into that native layer an doing so inside an async Main function, or perhaps any async function...

Korporal commented 8 months ago

This seems to be the code that represents the transition to native:

https://github.com/dotnet/corert/blob/c6af4cfc8b625851b91823d9be746c4f7abdc667/src/Common/src/Interop/Unix/System.Native/Interop.GetUnixRelease.cs

Korporal commented 8 months ago

I measured how long the "hang" lasts and it seems to be consistently 69 seconds or very close to.

image

Korporal commented 8 months ago

This is just FYI:

image

Korporal commented 8 months ago

There is a workaround. Make the async Main method private and rename it, this is more or less what the C# compiler does too:

        public static void Main(string[] args)
        {
            AsyncMain(args).GetAwaiter().GetResult();
        }
        private static async Task AsyncMain(string[] args)
        {
            // Async code can go here
        }