This allows programs to target older OSes while using Concurrency behind an availability check. When targeting older OSes, the symbols are weak-linked and the compiler will require the use of Concurrency features to be guarded by an availability check.
rdar://75850003
Most of the async runtime functions have been changed to not
expect the task and executor to be passed in. When knowing the
task and executor is necessary, there are runtime functions
available to recover them.
The biggest change I had to make to a runtime function signature
was to swift_task_switch, which has been altered to expect to be
passed the context and resumption function instead of requiring
the caller to park the task. This has the pleasant consequence
of allowing the implementation to very quickly turn around when
it recognizes that the current executor is satisfactory. It does
mean that on arm64e we have to sign the continuation function
pointer as an argument and then potentially resign it when
assigning into the task's resume slot.
rdar://70546948
This patch moves the actual running of the main code onto the main
thread by making it part of the main actor. This should be more
consistent with what folks are expecting.
First, just call an async -> T function instead of forcing the caller
to piece together which case we're in and perform its own copy. This
ensures that the task is actually kept alive properly.
Second, now that we no longer implicitly depend on the waiting tasks
being run synchronously, go ahead and schedule them to run on the
global executor.
This solves some problems which were blocking the work on TLS-ifying
the task/executor state.
- introduce UnsafeCurrentTask
- implement Hashable, Equatable on tasks
- assume we'll have a way to get a task from sync context
- Task.Handle now has a Failure type as well
- Task.Handle.getResult
We don't want people using this as a means of jumping from synchronous
code to async code. It blocks the thread that it's running on and spawns
a new thread to run the async code.
This has a couple of drawbacks. First, if it blocks a thread that is
needed by the async code (i.e, calling something on the main actor while
blocking the main thread), it is an immediate deadlock. This is a bad
day for everyone. Second, it can easily lead to thread-explosions.
The proper entry-point for async code is to use something of the
following form:
```
@main
struct Main {
static func main() async {
// ...
}
}
```
This should take care of most new code. There are still places where
folks will need to jump from synchronous code to async code. That still
needs to be designed and implemented, but we don't want people to come
to rely on `runAsyncAndBlock`.
The async handler code doesn't really run concurrently with where it is
launched, despite the implementation currently using `runDetached`
internally. Drop the @concurrent and bitcase it back on when needed.
Adding execution and death test to ensure that we crash appropriately
when the main function throws an uncaught exception, and that the async
main runs correctly.
Also switching to doing the CFRunLoopRun lookup with `RTLD_DEFAULT`
since `RTLD_SELF` isn't available on Linux.
Switching to `try await` since `await try` is no longer the right way to
do that.
Using exit(0) instead of EXIT_SUCCESS since the C++ importer doesn't
mark imported macros with @actorIndependent yet.
This patch has two desirable effects for the price of one.
1. An uncaught error thrown from main will now explode
2. Move us off of using runAsyncAndBlock
The issue with runAsyncAndBlock is that it blocks the main thread
outright. UI and the main actor need to run on the main thread or bad
things happen, so blocking the main thread results in a bad day for
them.
Instead, we're using CFRunLoopRun to run the core-foundation run loop on
the main thread, or, dispatch_main if CFRunLoopRun isn't available.
The issue with just using dispatch_main is that it doesn't actually
guarantee that it will run the tasks on the main thread either, just
that it clears the main queue. We don't want to require everything that
uses concurrency to have to include CoreFoundation either, but dispatch
is already required, which supplies dispatch_main, which just empties
out the main queue.
This patch adds the async-main start-point for programs.
When a `static func main() async` is inserted into the main program, it
gets called through `_runAsyncMain` instead of calling directly. This
starts the program in an async context, which is good because then we
can do async stuff from there.
The following code
```
@main struct MyProgram {
static func main() async {
// async code
}
}
```
is turned into
```
@main struct MyProgram {
static func $main() {
_runAsyncMain(main)
}
static func main() async {
// async code
}
}
```
_runAsyncMain code-gen's to the same thing as runAsyncAndBlock, which
emits a call to `swift_task_runAndBlockThread`.
The `try await` ordering is both easier to read and indicates the order
of operations better, because the suspension point occurs first and
then one can observe a thrown error.