Can entrypoint be marked with "async" modifier on CoreCLR?

In Stephan Cleary’s published blog about Async Console Apps on .NET CoreCLR, he shows us that in CoreCLR (currently running on Visual Studio 2015, CTP6), the “Main” entry point can actually be marked as async Task , compile correctly 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 enhanced by a blog post from an ASP.NET team called Deep Dive in the ASP.NET 5 Runtime :

In addition to the Program.Main static entry point, KRE supports instance-based entry points. You can even make the main entry point asynchronously and return the task. Having a main entry point, an instance method, you can have the services entered into your runtime application.

We know that, until now, the entry point cannot be marked with the 'async' modifier . So, how is this really possible in the new CoreCLR workspace?

+6
source share
1 answer

Diving into the original CoreCLR environment, we can see the static class RuntimeBootstrapper , which is responsible for calling our record 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 this inside, it calls ExecuteAsync(args).GetAwaiter().GetResult(); , which is semantically equivalent to calling Task.Result , except that instead of returning a wrapped AggregationException , we get an exception thrown.

This is important to understand, since there is no “black magic” as to how this happens. For the current version of the CoreCLR runtime, the method is allowed to flag async Task because it blocked the above chain of calls at runtime.

Side notes:

Immersed in ExecuteAsync , we see that it calls:

 return bootstrapper.RunAsync(app.RemainingArguments); 

When looking inside, we see that the actual MethodInfo calling 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); } 
+4
source

Source: https://habr.com/ru/post/983529/


All Articles