When an exception is thrown through swift_job_run, it leaves the Concurrency runtime in an inconsistent state, which can lead to misbehavior or crashes later on. It's very difficult to work out what the cause is when this happens. Since the program state is doomed once this happens, prevent exceptions from propagating through swift_job_run at all, and terminate immediately when throwing.
rdar://171909991
Avoid more of the C standard library to reduce dependencies and code
size. This removes a bunch of code used to format nice fatal error
messages, reducing code size at some cost to debuggability. This is a
deliberate tradeoff in Embedded Swift.
We had to do a silly trick to avoid strlen, because LLVM likes to
pattern-match the loop and inject the standard library dependency,
which we do not want.
The concurrency library accounts for distributed actors, which
introduces some dependencies on metadata computations that we don't
want. Distributed actors are not supported in Embedded Swift, so
remove these functions to drop the external reference.
Embedded Swift doesn't provide reporting to a debugger, which entails
system dependencies we don't model. Remove this functionality from the
Embedded Concurrency build.
Part of rdar://176300169.
Emit this in runOnAssumedThread. This handles the case where a task switches actors without suspending, and thus within a single job_run interval.
rdar://175776849
**Description**: The move to adopting nonisolated nonsending in
continuation APIs has removed un-necessary hops INTO the continuation
block, however this also removed hops "from" when the continuation is
resumed to where code is supposed to run; this actually causes bugs
where the continuation resume executing on whoever resumed it, rather
than the expected context as dictated by isolation.
**Scope/Impact**: uses of with...Continuation that have now adopted
nonisoalted(nonsending) to avoid extra hops
**Risk:** Low, corrects missing hops, the switch optimization is
definitely correct as well, and a subset of fixes we wanted to do in
switch optimizations
**Testing**: CI testing, verified SIL and the logic flow
**Radar:** Resolves: rdar://173371163
* Have job_run include the actor, executor, and task name.
* Make task_status_changed only trace if one of the tracked values actually changed.
* Add a job_enqueue_executor that covers enqueueing jobs on serial executors (default actors, custom executors, main actor, etc.).
* Actually end the actor lifetime signpost interval.
* Include the actor metadata and context descriptor pointers in actor enqueue/dequeue so the type can be identified.
* Add the task pointer to all task-related signpost messages.
* Emit job_enqueue_main_executor from the swift_task_enqueueImpl path so main actor enqueues appear in traces.
* Add TracingExecutorKind enum and log executorKind in job_enqueue_executor and job_run to distinguish global, default actor, main actor, custom serial executor, and task executor contexts.
While we're here, finally add a test for Concurrency signposts. This is difficult because the signposts are disabled by default and there isn't a good way to turn them on in an automated fashion. Resolve this by adding an environment variable, SWIFT_CONCURRENCY_TRACING_SUBSYSTEM, which overrides the subsystem used by the Concurrency signpost code. The test can use this to set a subsystem that isn't disabled by default, which then allows `log stream` to capture the signposts.
Because we guaranteed that one cannot access a distribted "remote"
instance's state via type system, we can allocate the instance much
smaller because we never access those user initialized fields.
This way a remote distributed actor reference is always 128 bytes, this
is because actors have special storage 12*8 bytes of PrivateData in
addition to the 16 bytes object header.
With this optimization, regardless how many fields an actor has, the
remote ref always is the same size.
resolves rdar://81825648
Used claude to generate a bunch of tests to check the sizes of actors
etc, I think this is correct (and getting it wrong totally just crashes
immediately anyway). Could use a review though, thank you!
cc @mikeash @xedin
swift_task_switchImpl can give up its thread and immediately start running the new code. In the actors-as-locks model, this acquires the new actor's lock and releases the old one. This results is a classic lock ordering problem and can deadlock. Avoid this by releasing the old lock first when in this mode.
rdar://168073822
Replace C++ implementation of swift_beginAccess and swift_endAccess with (almost) pure Swift implementation. Helpers remain in C++ for TLS, getting return addresses, and raising a fatal error on violations.
This change also moves the exclusivity access set head from the shared SwiftTLSContext structure to a dedicated TLS key. This improves performance, which is important for exclusivity checking. This is particularly the case where we can inline TLS access with a constant key, as on Darwin ARM64.
The code that bridges exclusivity tracking into Concurrency remains in C++. The new Swift implementation exposes a few helpers for it to use as a replacement for directly manipulating the C++ implementation.
rdar://161122309
The test was crashing due to `swift_unreachable("custom executors not supported in embedded Swift")` line in `swift_task_enqueueImpl`, as the corresponding non-embedded codepath was relying on an unspecialized generic function `_swift_task_enqueueOnExecutor` defined in `Executor.swift`. Unspecialized generics are unavailable in Embedded Swift, and such `@silgen_name` function can't be specialized when used from concurrency runtime code written in C/C++. We can redefine this function for Embedded Swift as using a class-bound existential instead, and re-enable this codepath with a slightly different call that avoids the use of unavailable `swift_getObjectType` function from the non-embedded runtime.
When using the new custom default executors, sometimes we end up
taking a long time to do a task switch. This is happening because
the path the new code takes sometimes results in a concrete pointer
to the default global executor being in the executor tracking
information in `swift_task_switch()`, and if we try to switch to
a `nil` task executor (which _also_ means the default global executor),
we aren’t spotting that and we’re taking the slow path.
Essentially, we want to take the fast path in cases where we switch
from `nil` to the concrete default global executor and vice-versa.
rdar://156701386
This changes the isIsolatingCurrentContext function to return `Bool?`
and removes all the witness table trickery we did previously to detect
if it was implemented or not. This comes at a cost of trying to invoke
it always, before `checkIsolated`, but it makes for an simpler
implementation and more checkable even by third party Swift code which
may want to ask this question.
Along with the `withSerialExecutor` function, this now enables us to
check the isolation at runtime when we have an `any Actor` e.g. from
`#isolation`.
Updates SE-0471 according to
https://forums.swift.org/t/se-0471-improved-custom-serialexecutor-isolation-checking-for-concurrency-runtime/78834/
review discussions
Previously there was still a sneaky hop which caused ordering issues.
This introduced a specific test startSynchronously_order which checks
that the task enqueues indeed are "immediate" and cleans up how we
handle this.
This also prepares for the being discussed in SE review direction of
this API that it SHOULD be ALLOWED to actually hop and NOT be
synchronous at all IF the isolation is specified on the closure and is
DIFFERENT than the callers dynamic isolation.
This effectively implements "synchronously run right now if dynamically
on the exact isolation as requested by the closure; otherwise enqueue
the task as usual".
resolves rdar://149284186
cc @drexin
Tweaked diagnostic to use a string instead of a type. Renamed the
feature in `FeatureAvailability.def` (and added the `TaskExecutor`
feature to 6.2). Also fixed the `swift_getActiveExecutor()`
function to return the main executor only when on the main thread.
rdar://141348916
* [Concurrency] Detect non-default impls of isIsolatingCurrentContext
* [Concurrency] No need for trailing info about isIsolating... in conformance
* Apply changes from review
Reorganise the Concurrency code so that it's possible to completely
implement executors (both main and global) in Swift.
Provide API to choose the desired executors for your application.
Also make `Task.Sleep` wait using the current executor, not the global
executor, and expose APIs on `Clock` to allow for conversion between
time bases.
rdar://141348916
* [Concurrency] Initial steps for startSynchronously for Task
* [Concurrency] Rename to _startSynchronously while in development
* [Concurrency] StartSynchronously special executor to avoid switching
* startSynchronously bring back more info output
* [Concurrency] startSynchronously with more custom executor tests
* add missing ABI additions to test for x86
* [Concurrency] gyb generate _startSynchronously
* [Concurrency] %import dispatch for Linux startSynchronously test
* [Concurrency] Add TaskGroup.startTaskSynchronously funcs
* [Concurrency] DispatchSerialQueue does not exist on linux still
This entrypoint is similar to swift_task_isCurrentExecutor except that it
provides an ABI level option flag that enables one to configure its behavior in
a backwards deployable manner via the option flag.
I used this to expose at the ABI level the ability to check the current executor
without crashing on failure, while preserving the current behavior of
swift_task_isCurrentExecutor (which crashes on failure).
I am going to use this to implement swift_task_runOnMainActor.
It's possible that the job we enqueue holds the last strong reference to the actor. If that job runs on another thread after we enqueue it, then it's possible for `this` to be destroyed while we're still in this function. We need to use `this` after the enqueue when the priorities don't match. When it looks like that will happen, retain `this` before the enqueue to ensure it stays alive until we're done with it.
Introduce a defensive retain helper class that makes it easy to do a single retain under certain conditions even in a loop, and does RAII to balance it with a release when the scope exits.
rdar://135400933
In embedded mode, we mustn't have references to the C++ library, because
some embedded platforms don't include the C++ library.
Additionally, it's good to avoid use of global operator new and operator
delete, because they can be globally overridden and this has bitten us
in the past.
rdar://137286187