diff --git a/include/swift/ABI/Executor.h b/include/swift/ABI/Executor.h index 1257673826434..5523255c05076 100644 --- a/include/swift/ABI/Executor.h +++ b/include/swift/ABI/Executor.h @@ -17,10 +17,10 @@ #ifndef SWIFT_ABI_EXECUTOR_H #define SWIFT_ABI_EXECUTOR_H -#include #include "swift/ABI/Actor.h" #include "swift/ABI/HeapObject.h" #include "swift/Runtime/Casting.h" +#include namespace swift { class AsyncContext; @@ -413,6 +413,23 @@ class AsyncFunctionPointer { uint32_t ExpectedContextSize; }; +/// Type-safe wrapper around the return value of `isIsolatingCurrentContext`. +enum class IsIsolatingCurrentContextDecision : int8_t { + // The function call could not determine if the current context is isolated + // by this executor or not. Default value for executors which do not implement + // `isIsolatingCurrentContext`. + Unknown = -1, + // The current context is definitely not isolated by this executor. + NotIsolated = 0, + // The current context is definitely isolated by this executor. + Isolated = 1, +}; + +IsIsolatingCurrentContextDecision +getIsIsolatingCurrentContextDecisionFromInt(int8_t value); + +StringRef getIsIsolatingCurrentContextDecisionNameStr(IsIsolatingCurrentContextDecision decision); + } #endif diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 0789767055800..52d6355ebb289 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2984,14 +2984,6 @@ struct TargetProtocolConformanceDescriptor final return Demangle::makeSymbolicMangledNameStringRef(this->template getTrailingObjects>()->type); } - /// True if this is a conformance to 'SerialExecutor' which has a non-default - /// (i.e. not the stdlib's default implementation) witness. This means that - /// the developer has implemented this method explicitly and we should prefer - /// calling it. - bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext() const { - return Flags.hasNonDefaultSerialExecutorIsIsolatingCurrentContext(); - } - /// Retrieve the protocol conformance of the global actor type to the /// GlobalActor protocol. const TargetProtocolConformanceDescriptor * diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index f239ae336c9cb..d16f44e075496 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -754,14 +754,6 @@ class ConformanceFlags { IsConformanceOfProtocolMask = 0x01u << 18, HasGlobalActorIsolation = 0x01u << 19, - // Used to detect if this is a conformance to SerialExecutor that has - // an user defined implementation of 'isIsolatingCurrentContext'. This - // requirement is special in the sense that if a non-default impl is present - // we will avoid calling the `checkIsolated` method which would lead to a - // crash. In other words, this API "soft replaces" 'checkIsolated' so we - // must at runtime the presence of a non-default implementation. - HasNonDefaultSerialExecutorIsIsolatingCurrentContext = 0x01u << 20, - NumConditionalPackDescriptorsMask = 0xFFu << 24, NumConditionalPackDescriptorsShift = 24 }; @@ -828,15 +820,7 @@ class ConformanceFlags { : 0)); } - ConformanceFlags withHasNonDefaultSerialExecutorIsIsolatingCurrentContext( - bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext) const { - return ConformanceFlags((Value & ~HasNonDefaultSerialExecutorIsIsolatingCurrentContext) - | (hasNonDefaultSerialExecutorIsIsolatingCurrentContext - ? HasNonDefaultSerialExecutorIsIsolatingCurrentContext - : 0)); - } - - /// Retrieve the type reference kind kind. + /// Retrieve the type reference kind. TypeReferenceKind getTypeReferenceKind() const { return TypeReferenceKind( (Value & TypeMetadataKindMask) >> TypeMetadataKindShift); @@ -880,10 +864,6 @@ class ConformanceFlags { return Value & HasGlobalActorIsolation; } - bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext() const { - return Value & HasNonDefaultSerialExecutorIsIsolatingCurrentContext; - } - /// Retrieve the # of conditional requirements. unsigned getNumConditionalRequirements() const { return (Value & NumConditionalRequirementsMask) diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index e87ff57ca4e31..f509c2ab36fbf 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -777,12 +777,12 @@ bool swift_task_invokeSwiftCheckIsolated(SerialExecutorRef executor); /// Invoke an executor's `isIsolatingCurrentContext` implementation; SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -bool swift_task_isIsolatingCurrentContext(SerialExecutorRef executor); +int8_t swift_task_isIsolatingCurrentContext(SerialExecutorRef executor); /// Invoke a Swift executor's `isIsolatingCurrentContext` implementation; returns /// `true` if it invoked the Swift implementation, `false` otherwise. SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) -bool swift_task_invokeSwiftIsIsolatingCurrentContext(SerialExecutorRef executor); +int8_t swift_task_invokeSwiftIsIsolatingCurrentContext(SerialExecutorRef executor); /// A count in nanoseconds. using JobDelay = unsigned long long; @@ -1041,12 +1041,6 @@ enum swift_task_is_current_executor_flag : uint64_t { /// The routine MUST NOT assert on failure. /// Even at the cost of not calling 'checkIsolated' if it is available. MustNotAssert = 0x10, - - /// The routine should use 'isIsolatingCurrentContext' function on the - /// 'expected' executor instead of `checkIsolated`. - /// - /// This is a variant of `MustNotAssert` - UseIsIsolatingCurrentContext = 0x20, }; SWIFT_EXPORT_FROM(swift_Concurrency) diff --git a/include/swift/Runtime/ConcurrencyHooks.def b/include/swift/Runtime/ConcurrencyHooks.def index 960753e4411ff..f087fd23e13e2 100644 --- a/include/swift/Runtime/ConcurrencyHooks.def +++ b/include/swift/Runtime/ConcurrencyHooks.def @@ -47,7 +47,7 @@ SWIFT_CONCURRENCY_HOOK(void, swift_task_enqueueGlobalWithDeadline, SWIFT_CONCURRENCY_HOOK(void, swift_task_checkIsolated, SerialExecutorRef executor); -SWIFT_CONCURRENCY_HOOK(bool, swift_task_isIsolatingCurrentContext, +SWIFT_CONCURRENCY_HOOK(int8_t, swift_task_isIsolatingCurrentContext, SerialExecutorRef executor); SWIFT_CONCURRENCY_HOOK(bool, swift_task_isOnExecutor, diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index 3e6d92110386c..108d922a27613 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -2225,7 +2225,6 @@ namespace { Flags = Flags.withIsSynthesizedNonUnique(conf->isSynthesizedNonUnique()); Flags = Flags.withIsConformanceOfProtocol(conf->isConformanceOfProtocol()); Flags = Flags.withHasGlobalActorIsolation(isolation.isGlobalActor()); - Flags = withSerialExecutorCheckingModeFlags(Flags, conf); } else { Flags = Flags.withIsRetroactive(false) .withIsSynthesizedNonUnique(false); @@ -2442,31 +2441,6 @@ namespace { B.addRelativeAddress(globalActorConformanceDescriptor); } - static ConformanceFlags - withSerialExecutorCheckingModeFlags(ConformanceFlags Flags, const NormalProtocolConformance *conf) { - ProtocolDecl *proto = conf->getProtocol(); - auto &C = proto->getASTContext(); - - ConformanceFlags UpdatedFlags = Flags; - if (proto->isSpecificProtocol(swift::KnownProtocolKind::SerialExecutor)) { - conf->forEachValueWitness([&](const ValueDecl *req, - Witness witness) { - bool nameMatch = witness.getDecl()->getBaseIdentifier() == C.Id_isIsolatingCurrentContext; - if (nameMatch) { - if (DeclContext *NominalOrExtension = witness.getDecl()->getDeclContext()) { - // If the witness is NOT the default implementation in the _Concurrency library, - // we should record that this is an user provided implementation and we should call it. - bool hasNonDefaultIsIsolatingCurrentContext = - !NominalOrExtension->getParentModule()->isConcurrencyModule(); - UpdatedFlags = UpdatedFlags.withHasNonDefaultSerialExecutorIsIsolatingCurrentContext( - hasNonDefaultIsIsolatingCurrentContext); - } - } - }); - } - - return UpdatedFlags; - } }; } diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 71ca7325f6e97..c59f05da93d58 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -410,9 +410,6 @@ static void _swift_task_debug_dumpIsCurrentExecutorFlags( if (options.contains(swift_task_is_current_executor_flag::Assert)) SWIFT_TASK_DEBUG_LOG("%s swift_task_is_current_executor_flag::%s", hint, "Assert"); - if (options.contains(swift_task_is_current_executor_flag::UseIsIsolatingCurrentContext)) - SWIFT_TASK_DEBUG_LOG("%s swift_task_is_current_executor_flag::%s", - hint, "UseIsIsolatingCurrentContext"); } // Shimming call to Swift runtime because Swift Embedded does not have @@ -470,13 +467,6 @@ swift_task_is_current_executor_flag swift_bincompat_selectDefaultIsCurrentExecut // Remove the assert option which is what would cause the "crash" mode options = swift_task_is_current_executor_flag( options & ~swift_task_is_current_executor_flag::Assert); - } else if (strcmp(modeStr, "isIsolatingCurrentContext") == 0) { - options = swift_task_is_current_executor_flag( - options | swift_task_is_current_executor_flag::UseIsIsolatingCurrentContext); - // When we're using the isIsolatingCurrentContext we don't want to use crashing APIs, - // so disable it explicitly. - options = swift_task_is_current_executor_flag( - options & ~swift_task_is_current_executor_flag::Assert); } else if (strcmp(modeStr, "crash") == 0 || strcmp(modeStr, "swift6") == 0) { options = swift_task_is_current_executor_flag( @@ -501,43 +491,11 @@ extern "C" SWIFT_CC(swift) void _swift_task_enqueueOnExecutor( Job *job, HeapObject *executor, const Metadata *executorType, const SerialExecutorWitnessTable *wtable); -/// Check the executor's witness table for specific information about e.g. -/// being able ignore `checkIsolated` and only call `isIsolatingCurrentContext`. -static swift_task_is_current_executor_flag -_getIsolationCheckingOptionsFromExecutorWitnessTable(const SerialExecutorWitnessTable *_wtable) { - const WitnessTable* wtable = reinterpret_cast(_wtable); -#if SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES - auto description = lookThroughOptionalConditionalWitnessTable( - reinterpret_cast(wtable)) - ->getDescription(); -#else - auto description = wtable->getDescription(); -#endif - if (!description) { - return swift_task_is_current_executor_flag::None; - } - - if (description->hasNonDefaultSerialExecutorIsIsolatingCurrentContext()) { - // The specific executor has implemented `isIsolatingCurrentContext` and - // we do not have to call `checkIsolated`. - return swift_task_is_current_executor_flag::UseIsIsolatingCurrentContext; - } - - // No changes to the checking mode. - return swift_task_is_current_executor_flag::None; -} - SWIFT_CC(swift) static bool swift_task_isCurrentExecutorWithFlagsImpl( SerialExecutorRef expectedExecutor, swift_task_is_current_executor_flag flags) { auto current = ExecutorTrackingInfo::current(); - if (expectedExecutor.getIdentity() && expectedExecutor.hasSerialExecutorWitnessTable()) { - if (auto *wtable = expectedExecutor.getSerialExecutorWitnessTable()) { - auto executorSpecificMode = _getIsolationCheckingOptionsFromExecutorWitnessTable(wtable); - flags = swift_task_is_current_executor_flag(flags | executorSpecificMode); - } - } auto options = SwiftTaskIsCurrentExecutorOptions(flags); _swift_task_debug_dumpIsCurrentExecutorFlags(__FUNCTION__, flags); @@ -557,16 +515,28 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl( // We cannot use 'complexEquality' as it requires two executor instances, // and we do not have a 'current' executor here. - if (options.contains(swift_task_is_current_executor_flag::UseIsIsolatingCurrentContext)) { - SWIFT_TASK_DEBUG_LOG("executor checking mode option: UseIsIsolatingCurrentContext; invoke (%p).isIsolatingCurrentContext", - expectedExecutor.getIdentity()); - // The executor has the most recent 'isIsolatingCurrentContext' API - // so available so we prefer calling that to 'checkIsolated'. - auto result = swift_task_isIsolatingCurrentContext(expectedExecutor); - - SWIFT_TASK_DEBUG_LOG("executor checking mode option: UseIsIsolatingCurrentContext; invoke (%p).isIsolatingCurrentContext => %s", - expectedExecutor.getIdentity(), result ? "pass" : "fail"); - return result; + // Invoke the 'isIsolatingCurrentContext', if "undecided" (i.e. nil), we need to make further calls + SWIFT_TASK_DEBUG_LOG("executor checking, invoke (%p).isIsolatingCurrentContext", + expectedExecutor.getIdentity()); + // The executor has the most recent 'isIsolatingCurrentContext' API + // so available so we prefer calling that to 'checkIsolated'. + auto isIsolatingCurrentContextDecision = + getIsIsolatingCurrentContextDecisionFromInt( + swift_task_isIsolatingCurrentContext(expectedExecutor)); + + SWIFT_TASK_DEBUG_LOG("executor checking mode option: UseIsIsolatingCurrentContext; invoke (%p).isIsolatingCurrentContext => %s", + expectedExecutor.getIdentity(), getIsIsolatingCurrentContextDecisionNameStr(isIsolatingCurrentContextDecision)); + switch (isIsolatingCurrentContextDecision) { + case IsIsolatingCurrentContextDecision::Isolated: + // We know for sure that this serial executor is isolating this context, return the decision. + return true; + case IsIsolatingCurrentContextDecision::NotIsolated: + // We know for sure that this serial executor is NOT isolating this context, return this decision. + return false; + case IsIsolatingCurrentContextDecision::Unknown: + // We don't know, so we have to continue trying to check using other methods. + // This most frequently would happen if a serial executor did not implement isIsolatingCurrentContext. + break; } // Otherwise, as last resort, let the expected executor check using @@ -675,18 +645,21 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl( // Invoke the 'isIsolatingCurrentContext' function if we can; If so, we can // avoid calling the `checkIsolated` because their result will be the same. - if (options.contains(swift_task_is_current_executor_flag::UseIsIsolatingCurrentContext)) { - SWIFT_TASK_DEBUG_LOG("executor checking: can call (%p).isIsolatingCurrentContext", - expectedExecutor.getIdentity()); + SWIFT_TASK_DEBUG_LOG("executor checking: call (%p).isIsolatingCurrentContext", + expectedExecutor.getIdentity()); - bool checkResult = swift_task_isIsolatingCurrentContext(expectedExecutor); + const auto isIsolatingCurrentContextDecision = + getIsIsolatingCurrentContextDecisionFromInt(swift_task_isIsolatingCurrentContext(expectedExecutor)); - SWIFT_TASK_DEBUG_LOG("executor checking: can call (%p).isIsolatingCurrentContext => %p", - expectedExecutor.getIdentity(), checkResult ? "pass" : "fail"); - return checkResult; - } else { - SWIFT_TASK_DEBUG_LOG("executor checking: can NOT call (%p).isIsolatingCurrentContext", - expectedExecutor.getIdentity()); + SWIFT_TASK_DEBUG_LOG("executor checking: can call (%p).isIsolatingCurrentContext => %p", + expectedExecutor.getIdentity(), getIsIsolatingCurrentContextDecisionNameStr(isIsolatingCurrentContextDecision)); + switch (isIsolatingCurrentContextDecision) { + case IsIsolatingCurrentContextDecision::Isolated: + return true; + case IsIsolatingCurrentContextDecision::NotIsolated: + return false; + case IsIsolatingCurrentContextDecision::Unknown: + break; } // This provides a last-resort check by giving the expected SerialExecutor the diff --git a/stdlib/public/Concurrency/ConcurrencyHooks.cpp b/stdlib/public/Concurrency/ConcurrencyHooks.cpp index 26fd5c8159954..642f887e133c0 100644 --- a/stdlib/public/Concurrency/ConcurrencyHooks.cpp +++ b/stdlib/public/Concurrency/ConcurrencyHooks.cpp @@ -111,18 +111,20 @@ swift::swift_task_checkIsolated(SerialExecutorRef executor) { swift_task_checkIsolatedOrig(executor); } -SWIFT_CC(swift) static bool +SWIFT_CC(swift) static int8_t swift_task_isIsolatingCurrentContextOrig(SerialExecutorRef executor) { return swift_task_isIsolatingCurrentContextImpl( *reinterpret_cast(&executor)); } -bool +int8_t swift::swift_task_isIsolatingCurrentContext(SerialExecutorRef executor) { - if (SWIFT_UNLIKELY(swift_task_isIsolatingCurrentContext_hook)) - return swift_task_isIsolatingCurrentContext_hook(executor, swift_task_isIsolatingCurrentContextOrig); - else + if (SWIFT_UNLIKELY(swift_task_isIsolatingCurrentContext_hook)) { + return swift_task_isIsolatingCurrentContext_hook( + executor, swift_task_isIsolatingCurrentContextOrig); + } else { return swift_task_isIsolatingCurrentContextOrig(executor); + } } // Implemented in Swift because we need to obtain the user-defined flags on the executor ref. diff --git a/stdlib/public/Concurrency/CooperativeGlobalExecutor.cpp b/stdlib/public/Concurrency/CooperativeGlobalExecutor.cpp index 7cc503e836c25..647b90421d798 100644 --- a/stdlib/public/Concurrency/CooperativeGlobalExecutor.cpp +++ b/stdlib/public/Concurrency/CooperativeGlobalExecutor.cpp @@ -155,7 +155,7 @@ void swift_task_checkIsolatedImpl(SwiftExecutorRef executor) { } SWIFT_CC(swift) -bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) { +int8_t swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) { return swift_executor_invokeSwiftIsIsolatingCurrentContext(executor); } diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index a4ec7618b6829..0b1dbc3644cd2 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -343,8 +343,25 @@ public protocol SerialExecutor: Executor { @available(SwiftStdlib 6.0, *) func checkIsolated() + /// Checks if the current execution context is isolated by this executor. + /// + /// This function can be called by the runtime in order to perform assertions, + /// or attempt to issue warnings about unexpected isolation. + /// + /// This method will be invoked _before_ `checkIsolated` and may also be invoked + /// when crashing is not an acceptable outcome of a check (e.g. when attempting to issue isolation _warnings_). + /// + /// Implementations should prefer to implement this method rather than `checkIsolated()` since it can often + /// result in more tailored error messages. It is allowed, and useful for backwards compatibility with old + /// runtimes which are not able to invoke `isIsolatingCurrentContext()` to implement `checkIsolated()`, + /// even if an implementation is able to implement this method. Often times an implementation of `checkIsolated()`, + /// would then invoke `isIsolatingCurrentContext()` and crash if the returned value was `false`. + /// + /// The default implementation returns `nil` is used to indicate that it is "unknown" if the current context is + /// isolated by this serial executor. The runtime then _may_ proceed to invoke `checkIsolated()` as a last-resort + /// attempt to verify the isolation of the current context. @available(SwiftStdlib 6.2, *) - func isIsolatingCurrentContext() -> Bool + func isIsolatingCurrentContext() -> Bool? } @@ -381,9 +398,8 @@ extension SerialExecutor { extension SerialExecutor { @available(SwiftStdlib 6.2, *) - public func isIsolatingCurrentContext() -> Bool { - self.checkIsolated() - return true + public func isIsolatingCurrentContext() -> Bool? { + return nil } } @@ -865,9 +881,13 @@ internal func _task_serialExecutor_checkIsolated(executor: E) @available(SwiftStdlib 6.2, *) @_silgen_name("_task_serialExecutor_isIsolatingCurrentContext") -internal func _task_serialExecutor_isIsolatingCurrentContext(executor: E) -> Bool - where E: SerialExecutor { - return executor.isIsolatingCurrentContext() +internal func _task_serialExecutor_isIsolatingCurrentContext(executor: E) -> Int8 + where E: SerialExecutor { + switch executor.isIsolatingCurrentContext() { + case nil: -1 // unknown + case .some(false): 0 // not isolated + case .some(true): 1 // isolated! + } } /// Obtain the executor ref by calling the executor's `asUnownedSerialExecutor()`. diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift index db718d53aa334..d160d424acc44 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.swift +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -35,12 +35,20 @@ internal func checkIsolated(executor: E) where E: SerialExecutor { executor.checkIsolated() } +/// Invokes the swift function isIsolatingCurrentContext on the given executor, +/// and converts between the `Optional` into: +/// -1: unknown +/// 0: not isolated +/// 1: isolated @available(SwiftStdlib 6.2, *) @_silgen_name("_swift_task_isIsolatingCurrentContextSwift") -internal func isIsolatingCurrentContext(executor: E) -> Bool - where E: SerialExecutor -{ - return executor.isIsolatingCurrentContext() +internal func isIsolatingCurrentContext(executor: E) -> Int8 + where E: SerialExecutor { + switch executor.isIsolatingCurrentContext() { + case nil: -1 // unknown + case .some(false): 0 // not isolated + case .some(true): 1 // isolated! + } } @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/ExecutorImpl.cpp b/stdlib/public/Concurrency/ExecutorImpl.cpp index 5f8e1aa45a0dc..74bc4c13987d4 100644 --- a/stdlib/public/Concurrency/ExecutorImpl.cpp +++ b/stdlib/public/Concurrency/ExecutorImpl.cpp @@ -56,31 +56,26 @@ extern "C" SWIFT_CC(swift) void swift_task_checkIsolatedImpl( extern "C" SWIFT_CC(swift) -bool _swift_task_isIsolatingCurrentContextSwift( +int8_t _swift_task_isIsolatingCurrentContextSwift( HeapObject *executor, const Metadata *executorType, const SerialExecutorWitnessTable *witnessTable ); -extern "C" SWIFT_CC(swift) bool swift_task_isIsolatingCurrentContextImpl( +extern "C" SWIFT_CC(swift) int8_t +swift_task_isIsolatingCurrentContextImpl( SerialExecutorRef executor) { HeapObject *identity = executor.getIdentity(); // We might be being called with an actor rather than a "proper" // SerialExecutor; in that case, we won't have a SerialExecutor witness // table. - if (executor.hasSerialExecutorWitnessTable()) { - return _swift_task_isIsolatingCurrentContextSwift( - identity, swift_getObjectType(identity), - executor.getSerialExecutorWitnessTable()); - } else { - const Metadata *objectType = swift_getObjectType(executor.getIdentity()); - auto typeName = swift_getTypeName(objectType, true); + if (!executor.hasSerialExecutorWitnessTable()) + return static_cast(IsIsolatingCurrentContextDecision::Unknown); - swift_Concurrency_fatalError( - 0, "Incorrect actor executor assumption; expected '%.*s' executor.\n", - (int)typeName.length, typeName.data); - } + return _swift_task_isIsolatingCurrentContextSwift( + identity, swift_getObjectType(identity), + executor.getSerialExecutorWitnessTable()); } extern "C" SWIFT_CC(swift) bool swift_task_isMainExecutorImpl( diff --git a/stdlib/public/Concurrency/ExecutorImpl.h b/stdlib/public/Concurrency/ExecutorImpl.h index a068c456b3c05..44fb28a904581 100644 --- a/stdlib/public/Concurrency/ExecutorImpl.h +++ b/stdlib/public/Concurrency/ExecutorImpl.h @@ -227,9 +227,14 @@ swift_executor_invokeSwiftCheckIsolated(SwiftExecutorRef executor) { } /// Check if the current context is isolated by the specified executor. -static inline bool +/// +/// The numeric values correspond to `swift::IsIsolatingCurrentContextDecision`. +/// +/// Specifically ONLY `1` means "isolated", while smaller values mean not isolated or unknown. +/// See ``IsIsolatingCurrentContextDecision`` for details. +static inline int8_t swift_executor_invokeSwiftIsIsolatingCurrentContext(SwiftExecutorRef executor) { - extern bool _swift_task_invokeSwiftIsIsolatingCurrentContext_c(SwiftExecutorRef executor); + extern int8_t _swift_task_invokeSwiftIsIsolatingCurrentContext_c(SwiftExecutorRef executor); return _swift_task_invokeSwiftIsIsolatingCurrentContext_c(executor); } @@ -292,7 +297,8 @@ SWIFT_CC(swift) void swift_task_enqueueMainExecutorImpl(SwiftJob * _Nonnull job) SWIFT_CC(swift) void swift_task_checkIsolatedImpl(SwiftExecutorRef executor); /// Check if the specified executor isolates the current context. -SWIFT_CC(swift) bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor); +SWIFT_CC(swift) int8_t + swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor); /// Get a reference to the main executor. SWIFT_CC(swift) SwiftExecutorRef swift_task_getMainExecutorImpl(void); diff --git a/stdlib/public/Concurrency/GlobalExecutor.cpp b/stdlib/public/Concurrency/GlobalExecutor.cpp index 833abb1a02f15..788ee0afeecc3 100644 --- a/stdlib/public/Concurrency/GlobalExecutor.cpp +++ b/stdlib/public/Concurrency/GlobalExecutor.cpp @@ -88,25 +88,31 @@ extern "C" bool _swift_task_invokeSwiftCheckIsolated_c(SwiftExecutorRef executor extern "C" SWIFT_CC(swift) -bool _task_serialExecutor_isIsolatingCurrentContext( +int8_t _task_serialExecutor_isIsolatingCurrentContext( HeapObject *executor, const Metadata *selfType, const SerialExecutorWitnessTable *wtable); -SWIFT_CC(swift) -bool swift::swift_task_invokeSwiftIsIsolatingCurrentContext(SerialExecutorRef executor) +SWIFT_CC(swift) int8_t +swift::swift_task_invokeSwiftIsIsolatingCurrentContext(SerialExecutorRef executor) { - if (!executor.hasSerialExecutorWitnessTable()) - return false; + if (!executor.hasSerialExecutorWitnessTable()) { + return static_cast(IsIsolatingCurrentContextDecision::NotIsolated); + } - return _task_serialExecutor_isIsolatingCurrentContext( + auto decision = _task_serialExecutor_isIsolatingCurrentContext( executor.getIdentity(), swift_getObjectType(executor.getIdentity()), executor.getSerialExecutorWitnessTable()); + + return decision; } -extern "C" bool _swift_task_invokeSwiftIsIsolatingCurrentContext_c(SwiftExecutorRef executor) +extern "C" int8_t +_swift_task_invokeSwiftIsIsolatingCurrentContext_c(SwiftExecutorRef executor) { - return swift_task_invokeSwiftIsIsolatingCurrentContext(*reinterpret_cast(&executor)); + return + static_cast(swift_task_invokeSwiftIsIsolatingCurrentContext( + *reinterpret_cast(&executor))); } extern "C" void _swift_job_run_c(SwiftJob *job, SwiftExecutorRef executor) @@ -155,6 +161,27 @@ extern "C" void swift_job_dealloc(SwiftJob *job, void *ptr) { return _swift_task_dealloc_specific(task, ptr); } +IsIsolatingCurrentContextDecision +swift::getIsIsolatingCurrentContextDecisionFromInt(int8_t value) { + switch (value) { + case -1: return IsIsolatingCurrentContextDecision::Unknown; + case 0: return IsIsolatingCurrentContextDecision::NotIsolated; + case 1: return IsIsolatingCurrentContextDecision::Isolated; + default: + swift_Concurrency_fatalError(0, "Unexpected IsIsolatingCurrentContextDecision value"); + } +} + +StringRef +swift::getIsIsolatingCurrentContextDecisionNameStr(IsIsolatingCurrentContextDecision decision) { + switch (decision) { + case IsIsolatingCurrentContextDecision::Unknown: return "Unknown"; + case IsIsolatingCurrentContextDecision::NotIsolated: return "NotIsolated"; + case IsIsolatingCurrentContextDecision::Isolated: return "Isolated"; + } + swift_Concurrency_fatalError(0, "Unexpected IsIsolatingCurrentContextDecision value"); +} + /*****************************************************************************/ /****************************** MAIN EXECUTOR *******************************/ /*****************************************************************************/ diff --git a/test/Concurrency/Runtime/actor_assert_precondition_executor_isIsolatingCurrentContext_swift62_mode.swift b/test/Concurrency/Runtime/actor_assert_precondition_executor_isIsolatingCurrentContext_swift62_mode.swift index e651b7f1abfcf..5cd7a28976288 100644 --- a/test/Concurrency/Runtime/actor_assert_precondition_executor_isIsolatingCurrentContext_swift62_mode.swift +++ b/test/Concurrency/Runtime/actor_assert_precondition_executor_isIsolatingCurrentContext_swift62_mode.swift @@ -30,7 +30,7 @@ final class IsIsolatingExecutor: SerialExecutor { print("called: checkIsolated") } - func isIsolatingCurrentContext() -> Bool { + func isIsolatingCurrentContext() -> Bool? { print("called: isIsolatingCurrentContext") return true } diff --git a/test/Concurrency/Runtime/actor_assert_precondition_executor_not_implemented_isIsolatingCurrentContext_swift62_mode.swift b/test/Concurrency/Runtime/actor_assert_precondition_executor_not_implemented_isIsolatingCurrentContext_swift62_mode.swift index dc83d4d708ce8..05f79173eabfd 100644 --- a/test/Concurrency/Runtime/actor_assert_precondition_executor_not_implemented_isIsolatingCurrentContext_swift62_mode.swift +++ b/test/Concurrency/Runtime/actor_assert_precondition_executor_not_implemented_isIsolatingCurrentContext_swift62_mode.swift @@ -30,7 +30,7 @@ final class IsIsolatingExecutor: SerialExecutor { print("called: checkIsolated") } - func isIsolatingCurrentContext() -> Bool { + func isIsolatingCurrentContext() -> Bool? { print("called: isIsolatingCurrentContext") return true } diff --git a/test/Concurrency/Runtime/actor_isIsolatingCurrentContext.swift b/test/Concurrency/Runtime/actor_isIsolatingCurrentContext.swift index 1a43d7ca584a9..d5667edeb4c97 100644 --- a/test/Concurrency/Runtime/actor_isIsolatingCurrentContext.swift +++ b/test/Concurrency/Runtime/actor_isIsolatingCurrentContext.swift @@ -30,12 +30,34 @@ final class IsIsolatingExecutor: SerialExecutor { print("called: checkIsolated") } - func isIsolatingCurrentContext() -> Bool { + func isIsolatingCurrentContext() -> Bool? { print("called: isIsolatingCurrentContext") return true } } +@available(SwiftStdlib 6.2, *) +final class UnknownIfIsIsolatingExecutor: SerialExecutor { + init() {} + + func enqueue(_ job: consuming ExecutorJob) { + job.runSynchronously(on: self.asUnownedSerialExecutor()) + } + + func asUnownedSerialExecutor() -> UnownedSerialExecutor { + UnownedSerialExecutor(ordinary: self) + } + + func checkIsolated() { + print("called: checkIsolated") + } + + func isIsolatingCurrentContext() -> Bool? { + print("called: isIsolatingCurrentContext; return nil") + return nil + } +} + @available(SwiftStdlib 6.2, *) final class NoChecksImplementedExecutor: SerialExecutor { init() {} @@ -78,7 +100,7 @@ actor ActorOnIsCheckImplementingExecutor { self.executor.asUnownedSerialExecutor() } - func checkIsIsolatingCurrentContext() async -> Bool { + func checkIsIsolatingCurrentContext() async -> Bool? { executor.isIsolatingCurrentContext() } } @@ -87,6 +109,7 @@ actor ActorOnIsCheckImplementingExecutor { static func main() async { let hasIsIsolatingCurrentContextExecutor = IsIsolatingExecutor() let hasIsCheckActor = ActorOnIsCheckImplementingExecutor(on: hasIsIsolatingCurrentContextExecutor) + let unknownIsIsolatingActor = ActorOnIsCheckImplementingExecutor(on: UnknownIfIsIsolatingExecutor()) let anyActor: any Actor = hasIsCheckActor @@ -99,5 +122,9 @@ actor ActorOnIsCheckImplementingExecutor { let inside = await hasIsCheckActor.checkIsIsolatingCurrentContext() assert(inside == true) // CHECK: called: isIsolatingCurrentContext + + _ = unknownIsIsolatingActor.preconditionIsolated() + // CHECK: called: isIsolatingCurrentContext; return nil + // CHECK: called: checkIsolated } } diff --git a/test/abi/macOS/arm64/concurrency.swift b/test/abi/macOS/arm64/concurrency.swift index a821e4a270556..39f9999d72878 100644 --- a/test/abi/macOS/arm64/concurrency.swift +++ b/test/abi/macOS/arm64/concurrency.swift @@ -404,12 +404,13 @@ Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8pri Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF +// isIsolatingCurrentContext Added: _swift_task_invokeSwiftIsIsolatingCurrentContext Added: _swift_task_isIsolatingCurrentContext Added: _swift_task_isIsolatingCurrentContext_hook -Added: _$sScf25isIsolatingCurrentContextSbyFTj -Added: _$sScf25isIsolatingCurrentContextSbyFTq -Added: _$sScfsE25isIsolatingCurrentContextSbyF +Added: _$sScfsE25isIsolatingCurrentContextSbSgyF +Added: _$sScf25isIsolatingCurrentContextSbSgyFTj +Added: _$sScf25isIsolatingCurrentContextSbSgyFTq // CoroutineAccessors Added: _swift_task_dealloc_through diff --git a/test/abi/macOS/x86_64/concurrency.swift b/test/abi/macOS/x86_64/concurrency.swift index 6fed1d757d715..8e406898f8eb6 100644 --- a/test/abi/macOS/x86_64/concurrency.swift +++ b/test/abi/macOS/x86_64/concurrency.swift @@ -404,12 +404,13 @@ Added: _$ss19DiscardingTaskGroupV05startB28SynchronouslyUnlessCancelled4name8pri Added: _$ss27ThrowingDiscardingTaskGroupV05startC13Synchronously4name8priority9operationySSSg_ScPSgyyYaKYAcntF Added: _$ss27ThrowingDiscardingTaskGroupV05startC28SynchronouslyUnlessCancelled4name8priority9operationySSSg_ScPSgyyYaKYAcntF +// isIsolatingCurrentContext Added: _swift_task_invokeSwiftIsIsolatingCurrentContext Added: _swift_task_isIsolatingCurrentContext Added: _swift_task_isIsolatingCurrentContext_hook -Added: _$sScf25isIsolatingCurrentContextSbyFTj -Added: _$sScf25isIsolatingCurrentContextSbyFTq -Added: _$sScfsE25isIsolatingCurrentContextSbyF +Added: _$sScfsE25isIsolatingCurrentContextSbSgyF +Added: _$sScf25isIsolatingCurrentContextSbSgyFTj +Added: _$sScf25isIsolatingCurrentContextSbSgyFTq // CoroutineAccessors Added: _swift_task_dealloc_through diff --git a/test/embedded/Inputs/executor.c b/test/embedded/Inputs/executor.c index 7032b3af833d3..cf5f9455fc565 100644 --- a/test/embedded/Inputs/executor.c +++ b/test/embedded/Inputs/executor.c @@ -308,7 +308,7 @@ void swift_task_checkIsolatedImpl(SwiftExecutorRef executor) { /// Check if the specified executor is the current executor. SWIFT_CC(swift) -bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) { +int8_t swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) { return swift_executor_invokeSwiftIsIsolatingCurrentContext(executor); } diff --git a/unittests/runtime/CompatibilityOverrideConcurrency.cpp b/unittests/runtime/CompatibilityOverrideConcurrency.cpp index 5a4358a635170..2f220bcf177ed 100644 --- a/unittests/runtime/CompatibilityOverrideConcurrency.cpp +++ b/unittests/runtime/CompatibilityOverrideConcurrency.cpp @@ -92,11 +92,11 @@ swift_task_checkIsolated_override(SerialExecutorRef executor, } SWIFT_CC(swift) -static bool +static int8_t swift_task_isIsolatingCurrentContext_override(SerialExecutorRef executor, swift_task_isIsolatingCurrentContext_original original) { Ran = true; - return true; + return 0; } SWIFT_CC(swift)