The `async` operation is a global function that initiates asynchronous
work on behalf of the synchronous code that calls it. Unlike `detach`,
`async` inherits priority, actor context, and other aspects of the
synchronous code that initiates it, making it a better "default"
operation for creating asynchronous work than `detach`. The `detach`
operation is still important for creating truly detached tasks that
can later be `await`'d or cancelled if needed.
Implements the main entry point for rdar://76927008.
The closure does not escape the startAsyncLet - endAsyncLet scope. Even though it's (potentially) running on a different thread.
The substantial change in the runtime is to not call swift_release on the closure context if it's a non-escaping closure.
Through various means, it is possible for a synchronous actor-isolated
function to escape to another concurrency domain and be called from
outside the actor. The problem existed previously, but has become far
easier to trigger now that `@escaping` closures and local functions
can be actor-isolated.
Introduce runtime detection of such data races, where a synchronous
actor-isolated function ends up being called from the wrong executor.
Do this by emitting an executor check in actor-isolated synchronous
functions, where we query the executor in thread-local storage and
ensure that it is what we expect. If it isn't, the runtime complains.
The runtime's complaints can be controlled with the environment
variable `SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL`:
0 - disable checking
1 - warn when a data race is detected
2 - error and abort when a data race is detected
At an implementation level, this introduces a new concurrency runtime
entry point `_checkExpectedExecutor` that checks the given executor
(on which the function should always have been called) against the
executor on which is called (which is in thread-local storage). There
is a special carve-out here for `@MainActor` code, where we check
against the OS's notion of "main thread" as well, so that `@MainActor`
code can be called via (e.g.) the Dispatch library's
`DispatchQueue.main.async`.
The new SIL instruction `extract_executor` performs the lowering of an
actor down to its executor, which is implicit in the `hop_to_executor`
instruction. Extend the LowerHopToExecutor pass to perform said
lowering.
`TaskGroup::offer(completedTask)`:
* Called from `AsyncTask::completeFuture()` which already takes care of
`release(completedTask)`. No additional edge is required if no task
is waiting and completed task is stored so it can be retrieved by
`group.poll()`.
* If group has waiting task, it will be dequeued and scheduled, add
`acquire(waitingTask)`.
`TaskGroupImpl::poll(waitingTask)`:
* If no pending tasks, do nothing.
* If returning finished task, `acquire(finishedTask)`.
* If enqueuing waiting task, `release(waitingTask)`.
Note: `release()` should go before, and `acquire()` after the annotated
synchronization operation.
Radar-Id: rdar://75910632
- stop storing the parent task in the TaskGroup at the .swift level
- make sure that swift_taskGroup_isCancelled is implied by the parent
task being cancelled
- make the TaskGroup structs frozen
- make the withTaskGroup functions inlinable
- remove swift_taskGroup_create
- teach IRGen to allocate memory for the task group
- don't deallocate the task group in swift_taskGroup_destroy
To achieve the allocation change, introduce paired create/destroy builtins.
Furthermore, remove the _swiftRetain and _swiftRelease functions and
several calls to them. Replace them with uses of the appropriate builtins.
I should probably change the builtins to return retained, since they're
working with a managed type, but I'll do that in a separate commit.
* Implement a YieldingContinuation type suitable for emitting values more than once via a yielding family of functions and awaiting production via next
* Add availability for YieldingContinuation and tests
* remove UnsafeConcurrentValue
* use UnsafeSendable for now
* Ensure the testing contexts are actually async
* Change the usages of Task.runDetached to Task.detach
* Change the usages of Task.detach to detach
* Transition to a external storage class outside of the generic, move to acqrel atomics, and change the error type to be enforced to Error existentials for next.
* Apply suggestions from code review
Co-authored-by: Nate Cook <natecook@apple.com>
* Remove inlines to allow for resilient changes
* Add unreachable cases in testing
Co-authored-by: Nate Cook <natecook@apple.com>
For ordinary memory-management reasons, this should only ever
happen when there will be no more uses of the actor outside of the
actor runtime. The actor runtime, meanwhile, doesn't care about
anything except the default-actor control state of the actor. So
we can just allow the rest of the actor to be destructed when it
isn't needed anymore, then destroy the actor state and deallocate
the object when we get around to switching off the executor.
This does assume that the task doesn't do anything which semantically
detects the executor it's on before switching off it, since doing so
might read a bogus executor. However, we should only get an executor
in a zombie state like this when a hop has been removed or reordered,
and detection events should count as inhibiting that and forcing the
true executor to be switched to (and thus detected).
(But maybe lifetime optimization can make this happen? Maybe we
need semantic detection to filter out zombie executors.)
Previously, if this happened, we simply left the actor in a running
state, causing any further jobs submitted to it to never be executed.
I can only speculate why this wasn't showing up in testing.
Also, change swift_job_run so that it prevents switching if the executor
passed in is not generic. This is an entrypoint for arbitrary executors
and generally should not allow unexpected switching (if someday custom
executors participate in that scheme). This infrastructure will also
be useful for implementing the `async let` semantics of running
synchronously until the task reaches a suspension point.
Finally, improve the #if'ed logging code throughout the task/actor runtime.
* Move TSan function lookup out of the common path
Move TSan function lookup via `dlsym()` out of the common path. The
TSan runtime will now call `__tsan_on_initialize()` which we can use to
initialize the TSan functions in the Swift runtime.
This avoids paying the cost of `dlsym()` in the common, non-TSan case.
Depends on: https://reviews.llvm.org/D98810
rdar://75493372
* Remove Windows code
Thread Sanitizer is not supported on Windows.
Co-authored-by: Julian Lettner <julian.lettner@apple.com>
Previously, AsyncFunctionPointer constants were signed as code. That
was incorrect considering that these constants are in fact data. Here,
that is fixed.
rdar://76118522