mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Concurrency] Implement detecting isIsolatingCurrentContext user impls (#79946)
* [Concurrency] Detect non-default impls of isIsolatingCurrentContext * [Concurrency] No need for trailing info about isIsolating... in conformance * Apply changes from review
This commit is contained in:
committed by
GitHub
parent
1c78d0c683
commit
85fcd69833
@@ -2930,6 +2930,14 @@ public:
|
||||
return Demangle::makeSymbolicMangledNameStringRef(this->template getTrailingObjects<TargetGlobalActorReference<Runtime>>()->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<Runtime> *
|
||||
|
||||
@@ -748,6 +748,14 @@ private:
|
||||
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
|
||||
};
|
||||
@@ -813,7 +821,15 @@ public:
|
||||
? HasGlobalActorIsolation
|
||||
: 0));
|
||||
}
|
||||
|
||||
|
||||
ConformanceFlags withHasNonDefaultSerialExecutorIsIsolatingCurrentContext(
|
||||
bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext) const {
|
||||
return ConformanceFlags((Value & ~HasNonDefaultSerialExecutorIsIsolatingCurrentContext)
|
||||
| (hasNonDefaultSerialExecutorIsIsolatingCurrentContext
|
||||
? HasNonDefaultSerialExecutorIsIsolatingCurrentContext
|
||||
: 0));
|
||||
}
|
||||
|
||||
/// Retrieve the type reference kind kind.
|
||||
TypeReferenceKind getTypeReferenceKind() const {
|
||||
return TypeReferenceKind(
|
||||
@@ -858,6 +874,10 @@ public:
|
||||
return Value & HasGlobalActorIsolation;
|
||||
}
|
||||
|
||||
bool hasNonDefaultSerialExecutorIsIsolatingCurrentContext() const {
|
||||
return Value & HasNonDefaultSerialExecutorIsIsolatingCurrentContext;
|
||||
}
|
||||
|
||||
/// Retrieve the # of conditional requirements.
|
||||
unsigned getNumConditionalRequirements() const {
|
||||
return (Value & NumConditionalRequirementsMask)
|
||||
|
||||
@@ -118,6 +118,7 @@ IDENTIFIER(makeIterator)
|
||||
IDENTIFIER(makeAsyncIterator)
|
||||
IDENTIFIER(nestedContainer)
|
||||
IDENTIFIER(isEmpty)
|
||||
IDENTIFIER(isIsolatingCurrentContext)
|
||||
IDENTIFIER(isolation)
|
||||
IDENTIFIER(Iterator)
|
||||
IDENTIFIER(AsyncIterator)
|
||||
|
||||
@@ -1158,6 +1158,9 @@ public:
|
||||
/// \returns true if this module is the "swift" standard library module.
|
||||
bool isStdlibModule() const;
|
||||
|
||||
/// \returns true if this module is the "_Concurrency" standard library module.
|
||||
bool isConcurrencyModule() const;
|
||||
|
||||
/// \returns true if this module has standard substitutions for mangling.
|
||||
bool hasStandardSubstitutions() const;
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ bool useLegacySwiftObjCHashing();
|
||||
/// - if not available, it will invoke the the *crashing* 'checkIsolated'
|
||||
///
|
||||
/// This can be overridden by using `SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=1`
|
||||
/// or `SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=crash|nocrash|swift6|swift6.2`
|
||||
/// or `SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=crash|nocrash|swift6|isIsolatingCurrentContext`
|
||||
SWIFT_RUNTIME_STDLIB_SPI
|
||||
bool swift_bincompat_useLegacyNonCrashingExecutorChecks();
|
||||
|
||||
|
||||
@@ -1023,6 +1023,7 @@ bool swift_task_isCurrentExecutor(SerialExecutorRef executor);
|
||||
/// this could be a pointer to a different enum instance if we need it to be.
|
||||
enum swift_task_is_current_executor_flag : uint64_t {
|
||||
/// We aren't passing any flags.
|
||||
/// Effectively this is a backwards compatible mode.
|
||||
None = 0x0,
|
||||
|
||||
/// This is not used today, but is just future ABI reservation.
|
||||
@@ -1047,9 +1048,15 @@ enum swift_task_is_current_executor_flag : uint64_t {
|
||||
/// The routine should assert on failure.
|
||||
Assert = 0x8,
|
||||
|
||||
/// 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`.
|
||||
HasIsIsolatingCurrentContext = 0x10,
|
||||
///
|
||||
/// This is a variant of `MustNotAssert`
|
||||
UseIsIsolatingCurrentContext = 0x20,
|
||||
};
|
||||
|
||||
SWIFT_EXPORT_FROM(swift_Concurrency)
|
||||
|
||||
@@ -2031,6 +2031,10 @@ bool ModuleDecl::isStdlibModule() const {
|
||||
return !getParent() && getName() == getASTContext().StdlibModuleName;
|
||||
}
|
||||
|
||||
bool ModuleDecl::isConcurrencyModule() const {
|
||||
return !getParent() && getName() == getASTContext().Id_Concurrency;
|
||||
}
|
||||
|
||||
bool ModuleDecl::hasStandardSubstitutions() const {
|
||||
return !getParent() &&
|
||||
(getName() == getASTContext().StdlibModuleName ||
|
||||
@@ -4285,11 +4289,6 @@ struct SwiftSettingsWalker : ASTWalker {
|
||||
|
||||
} // namespace
|
||||
|
||||
static bool isConcurrencyModule(DeclContext *dc) {
|
||||
auto *m = dc->getParentModule();
|
||||
return !m->getParent() && m->getName() == m->getASTContext().Id_Concurrency;
|
||||
}
|
||||
|
||||
bool SwiftSettingsWalker::isSwiftSettingsMacroExpr(
|
||||
MacroExpansionExpr *macroExpr) {
|
||||
// First make sure we actually have a macro with the name SwiftSettings.
|
||||
@@ -4406,7 +4405,8 @@ SwiftSettingsWalker::patternMatchDefaultIsolationMainActor(CallExpr *callExpr) {
|
||||
if (!nomDecl)
|
||||
return CanType();
|
||||
auto *nomDeclDC = nomDecl->getDeclContext();
|
||||
if (!nomDeclDC->isModuleScopeContext() || !isConcurrencyModule(nomDeclDC))
|
||||
auto *nomDeclModule = nomDecl->getParentModule();
|
||||
if (!nomDeclDC->isModuleScopeContext() || !nomDeclModule->isConcurrencyModule())
|
||||
return CanType();
|
||||
|
||||
return nomDecl->getDeclaredType()->getCanonicalType();
|
||||
|
||||
@@ -2238,6 +2238,7 @@ 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);
|
||||
@@ -2459,6 +2460,32 @@ namespace {
|
||||
rootGlobalActorConformance));
|
||||
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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -351,17 +351,43 @@ enum IsCurrentExecutorCheckMode : unsigned {
|
||||
Legacy_NoCheckIsolated_NonCrashing,
|
||||
};
|
||||
|
||||
namespace {
|
||||
using SwiftTaskIsCurrentExecutorOptions =
|
||||
OptionSet<swift_task_is_current_executor_flag>;
|
||||
}
|
||||
|
||||
static void _swift_task_debug_dumpIsCurrentExecutorFlags(
|
||||
const char *hint,
|
||||
swift_task_is_current_executor_flag flags) {
|
||||
if (flags == swift_task_is_current_executor_flag::None) {
|
||||
SWIFT_TASK_DEBUG_LOG("%s swift_task_is_current_executor_flag::%s",
|
||||
hint, "None");
|
||||
return;
|
||||
}
|
||||
|
||||
auto options = SwiftTaskIsCurrentExecutorOptions(flags);
|
||||
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
|
||||
// these symbols defined.
|
||||
swift_task_is_current_executor_flag
|
||||
__swift_bincompat_useLegacyNonCrashingExecutorChecks() {
|
||||
swift_task_is_current_executor_flag options = swift_task_is_current_executor_flag::None;
|
||||
#if !SWIFT_CONCURRENCY_EMBEDDED
|
||||
if (swift::runtime::bincompat::
|
||||
if (!swift::runtime::bincompat::
|
||||
swift_bincompat_useLegacyNonCrashingExecutorChecks()) {
|
||||
return swift_task_is_current_executor_flag::None;
|
||||
options = swift_task_is_current_executor_flag(
|
||||
options | swift_task_is_current_executor_flag::Assert);
|
||||
}
|
||||
#endif
|
||||
return swift_task_is_current_executor_flag::Assert;
|
||||
_swift_task_debug_dumpIsCurrentExecutorFlags("runtime linking determined default mode", options);
|
||||
return options;
|
||||
}
|
||||
|
||||
// Shimming call to Swift runtime because Swift Embedded does not have
|
||||
@@ -376,47 +402,47 @@ const char *__swift_runtime_env_useLegacyNonCrashingExecutorChecks() {
|
||||
#endif
|
||||
}
|
||||
|
||||
// Determine the default effective executor checking mode, and apply environment
|
||||
// variable overrides of the executor checking mode.
|
||||
|
||||
// Done this way because of the interaction with the initial value of
|
||||
// 'unexpectedExecutorLogLevel'
|
||||
swift_task_is_current_executor_flag swift_bincompat_useLegacyNonCrashingExecutorChecks() {
|
||||
// 'unexpectedExecutorLogLevel'.
|
||||
swift_task_is_current_executor_flag swift_bincompat_selectDefaultIsCurrentExecutorCheckingMode() {
|
||||
// Default options as determined by linked runtime,
|
||||
// i.e. very old runtimes were not allowed to crash but then we introduced 'checkIsolated'
|
||||
// which was allowed to crash;
|
||||
swift_task_is_current_executor_flag options =
|
||||
__swift_bincompat_useLegacyNonCrashingExecutorChecks();
|
||||
|
||||
// Potentially, override the platform detected mode, primarily used in tests.
|
||||
if (const char *modeStr =
|
||||
__swift_runtime_env_useLegacyNonCrashingExecutorChecks()) {
|
||||
|
||||
if (strlen(modeStr) == 0) {
|
||||
return swift_task_is_current_executor_flag::None;
|
||||
_swift_task_debug_dumpIsCurrentExecutorFlags("mode override is empty", options);
|
||||
return options;
|
||||
}
|
||||
|
||||
if (strcmp(modeStr, "nocrash") == 0 ||
|
||||
strcmp(modeStr, "legacy") == 0) {
|
||||
// Since we're in nocrash/legacy mode:
|
||||
// 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);
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE = %s => NONE; options = %d",
|
||||
modeStr, options);
|
||||
} else if (strcmp(modeStr, "swift6.2") == 0) {
|
||||
// Since we're in nocrash/legacy mode:
|
||||
// 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::HasIsIsolatingCurrentContext);
|
||||
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);
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE = %s => 6.2; options = %d", modeStr, options);
|
||||
} else if (strcmp(modeStr, "crash") == 0 ||
|
||||
strcmp(modeStr, "swift6") == 0) {
|
||||
options = swift_task_is_current_executor_flag(
|
||||
options | swift_task_is_current_executor_flag::Assert);
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE = %s => 6.9; options = %d", modeStr, options);
|
||||
}
|
||||
// else { // else, just use the platform detected mode
|
||||
// assert(strlen(modeStr) > 0 && "why is it empty!");
|
||||
// }
|
||||
} // else, just use the platform detected mode
|
||||
} // no override, use the default mode
|
||||
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: final options = %d", options);
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -434,24 +460,40 @@ extern "C" SWIFT_CC(swift) void _swift_task_enqueueOnExecutor(
|
||||
Job *job, HeapObject *executor, const Metadata *executorType,
|
||||
const SerialExecutorWitnessTable *wtable);
|
||||
|
||||
namespace {
|
||||
using SwiftTaskIsCurrentExecutorOptions =
|
||||
OptionSet<swift_task_is_current_executor_flag>;
|
||||
/// 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<const WitnessTable*>(_wtable);
|
||||
auto description = wtable->getDescription();
|
||||
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 options = SwiftTaskIsCurrentExecutorOptions(flags);
|
||||
auto current = ExecutorTrackingInfo::current();
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: current task %p", current);
|
||||
#ifndef NDEBUG
|
||||
if (options.contains(swift_task_is_current_executor_flag::Assert))
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: active option = Assert (%d)", flags);
|
||||
if (options.contains(swift_task_is_current_executor_flag::HasIsIsolatingCurrentContext))
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: active option = HasIsIsolatingCurrentContext (%d)", flags);
|
||||
#endif
|
||||
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);
|
||||
|
||||
if (!current) {
|
||||
// We have no current executor, i.e. we are running "outside" of Swift
|
||||
@@ -468,14 +510,14 @@ 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::HasIsIsolatingCurrentContext)) {
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking mode option: HasIsIsolatingCurrentContext; invoke (%p).isIsolatingCurrentContext",
|
||||
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: HasIsIsolatingCurrentContext; invoke (%p).isIsolatingCurrentContext => %s",
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking mode option: UseIsIsolatingCurrentContext; invoke (%p).isIsolatingCurrentContext => %s",
|
||||
expectedExecutor.getIdentity(), result ? "pass" : "fail");
|
||||
return result;
|
||||
}
|
||||
@@ -553,6 +595,8 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
|
||||
// We may be able to prove we're on the same executor as expected by
|
||||
// using 'checkIsolated' later on though.
|
||||
if (expectedExecutor.isComplexEquality()) {
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: expectedExecutor is complex equality (%p)",
|
||||
expectedExecutor.getIdentity());
|
||||
if (currentExecutor.getIdentity() &&
|
||||
currentExecutor.hasSerialExecutorWitnessTable() &&
|
||||
expectedExecutor.getIdentity() &&
|
||||
@@ -582,16 +626,20 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
|
||||
}
|
||||
}
|
||||
|
||||
if (options.contains(swift_task_is_current_executor_flag::HasIsIsolatingCurrentContext)) {
|
||||
// We can invoke the 'isIsolatingCurrentContext' function.
|
||||
// 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());
|
||||
expectedExecutor.getIdentity());
|
||||
|
||||
bool checkResult = 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());
|
||||
}
|
||||
|
||||
// This provides a last-resort check by giving the expected SerialExecutor the
|
||||
@@ -623,6 +671,9 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: call (%p).checkIsolated passed => pass",
|
||||
expectedExecutor.getIdentity());
|
||||
return true;
|
||||
} else {
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: can NOT call (%p).checkIsolated",
|
||||
expectedExecutor.getIdentity());
|
||||
}
|
||||
|
||||
// In the end, since 'checkIsolated' could not be used, so we must assume
|
||||
@@ -633,30 +684,15 @@ static bool swift_task_isCurrentExecutorWithFlagsImpl(
|
||||
|
||||
// Check override of executor checking mode.
|
||||
static void swift_task_setDefaultExecutorCheckingFlags(void *context) {
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: swift_task_setDefaultExecutorCheckingFlags = %d", nullptr);
|
||||
auto useLegacyMode = swift_bincompat_useLegacyNonCrashingExecutorChecks();
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: use legacy mode = %d", useLegacyMode);
|
||||
auto *options = static_cast<swift_task_is_current_executor_flag *>(context);
|
||||
if (useLegacyMode) {
|
||||
*options = useLegacyMode;
|
||||
}
|
||||
// else {
|
||||
// *options = swift_task_is_current_executor_flag(
|
||||
// *options | swift_task_is_current_executor_flag::Assert);
|
||||
// SWIFT_TASK_DEBUG_LOG("executor checking: ADD ASSERT -> %d", *options);
|
||||
// }
|
||||
|
||||
// FIXME: remove this
|
||||
*options = swift_task_is_current_executor_flag(
|
||||
*options | swift_task_is_current_executor_flag::None);
|
||||
auto modeOverride = swift_bincompat_selectDefaultIsCurrentExecutorCheckingMode();
|
||||
if (modeOverride != swift_task_is_current_executor_flag::None) {
|
||||
*options = modeOverride;
|
||||
}
|
||||
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: resulting options = %d", *options);
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: option Assert = %d",
|
||||
SwiftTaskIsCurrentExecutorOptions(*options)
|
||||
.contains(swift_task_is_current_executor_flag::Assert));
|
||||
SWIFT_TASK_DEBUG_LOG("executor checking: option HasIsIsolatingCurrentContext = %d",
|
||||
SwiftTaskIsCurrentExecutorOptions(*options)
|
||||
.contains(swift_task_is_current_executor_flag::HasIsIsolatingCurrentContext));
|
||||
_swift_task_debug_dumpIsCurrentExecutorFlags(__FUNCTION__, *options);
|
||||
}
|
||||
|
||||
SWIFT_CC(swift)
|
||||
@@ -689,7 +725,7 @@ swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor) {
|
||||
/// an application was linked to. Since Swift 6 the default is to crash,
|
||||
/// and the logging behavior is no longer available.
|
||||
static unsigned unexpectedExecutorLogLevel =
|
||||
swift_bincompat_useLegacyNonCrashingExecutorChecks()
|
||||
(swift_bincompat_selectDefaultIsCurrentExecutorCheckingMode() == swift_task_is_current_executor_flag::None)
|
||||
? 1 // legacy apps default to the logging mode, and cannot use `checkIsolated`
|
||||
: 2; // new apps will only crash upon concurrency violations, and will call into `checkIsolated`
|
||||
|
||||
@@ -701,9 +737,9 @@ static void checkUnexpectedExecutorLogLevel(void *context) {
|
||||
|
||||
long level = strtol(levelStr, nullptr, 0);
|
||||
if (level >= 0 && level < 3) {
|
||||
auto flag = SwiftTaskIsCurrentExecutorOptions(
|
||||
swift_bincompat_useLegacyNonCrashingExecutorChecks());
|
||||
if (flag.contains(swift_task_is_current_executor_flag::Assert)) {
|
||||
auto options = SwiftTaskIsCurrentExecutorOptions(
|
||||
swift_bincompat_selectDefaultIsCurrentExecutorCheckingMode());
|
||||
if (options.contains(swift_task_is_current_executor_flag::Assert)) {
|
||||
// We are in swift6/crash mode of isCurrentExecutor which means that
|
||||
// rather than returning false, that method will always CRASH when an
|
||||
// executor mismatch is discovered.
|
||||
|
||||
@@ -126,11 +126,11 @@ VARIABLE(SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE, string, "",
|
||||
"legacy 'nocrash' behavior until adopting code has been adjusted. "
|
||||
"It is possible to force the use of Swift 6.2's "
|
||||
"isIsolatingCurrentContext instead of checkIsolated by passing "
|
||||
" the 'swift6.2' configuration value. "
|
||||
" the 'isIsolatingCurrentContext' configuration value. "
|
||||
"Legal values are: "
|
||||
" 'legacy' (Legacy behavior), "
|
||||
" 'swift6' (Swift 6.0-6.1 behavior)"
|
||||
" 'swift6.2' (Swift 6.2 behavior)")
|
||||
" 'isIsolatingCurrentContext' (Swift 6.2 behavior)")
|
||||
|
||||
VARIABLE(SWIFT_DUMP_ACCESSIBLE_FUNCTIONS, bool, false,
|
||||
"Dump a listing of all 'AccessibleFunctionRecord's upon first access. "
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-build-swift %import-libdispatch -parse-as-library %s -o %t/a.out
|
||||
// RUN: %target-codesign %t/a.out
|
||||
// RUN: %target-run %t/a.out | %FileCheck %s
|
||||
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: concurrency
|
||||
// REQUIRES: concurrency_runtime
|
||||
|
||||
// REQUIRES: libdispatch
|
||||
|
||||
// UNSUPPORTED: back_deployment_runtime
|
||||
// UNSUPPORTED: back_deploy_concurrency
|
||||
// UNSUPPORTED: use_os_stdlib
|
||||
// UNSUPPORTED: freestanding
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
final class IsIsolatingExecutor: 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 true
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
final class NoChecksImplementedExecutor: SerialExecutor {
|
||||
init() {}
|
||||
|
||||
func enqueue(_ job: consuming ExecutorJob) {
|
||||
job.runSynchronously(on: self.asUnownedSerialExecutor())
|
||||
}
|
||||
|
||||
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
|
||||
UnownedSerialExecutor(ordinary: self)
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
final class JustCheckIsolatedExecutor: SerialExecutor {
|
||||
init() {}
|
||||
|
||||
func enqueue(_ job: consuming ExecutorJob) {
|
||||
job.runSynchronously(on: self.asUnownedSerialExecutor())
|
||||
}
|
||||
|
||||
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
|
||||
UnownedSerialExecutor(ordinary: self)
|
||||
}
|
||||
|
||||
func checkIsolated() {
|
||||
print("called: checkIsolated")
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
actor ActorOnIsCheckImplementingExecutor<Ex: SerialExecutor> {
|
||||
let executor: Ex
|
||||
|
||||
init(on executor: Ex) {
|
||||
self.executor = executor
|
||||
}
|
||||
|
||||
nonisolated var unownedExecutor: UnownedSerialExecutor {
|
||||
self.executor.asUnownedSerialExecutor()
|
||||
}
|
||||
|
||||
nonisolated func checkPreconditionIsolated() async {
|
||||
print("Before preconditionIsolated")
|
||||
self.preconditionIsolated()
|
||||
print("After preconditionIsolated")
|
||||
|
||||
print("Before assumeIsolated")
|
||||
self.assumeIsolated { iso in
|
||||
print("Inside assumeIsolated")
|
||||
}
|
||||
print("After assumeIsolated")
|
||||
}
|
||||
}
|
||||
|
||||
@main struct Main {
|
||||
static func main() async {
|
||||
if #available(SwiftStdlib 6.2, *) {
|
||||
|
||||
let hasIsIsolatingCurrentContextExecutor = IsIsolatingExecutor()
|
||||
let justCheckIsolatedExecutor = JustCheckIsolatedExecutor()
|
||||
|
||||
print("=== do not crash with executor which implements isIsolatingCurrentContext")
|
||||
let hasIsCheckActor = ActorOnIsCheckImplementingExecutor(on: hasIsIsolatingCurrentContextExecutor)
|
||||
await hasIsCheckActor.checkPreconditionIsolated()
|
||||
// CHECK: Before preconditionIsolated
|
||||
// CHECK-NOT: called: checkIsolated
|
||||
// CHECK: called: isIsolatingCurrentContext
|
||||
// CHECK-NOT: called: checkIsolated
|
||||
// CHECK: After preconditionIsolated
|
||||
|
||||
// CHECK: Before assumeIsolated
|
||||
// CHECK-NOT: called: checkIsolated
|
||||
// CHECK: called: isIsolatingCurrentContext
|
||||
// CHECK-NOT: called: checkIsolated
|
||||
// CHECK: After assumeIsolated
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-build-swift %import-libdispatch -parse-as-library %s -o %t/a.out
|
||||
// RUN: %target-codesign %t/a.out
|
||||
// RUN: %env-SWIFT_IS_CURRENT_EXECUTOR_LEGACY_MODE_OVERRIDE=swift6.2 %target-run %t/a.out
|
||||
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: concurrency
|
||||
// REQUIRES: concurrency_runtime
|
||||
|
||||
// REQUIRES: libdispatch
|
||||
|
||||
// UNSUPPORTED: back_deployment_runtime
|
||||
// UNSUPPORTED: back_deploy_concurrency
|
||||
// UNSUPPORTED: use_os_stdlib
|
||||
// UNSUPPORTED: freestanding
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
final class NaiveQueueExecutor: SerialExecutor {
|
||||
init() {}
|
||||
|
||||
func enqueue(_ job: consuming ExecutorJob) {
|
||||
job.runSynchronously(on: self.asUnownedSerialExecutor())
|
||||
}
|
||||
|
||||
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
|
||||
UnownedSerialExecutor(ordinary: self)
|
||||
}
|
||||
|
||||
func checkIsolated() {
|
||||
fatalError("Should not call this: checkIsolated")
|
||||
}
|
||||
|
||||
func isIsolatingCurrentContext() -> Bool {
|
||||
print("pretend it is ok: isIsolatingCurrentContext")
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
actor ActorOnNaiveQueueExecutor {
|
||||
let executor: NaiveQueueExecutor
|
||||
|
||||
init() {
|
||||
self.executor = NaiveQueueExecutor()
|
||||
}
|
||||
|
||||
nonisolated var unownedExecutor: UnownedSerialExecutor {
|
||||
self.executor.asUnownedSerialExecutor()
|
||||
}
|
||||
|
||||
nonisolated func checkPreconditionIsolated() async {
|
||||
print("Before preconditionIsolated")
|
||||
self.preconditionIsolated()
|
||||
print("After preconditionIsolated")
|
||||
|
||||
print("Before assumeIsolated")
|
||||
self.assumeIsolated { iso in
|
||||
print("Inside assumeIsolated")
|
||||
}
|
||||
print("After assumeIsolated")
|
||||
}
|
||||
}
|
||||
|
||||
@main struct Main {
|
||||
static func main() async {
|
||||
if #available(SwiftStdlib 6.2, *) {
|
||||
let actor = ActorOnNaiveQueueExecutor()
|
||||
await actor.checkPreconditionIsolated()
|
||||
// CHECK: Before preconditionIsolated
|
||||
// CHECK-NOT: Should not call this: checkIsolated
|
||||
// CHECK-NEXT: pretend it is ok: isIsolatingCurrentContext
|
||||
// CHECK-NEXT: After preconditionIsolated
|
||||
|
||||
// CHECK-NEXT: Before assumeIsolated
|
||||
// CHECK-NOT: Should not call this: checkIsolated
|
||||
// CHECK-NEXT: pretend it is ok: isIsolatingCurrentContext
|
||||
// CHECK-NEXT: After assumeIsolated
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
// RUN: %target-build-swift %import-libdispatch -parse-as-library %s -o %t/a.out
|
||||
// RUN: %target-codesign %t/a.out
|
||||
// RUN: %target-run %t/a.out | %FileCheck %s
|
||||
|
||||
// REQUIRES: executable_test
|
||||
// REQUIRES: concurrency
|
||||
// REQUIRES: concurrency_runtime
|
||||
|
||||
// REQUIRES: libdispatch
|
||||
|
||||
// UNSUPPORTED: back_deployment_runtime
|
||||
// UNSUPPORTED: back_deploy_concurrency
|
||||
// UNSUPPORTED: use_os_stdlib
|
||||
// UNSUPPORTED: freestanding
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
final class IsIsolatingExecutor: 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 true
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
final class JustCheckIsolatedExecutor: SerialExecutor {
|
||||
init() {}
|
||||
|
||||
func enqueue(_ job: consuming ExecutorJob) {
|
||||
job.runSynchronously(on: self.asUnownedSerialExecutor())
|
||||
}
|
||||
|
||||
func asUnownedSerialExecutor() -> UnownedSerialExecutor {
|
||||
UnownedSerialExecutor(ordinary: self)
|
||||
}
|
||||
|
||||
func checkIsolated() {
|
||||
print("called: checkIsolated")
|
||||
}
|
||||
}
|
||||
|
||||
@available(SwiftStdlib 6.2, *)
|
||||
actor ActorOnIsCheckImplementingExecutor<Ex: SerialExecutor> {
|
||||
let executor: Ex
|
||||
|
||||
init(on executor: Ex) {
|
||||
self.executor = executor
|
||||
}
|
||||
|
||||
nonisolated var unownedExecutor: UnownedSerialExecutor {
|
||||
self.executor.asUnownedSerialExecutor()
|
||||
}
|
||||
|
||||
nonisolated func checkPreconditionIsolated() async {
|
||||
print("Before preconditionIsolated")
|
||||
self.preconditionIsolated()
|
||||
print("After preconditionIsolated")
|
||||
|
||||
print("Before assumeIsolated")
|
||||
self.assumeIsolated { iso in
|
||||
print("Inside assumeIsolated")
|
||||
}
|
||||
print("After assumeIsolated")
|
||||
}
|
||||
}
|
||||
|
||||
@main struct Main {
|
||||
static func main() async {
|
||||
if #available(SwiftStdlib 6.2, *) {
|
||||
|
||||
let hasIsIsolatingCurrentContextExecutor = IsIsolatingExecutor()
|
||||
let justCheckIsolatedExecutor = JustCheckIsolatedExecutor()
|
||||
|
||||
print("do checkIsolated with executor which does NOT implement isIsolatingCurrentContext")
|
||||
let checkIsolatedActor = ActorOnIsCheckImplementingExecutor(on: justCheckIsolatedExecutor)
|
||||
await checkIsolatedActor.checkPreconditionIsolated()
|
||||
// CHECK: Before preconditionIsolated
|
||||
// CHECK-NOT: called: isIsolatingCurrentContext
|
||||
// CHECK: called: checkIsolated
|
||||
// CHECK-NOT: called: isIsolatingCurrentContext
|
||||
// CHECK: After preconditionIsolated
|
||||
|
||||
// CHECK: Before assumeIsolated
|
||||
// CHECK-NOT: called: isIsolatingCurrentContext
|
||||
// CHECK: called: checkIsolated
|
||||
// CHECK-NOT: called: isIsolatingCurrentContext
|
||||
// CHECK: After assumeIsolated
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user