Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Champion "Async Main" (C# 7.1) #97

Open
gafter opened this issue Feb 14, 2017 · 21 comments
Open

Champion "Async Main" (C# 7.1) #97

gafter opened this issue Feb 14, 2017 · 21 comments
Assignees
Milestone

Comments

@gafter
Copy link
Member

@gafter gafter commented Feb 14, 2017

  • Proposal added: proposals/async-main.md
  • Discussed in LDM
  • Decision in LDM
  • Finalized (done, rejected, inactive)
  • Spec'ed

See also dotnet/roslyn#1695 dotnet/roslyn#7476

@gafter gafter changed the title Async Main Champion Async Main Feb 15, 2017
@gafter gafter changed the title Champion Async Main Champion "Async Main" Feb 21, 2017
@gafter gafter added this to the 7.1 candidate milestone Feb 21, 2017
@yaakov-h
Copy link
Contributor

@yaakov-h yaakov-h commented Apr 12, 2017

Can we add a link from the above post to the proposal text?

@stephentoub
Copy link
Member

@stephentoub stephentoub commented Apr 12, 2017

Can we add a link from the above post to the proposal text?

Done

@jcouv jcouv changed the title Champion "Async Main" Champion "Async Main" (C# 7.1) Jun 5, 2017
@dbmercer
Copy link

@dbmercer dbmercer commented Jun 6, 2017

Meh. I don't really see much gain from this purely syntactic sugar change. The lack of an async Main may have been an oversight, but this solution doesn't resolve the oversight. Sure, now you can have an async Main, but you gain nothing over what you could have previously done with one line of code. An alternative async Main implementation could have passed in a cancellation token, and had the runtime host cancel the token if, say, a kill signal was received.

@nbarbettini
Copy link

@nbarbettini nbarbettini commented Jun 6, 2017

Sure, now you can have an async Main, but you gain nothing over what you could have previously done with one line of code.

I think this is still valuable, because it reduces confusion. Having to hit StackOverflow to learn that I need to add .GetAwaiter().GetResult() is nonobvious for a newbie.

An alternative async Main implementation could have passed in a cancellation token, and had the runtime host cancel the token if, say, a kill signal was received.

This would be great. I'd love to see built-in passing of a cancellation token that was linked to the kill signal. 👍

@HaloFour
Copy link
Contributor

@HaloFour HaloFour commented Jun 6, 2017

@dbmercer

I kind of agree. This is low hanging fruit, but also something very easily accomplished by a library. Where a library would have a significant advantage would be in the flexibility of handling different scenarios, such as wiring up a CancellationToken to a kill signal as you've described, which is unlikely for the language feature to do.

static class Program {
    static int Main(string[] args) {
        return Console.Async(async cancellationToken => {
            await DoStuffAsync(cancellationToken);
            return 0;
        });
    } 
}
@stephentoub
Copy link
Member

@stephentoub stephentoub commented Jun 6, 2017

There are a myriad of possible things you might want to do in your "entrypoint". You might want to respond to signals. You might want a serialized synchronization context. You might want ... etc. This feature is not meant to cover all possible scenarios, which @HaloFour highlights can be done by library. Rather, this simple feature is meant to eliminate the really annoying boilerplate that everyone has to find and copy or write to use Task-returning methods in their code:

static void Main(string[] args) => MainAsync(args).GetAwaiter().GetResult();
static async Task Main(string[] args)
{
}

becoming:

static async Task Main(string[] args)
{
}

such that a dev only needs to change void to async Task. It's the simple, most policy-free thing that can be incorporated into the language (it also doesn't preclude additional overloads being supported in the future if it becomes important to do so). For anything with more policy, such as the (valuable but more complicated) semantics mentioned, libraries are the way to go.

@mblataric
Copy link

@mblataric mblataric commented Aug 16, 2017

Hi,

I am trying new async Main in VS 2017 15.3 in one of my projects, but I get following exception:

System.Threading.ThreadStateException: 'Current thread must be set to single thread apartment (STA) mode before OLE calls can be made. Ensure that your Main function has STAThreadAttribute marked on it.'
Main method does have STAThread attribute.

Are there restrictions with using async Main?

Thanks,
Mario

@gulshan
Copy link

@gulshan gulshan commented Aug 16, 2017

Try adding <LangVersion>Latest</LangVersion> or <LangVersion>7.1</LangVersion> to csproj file.

@mblataric
Copy link

@mblataric mblataric commented Aug 16, 2017

Hi,

Yes I changed language to 7.1 (it says there is no valid Main method if you don't).
My app is WinForms app and it seems some of the visual components code calls OLE methods.
Is that a fix restriction with async Main or is there a way to make it work?

@jcouv
Copy link
Member

@jcouv jcouv commented Aug 16, 2017

FYI @TyOverby For question above

@jnm2
Copy link
Contributor

@jnm2 jnm2 commented Aug 16, 2017

I didn't think of that. Either STAThread will only affect code prior to the first await, or you'll have to install a synchronization context. It's easy,see nunit/nunit#1200 (comment).

Edit: Actually, since STAThread does nothing on async Main you'll have to just not use async Main.

Should that be automatically installed behind the scenes if you use the STAThread attribute? It would be super handy for SingleThreadedSynchronizationContext to be in the BCL too.

@TyOverby
Copy link

@TyOverby TyOverby commented Aug 16, 2017

@mblataric: Any attributes that you put on the async Main method will remain on that method, but this method isn't considered to be "the Main method" by the runtime.

Unfortunately this is a leaky abstraction that offers hints into how the feature was implemented. Under the hood, we create a new method called $main which contains the boilerplate Main().GetAwaiter().GetResult(). Because there is a new method, attributes are not carried over.

The decision to not move attributes was a tough one, but in the end we decided to for a few reasons

  • Moving the attributes was not considered because the original Main method is still around and perfectly usable.
  • Copying the attributes was considered but rejected because it would not be obvious to the user that there would be another method with the attributes of the original.

That leaves us where we are today: Attributes are not copied or moved to the hidden "real" Main method. @gafter: do you have any suggestions on where these details could be documented so that more people aren't left trying to interpret runtime errors?

@jnm2
Copy link
Contributor

@jnm2 jnm2 commented Aug 16, 2017

Could you special-case the STAThread attribute to be copied to $main and also automatically install a SingleThreadedSynchronizationContext?

@TyOverby
Copy link

@TyOverby TyOverby commented Aug 16, 2017

@jnm2: The compiler doesn't know what STAThread or SingleThreadedSynchronizationContext is. We try to keep the coupling between language and framework as minimal as possible.

@tannergooding
Copy link
Member

@tannergooding tannergooding commented Aug 16, 2017

@TyOverby, I agree. However, it is likely worth providing a way to propagate attributes placed on the async Main method to the actual Main method, as this is the only way to enforce certain runtime behaviors (as happens with STAThread and MTAThread).

Otherwise, async Main is essentially unusable from any kind of UI based application.

@jnm2
Copy link
Contributor

@jnm2 jnm2 commented Aug 16, 2017

If Main is async, it's because you're going to await which will break the STAThread unless you install a synchronization context, which ideally would block and pump rather than starting a new STA thread. If it's going to block, a custom synchronization context API could still give the illusion that you await the pump and the continuation actually executes inside the pump, but that is weird and it's still boilerplate. May as well stick with synchronous Main for UI code if you aren't going to automatically install that sync context.

@tannergooding
Copy link
Member

@tannergooding tannergooding commented Sep 14, 2017

@gafter
Copy link
Member Author

@gafter gafter commented Sep 15, 2017

That leaves us where we are today: Attributes are not copied or moved to the hidden "real" Main method. @gafter: do you have any suggestions on where these details could be documented so that more people aren't left trying to interpret runtime errors?

I think we need to take this to the LDM. I suspect we will want to copy certain attributes such as this one. I will bring the issue up.

@Thaina
Copy link

@Thaina Thaina commented Jan 22, 2019

Doesn't this issue is done and should be already closed?

@yaakov-h
Copy link
Contributor

@yaakov-h yaakov-h commented Jan 22, 2019

It hasn't yet been published in an updated C# language specification.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.