Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
208 views
in Technique[技术] by (71.8m points)

c# - Entry point can be marked with the 'async' modifier on CoreCLR?

In Stephan Cleary's recent blog post about Async Console Apps on .NET CoreCLR he shows us that in CoreCLR (currently running on Visual Studio 2015, CTP6), the entry point "Main" can actually be marked as async Task, compile properly and actually run:

public class Program
{
    public async Task Main(string[] args)
    {
        Console.WriteLine("Hello World");
        await Task.Delay(TimeSpan.FromSeconds(1));
        Console.WriteLine("Still here!");
        Console.ReadLine();
    }
}

Gives the following output:

async Main entry point

This is strengthend by a blog post from the ASP.NET team called A Deep Dive into the ASP.NET 5 Runtime:

In addition to a static Program.Main entry point, the KRE supports instance-based entry points. You can even make the main entry point asynchronous and return a Task. By having the main entry point be an instance method, you can have services injected into your application by the runtime environment.

We know that up until now, An entry point cannot be marked with the 'async' modifier. So, how is that actually possible in the new CoreCLR runtime?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Diving into the source of the CoreCLR runtime, we can see a static class called RuntimeBootstrapper, which is in charge of invoking our entry point:

public static int Execute(string[] args)
{
    // If we're a console host then print exceptions to stderr
    var printExceptionsToStdError = Environment.GetEnvironmentVariable(EnvironmentNames.ConsoleHost) == "1";

    try
    {
        return ExecuteAsync(args).GetAwaiter().GetResult();
    }
    catch (Exception ex)
    {
        if (printExceptionsToStdError)
        {
            PrintErrors(ex);
            return 1;
        }

        throw;
    }
}

We can see that internally, it calls ExecuteAsync(args).GetAwaiter().GetResult();, which is semantically equivallent to calling Task.Result, except that instead of recieveing the wrapped AggregationException, we recieve the exception unwrapped.

This is important to understand, as there is no "black magic" as to how it's happening. For the current version of the CoreCLR runtime, the method is allowed to marked async Task because it's blocked higher up the callchain by the runtime.

Side Notes:

Diving into ExecuteAsync, we'll see that it ends up calling:

return bootstrapper.RunAsync(app.RemainingArguments);

When looking inside, we see that actual MethodInfo invocation of our entry point:

public static Task<int> Execute(Assembly assembly, string[] args, IServiceProvider serviceProvider)
{
    object instance;
    MethodInfo entryPoint;

    if (!TryGetEntryPoint(assembly, serviceProvider, out instance, out entryPoint))
    {
        return Task.FromResult(-1);
    }

    object result = null;
    var parameters = entryPoint.GetParameters();

    if (parameters.Length == 0)
    {
        result = entryPoint.Invoke(instance, null);
    }
    else if (parameters.Length == 1)
    {
        result = entryPoint.Invoke(instance, new object[] { args });
    }

    if (result is int)
    {
        return Task.FromResult((int)result);
    }

    if (result is Task<int>)
    {
        return (Task<int>)result;
    }

    if (result is Task)
    {
        return ((Task)result).ContinueWith(t =>
        {
            return 0;
        });
    }

    return Task.FromResult(0);
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...