Skip to content

Implementing 0323 async main #39607

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

Merged
merged 15 commits into from
Oct 12, 2021

Conversation

etcwilde
Copy link
Member

@etcwilde etcwilde commented Oct 6, 2021

Cherry-picking #38604, #39503, and #39593, and applying necessary changes to make up for missing

This change relies on the SwiftDeclRef::Kind::EntryPoint created in #37014.

This implements the semantic changes described in swiftlang/swift-evolution#1437, Async Main Semantics.

  • Explanation: This implements the semantic changes to the asynchronous main function described in SE-proposal 0323-Asynchronous Main Semantics. The main function (both synchronous and asynchronous) has MainActor isolation rules applied and is run synchronously up to the first suspension point.
  • Scope: Main Functions
  • Risk: Low (Semantics are implemented in compiler instead of runtimes, so things can be fixed easily. There is a small source break by disallowing the main function to have any other global actor than the MainActor.)
  • Testing: Added new tests cases to ensure main-actor isolation is applied and the correct SIL is emitted.
  • Issue: rdar://80027250
  • Reviewers:

hamishknight and others added 15 commits October 5, 2021 16:01
Now that SILDeclRef can represent the main function,
tweak TBDGen to refer to it.
Allow SILDeclRef to refer to the main program
entry-point, which will either be for a main
SourceFile, or a synthetic main such as an `@main`
decl. Adjust the various SILDeclRef related
functions to handle this new case, and change the
emission to go through `emitFunctionDefinition`.

This change will allow the entry-point for an `@main`
decl (and eventually a main SourceFile) to be
emitted on-demand from its symbol name.
This will be used to represent the entry-point
for a main SourceFile.
This will make it easier to store in a SILDeclRef.
The lookupConcurrencyIntrinsic function only looked in the concurrency
module. It is useful to look in other modules for intrinsics too.
The AsyncEntryPoint represents the thunk that is wrapped in a task. This
thunk is used to ensure that the main function explicitly calls "exit",
and to properly unwrap and report any unhandled errors returned from the
user-written main. The function takes on the name `@async_main` in the
emitted SIL.
This patch updates the asynchronous main function to run the first thunk
of the function synchronously through a call to `swift_job_run`.

The runloop is killed by exiting or aborting the task that it is running
on. As such, we need to ensure that the task contains an async function
that either calls exit explicitly or aborts. The AsyncEntryPoint, that
contains this code, was added in the previous patch. This patch adds the
pieces for the actual implementation of this behaviour as well as adding
the necessary code to start the runloop.

There are now four layers of main functions before hitting the "real"
code.

@main: This is the actual main entrypoint of the program. This
constructs the task containing @async_main, grabs the main executor,
runs swift_job_run to run the first part synchronously, and finally
kicks off the runloop with a call to _asyncMainDrainQueue. This is
generated in the call to `emitAsyncMainThreadStart`.

@async_main: This thunk exists to ensure that the main function calls
`exit` at some point so that the runloop stops. It also handles emitting
an error if the user-written main function throws.

e.g:

```
func async_main() async -> () {
  do {
    try await Main.$main()
    exit(0)
  } catch {
    _errorInMain(error)
  }
}
```

Main.$main(): This still has the same behaviour as with the
synchronous case. It just calls `try await Main.main()` and exists to
simplify typechecking.

Main.main(): This is the actual user-specified main. It serves the same
purpose as in the synchronous, allowing the programmer to write code,
but it's async!

The control flow in `emitFunctionDefinition` is a little confusing (to
me anyway), so here it is spelled out:

If the main function is synchronous, the `constant.kind` will be a
`SILDeclRef::Kind::EntryPoint`, but the `decl` won't be async, so it
drops down to `emitArtificalTopLevel` anyway.

If the main function is async and we're generating `@main`, the
`constant.kind` will be `SILDeclRef::Kind::AsyncEntryPoint`, so we also
call `emitArtificalTopLevel`. `emitArtificalTopLevel` is responsible for
detecting whether the decl is async and deciding whether to emit code to
extract the argc/argv variables that get passed into the actual main
entrypoint to the program. If we're generating the `@async_main` body,
the kind will be `SILDeclRef::Kind::EntryPoint` and the `decl` will be
async, so we grab the mainEntryPoint decl and call
`emitAsyncMainThreadStart` to generate the wrapping code.

Note; there is a curious change in `SILLocation::getSourceLoc()`
where instead of simply checking `isFilenameAndLocation()`, I change it
to `getStorageKind() == FilenameAndLocationKind`. This is because the
SILLocation returned is to a FilenameAndLocationKind, but the actual
storage returns true for the call to `isNull()` inside of the
`isFilenameAndLocation()` call. This results in us incorrectly falling
through to the `getASTNode()` call below that, which asserts when asked
to get the AST node of a location.

I also did a little bit of refactoring in the SILGenModule for grabbing
intrinsics. Previously, there was only a `getConcurrencyIntrinsic`
function, which would only load FuncDecls out of the concurrency
module. The `exit` function is in the concurrency shims module, so I
refactored the load code to take a ModuleDecl to search from.

The emitBuiltinCreateAsyncTask function symbol is exposed from
SILGenBuiltin so that it is available from SILGenFunction. There is a
fair bit of work involved going from what is available at the SGF to
what is needed for actually calling the CreateAsyncTask builtin, so in
order to avoid additional maintenance, it's good to re-use that.
This patch changes the main task to inherit the context of the main
thread. This should assign the appropriate priority based on how the
program was invoked. I've also updated the tests to reflect these
changes.
Priorities keep shifting since I first wrote this and are apparently
different on different OS's and different versions of the same OS.
Since the test is checking that entering and exiting tasks doesn't
effect the outer-scoped priority, I've set a variable based on the main
thread's priories, which should be inherited by the child.
A single-element struct is structurally the same as the member type
itself.
This patch forces the main function to be protected behind MainActor
isolation. If no actor isolation is specified, the main function will
implicitly have MainActor isolation.
Putting the main function under the MainActor appears to have
accidentally "fixed" the resilience tests on Windows. I've put the entry
to those tests under a separate task to try and avoid accidentally
hiding the bug behind the main actor.
As per the C++14 spec, $ can't be used in a portable way in an
identifier name. It falls under the implementation-defined characters
and is allowed by clang. Removing it.
Adding new async semantics to Swift 5.5
5.5 doesn't have any `get*Type()` interfaces, so have to get the types
through some other means.
@etcwilde etcwilde added r5.5 concurrency Feature: umbrella label for concurrency language features labels Oct 6, 2021
@etcwilde etcwilde requested a review from DougGregor October 6, 2021 05:09
@etcwilde etcwilde requested a review from a team as a code owner October 6, 2021 05:09
@etcwilde
Copy link
Member Author

etcwilde commented Oct 6, 2021

@swift-ci please test

@swift-ci
Copy link
Contributor

swift-ci commented Oct 6, 2021

Build failed
Swift Test Linux Platform
Git Sha - 01eebe1

@etcwilde
Copy link
Member Author

etcwilde commented Oct 6, 2021

Windows appears to be missing libxml2:

  The C compiler

    "T:/Library/Developer/Toolchains/unknown-Asserts-development.xctoolchain/usr/bin/clang-cl.exe"

  is not able to compile a simple test program.

  It fails with the following output:

    Change Dir: T:/swift-corelibs-libdispatch/CMakeFiles/CMakeTmp
    
    Run Build Command(s):C:/PROGRA~2/MICROS~1/2019/COMMUN~1/Common7/IDE/COMMON~1/MICROS~1/CMake/Ninja/ninja.exe cmTC_66d23 && [1/2] Building C object CMakeFiles\cmTC_66d23.dir\testCCompiler.c.obj
    [2/2] Linking C executable cmTC_66d23.exe
    FAILED: cmTC_66d23.exe 
    cmd.exe /C "cd . && "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe" -E vs_link_exe --intdir=CMakeFiles\cmTC_66d23.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100177~1.0\x64\rc.exe --mt=T:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin\llvm-mt.exe --manifests  -- T:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin\lld-link.exe /nologo CMakeFiles\cmTC_66d23.dir\testCCompiler.c.obj  /out:cmTC_66d23.exe /implib:cmTC_66d23.lib /pdb:cmTC_66d23.pdb /version:0.0 /INCREMENTAL:NO  /debug /INCREMENTAL /subsystem:console  kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ."
    MT: command "T:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin\llvm-mt.exe /nologo /manifest CMakeFiles\cmTC_66d23.dir/intermediate.manifest /out:CMakeFiles\cmTC_66d23.dir/embed.manifest /notify_update" failed (exit code 0x1) with the following output:
    llvm-mt: error: no libxml2
    llvm-mt: ignoring unsupported 'notify_update' option
    ninja: build stopped: subcommand failed.

Linux is crashing while emitting the asynchronous @main function for one test. I'll have to investigate once I have a chance.

Failing test: Driver/static-stdlib-autolink-linux.swift

Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project and the crash backtrace.
Stack dump:
0.	Program arguments: /home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend -frontend -c -primary-file /home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/swift/test/Driver/static-stdlib-autolink-linux.swift -target x86_64-unknown-linux-gnu -disable-objc-interop -I /home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/swift-corelibs-libdispatch -I /home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/libdispatch_static-linux-x86_64/src/swift/swift -I /home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/test-linux-x86_64/Driver/Output/static-stdlib-autolink-linux.swift.tmp -module-cache-path /home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/swift-test-results/x86_64-unknown-linux-gnu/clang-module-cache -static -define-availability "SwiftStdlib 5.5:macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0" -parse-as-library -module-name main -o /home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/tmp/static-stdlib-autolink-linux-9e946d.o -use-static-resource-dir
1.	Swift version 5.5-dev (LLVM e93a364ceaa35b8, Swift 4f0259e800cceaa)
2.	
3.	While evaluating request ASTLoweringRequest(Lowering AST to SIL for file "/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/swift/test/Driver/static-stdlib-autolink-linux.swift")
4.	While silgen emitArtificialTopLevel SIL function "@main".
 #0 0x0000000005aa5193 llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x5aa5193)
 #1 0x0000000005aa32a0 llvm::sys::RunSignalHandlers() (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x5aa32a0)
 #2 0x0000000005aa551a SignalHandler(int) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x5aa551a)
 #3 0x00007f65fba95390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390)
 #4 0x0000000000772f7a swift::SILDeclRef::mangle[abi:cxx11](swift::SILDeclRef::ManglingKind) const (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x772f7a)
 #5 0x000000000162df16 swift::SILFunctionBuilder::getOrCreateFunction(swift::SILLocation, swift::SILDeclRef, swift::ForDefinition_t, llvm::function_ref<swift::SILFunction* (swift::SILLocation, swift::SILDeclRef)>, swift::ProfileCounter) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x162df16)
 #6 0x0000000000c8d8c6 swift::Lowering::SILGenModule::getFunction(swift::SILDeclRef, swift::ForDefinition_t) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0xc8d8c6)
 #7 0x0000000000cf934a swift::Lowering::SILGenFunction::emitAsyncMainThreadStart(swift::SILDeclRef) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0xcf934a)
 #8 0x0000000000c8e887 swift::Lowering::SILGenModule::emitFunctionDefinition(swift::SILDeclRef, swift::SILFunction*) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0xc8e887)
 #9 0x0000000000c94f42 swift::ASTLoweringRequest::evaluate(swift::Evaluator&, swift::ASTLoweringDescriptor) const (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0xc94f42)
#10 0x0000000000d44903 swift::SimpleRequest<swift::ASTLoweringRequest, std::unique_ptr<swift::SILModule, std::default_delete<swift::SILModule> > (swift::ASTLoweringDescriptor), (swift::RequestFlags)9>::evaluateRequest(swift::ASTLoweringRequest const&, swift::Evaluator&) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0xd44903)
#11 0x0000000000c98eda llvm::Expected<swift::ASTLoweringRequest::OutputType> swift::Evaluator::getResultUncached<swift::ASTLoweringRequest>(swift::ASTLoweringRequest const&) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0xc98eda)
#12 0x0000000000c95fb4 swift::performASTLowering(swift::FileUnit&, swift::Lowering::TypeConverter&, swift::SILOptions const&) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0xc95fb4)
#13 0x000000000058735f performCompileStepsPostSema(swift::CompilerInstance&, int&, swift::FrontendObserver*) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x58735f)
#14 0x000000000057af00 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*) (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x57af00)
#15 0x00000000004d39a5 main (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x4d39a5)
#16 0x00007f65fa5ff840 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x20840)
#17 0x00000000004d35c9 _start (/home/buildnode/jenkins/workspace/swift-PR-Linux/branch-release/5.5/buildbot_linux/swift-linux-x86_64/bin/swift-frontend+0x4d35c9)

@etcwilde
Copy link
Member Author

etcwilde commented Oct 8, 2021

@swift-ci please test Linux platform

@etcwilde
Copy link
Member Author

etcwilde commented Oct 8, 2021

@swift-ci please test Windows platform

@etcwilde
Copy link
Member Author

etcwilde commented Oct 8, 2021

CMake Error at C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/share/cmake-3.20/Modules/CMakeDetermineCompilerId.cmake:6 (CMAKE_DETERMINE_COMPILER_ID_BUILD):
  Syntax error in cmake code at

    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/share/cmake-3.20/Modules/CMakeDetermineCompilerId.cmake:6

  when parsing string

    -resource-dir;"T:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\lib\swift"

  Invalid character escape '\L'.
I’m seeing weird things on the Windows swift builder: https://ci-external.swift.org/job/swift-PR-windows/17561/console

Pretty sure this isn't my fault.

@etcwilde
Copy link
Member Author

etcwilde commented Oct 8, 2021

@swift-ci please test windows platform

Copy link
Member

@kavon kavon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍🏼 Looks good

@DougGregor
Copy link
Member

@swift-ci please nominate

@DougGregor DougGregor merged commit 06b73da into swiftlang:release/5.5 Oct 12, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
concurrency Feature: umbrella label for concurrency language features 🍒 release cherry pick Flag: Release branch cherry picks swift 5.5
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants