diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 90643035ec21d..c8906fcb3d426 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -1133,6 +1133,14 @@ NOTE(rbi_add_generic_parameter_sendable_conformance,none, "consider making generic parameter %0 conform to the 'Sendable' protocol", (Type)) +// Concurrency related diagnostics +ERROR(cannot_find_executor_factory_type, none, + "the specified executor factory '%0' could not be found", (StringRef)) +ERROR(executor_factory_must_conform, none, + "the executor factory '%0' does not conform to 'ExecutorFactory'", (Type)) +ERROR(executor_factory_not_supported, none, + "deployment target too low for executor factory specification", ()) + //===----------------------------------------------------------------------===// // MARK: Misc Diagnostics //===----------------------------------------------------------------------===// diff --git a/include/swift/AST/FeatureAvailability.def b/include/swift/AST/FeatureAvailability.def index ae1bbdbc5db67..3270cca5ddfd9 100644 --- a/include/swift/AST/FeatureAvailability.def +++ b/include/swift/AST/FeatureAvailability.def @@ -80,6 +80,7 @@ FEATURE(IsolatedDeinit, (6, 1)) FEATURE(ValueGenericType, (6, 2)) FEATURE(InitRawStructMetadata2, (6, 2)) +FEATURE(CustomExecutors, (6, 2)) FEATURE(TaskExecutor, FUTURE) FEATURE(Differentiation, FUTURE) diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 27ecbbcfeaf75..89c14badf0464 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -97,6 +97,7 @@ PROTOCOL(Executor) PROTOCOL(SerialExecutor) PROTOCOL(TaskExecutor) PROTOCOL(GlobalActor) +PROTOCOL(ExecutorFactory) PROTOCOL_(BridgedNSError) PROTOCOL_(BridgedStoredNSError) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 7237ed538b198..4df3e0e9deaaf 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -396,6 +396,10 @@ namespace swift { /// Specifies how strict concurrency checking will be. StrictConcurrency StrictConcurrencyLevel = StrictConcurrency::Minimal; + /// Specifies the name of the executor factory to use to create the + /// default executors for Swift Concurrency. + std::optional ExecutorFactory; + /// Enable experimental concurrency model. bool EnableExperimentalConcurrency = false; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index d5f1eccc580db..d42fee50fe3ad 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -985,6 +985,16 @@ def strict_concurrency : Joined<["-"], "strict-concurrency=">, "concurrency model, or 'complete' ('Sendable' and other checking is " "enabled for all code in the module)">; +def executor_factory : JoinedOrSeparate<["-"], "executor-factory">, + Flags<[FrontendOption]>, + HelpText<"Specify the factory to use to create the default executors for " + "Swift Concurrency. This must be a type conforming to the " + "'ExecutorFactory' protocol.">, + MetaVarName<"">; +def executor_factory_EQ : Joined<["-"], "executor-factory=">, + Flags<[FrontendOption]>, + Alias; + def enable_experimental_feature : Separate<["-"], "enable-experimental-feature">, Flags<[FrontendOption, ModuleInterfaceOption]>, diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index 55995b0926f0b..befda4a72da17 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -149,6 +149,29 @@ SWIFT_EXPORT_FROM(swift_Concurrency) CoroAllocator *const _swift_coro_malloc_allocator; // }} TODO: CoroutineAccessors +/// Deallocate memory in a task. +/// +/// The pointer provided must be the last pointer allocated on +/// this task that has not yet been deallocated; that is, memory +/// must be allocated and deallocated in a strict stack discipline. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_dealloc(void *ptr); + +/// Allocate memory in a job. +/// +/// All allocations will be rounded to a multiple of MAX_ALIGNMENT; +/// if the job does not support allocation, this will return NULL. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void *swift_job_allocate(Job *job, size_t size); + +/// Deallocate memory in a job. +/// +/// The pointer provided must be the last pointer allocated on +/// this task that has not yet been deallocated; that is, memory +/// must be allocated and deallocated in a strict stack discipline. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_job_deallocate(Job *job, void *ptr); + /// Cancel a task and all of its child tasks. /// /// This can be called from any thread. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 9f7ab7f111bd4..857603e92b545 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1435,6 +1435,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::Executor: case KnownProtocolKind::TaskExecutor: case KnownProtocolKind::SerialExecutor: + case KnownProtocolKind::ExecutorFactory: M = getLoadedModule(Id_Concurrency); break; case KnownProtocolKind::DistributedActor: diff --git a/lib/ClangImporter/SortedCFDatabase.def.gyb b/lib/ClangImporter/SortedCFDatabase.def.gyb index 41d83ccb4b0f4..340ed2293a661 100644 --- a/lib/ClangImporter/SortedCFDatabase.def.gyb +++ b/lib/ClangImporter/SortedCFDatabase.def.gyb @@ -39,7 +39,7 @@ with codecs.open(CFDatabaseFile, encoding='utf-8', errors='strict') as f: continue # Otherwise, check for lines like FOO(BAR) - m = re.match('^\w+\((\w+)\)', line) + m = re.match(r'^\w+\((\w+)\)', line) if m: lineForName[m.group(1)] = line }% diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 6a7ae73498e00..0ca869042faca 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -377,6 +377,10 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, arguments.push_back(inputArgs.MakeArgString(globalRemapping)); } + if (inputArgs.hasArg(options::OPT_executor_factory)) { + inputArgs.AddLastArg(arguments, options::OPT_executor_factory); + } + // Pass through the values passed to -Xfrontend. inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 9233295bfd1a0..6a7b9432c6002 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1314,6 +1314,12 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.enableFeature(Feature::RegionBasedIsolation); } + // Get the executor factory name + if (const Arg *A = Args.getLastArg(OPT_executor_factory)) { + printf("Got executor-factory option\n"); + Opts.ExecutorFactory = A->getValue(); + } + Opts.WarnImplicitOverrides = Args.hasArg(OPT_warn_implicit_overrides); diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 6fa5ce3a06911..cdf6235ee3364 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -7063,6 +7063,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::Executor: case KnownProtocolKind::SerialExecutor: case KnownProtocolKind::TaskExecutor: + case KnownProtocolKind::ExecutorFactory: case KnownProtocolKind::Sendable: case KnownProtocolKind::UnsafeSendable: case KnownProtocolKind::RangeReplaceableCollection: diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 0bd0a03e244ef..27c554a71651b 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -458,6 +458,10 @@ FuncDecl *SILGenModule::getDeinitOnExecutor() { return lookupConcurrencyIntrinsic(getASTContext(), "_deinitOnExecutor"); } +FuncDecl *SILGenModule::getCreateExecutors() { + return lookupConcurrencyIntrinsic(getASTContext(), "_createExecutors"); +} + FuncDecl *SILGenModule::getExit() { ASTContext &C = getASTContext(); @@ -507,6 +511,46 @@ FuncDecl *SILGenModule::getExit() { return exitFunction; } +Type SILGenModule::getExecutorFactory() { + auto &ctx = getASTContext(); + + ModuleDecl *module; + + // Parse the executor factory name + StringRef qualifiedName = *ctx.LangOpts.ExecutorFactory; + StringRef typeName; + + auto parts = qualifiedName.split('.'); + + if (parts.second.empty()) { + // This was an unqualified name; assume it's relative to the main module + module = ctx.MainModule; + typeName = qualifiedName; + } else { + Identifier moduleName = ctx.getIdentifier(parts.first); + module = ctx.getModuleByIdentifier(moduleName); + typeName = parts.second; + } + + return ctx.getNamedSwiftType(module, typeName); +} + +Type SILGenModule::getDefaultExecutorFactory() { + auto &ctx = getASTContext(); + + ModuleDecl *module = ctx.getModuleByIdentifier(ctx.Id_Concurrency); + if (!module) + return Type(); + + return ctx.getNamedSwiftType(module, "DefaultExecutorFactory"); +} + +ProtocolDecl *SILGenModule::getExecutorFactoryProtocol() { + auto &ctx = getASTContext(); + + return ctx.getProtocol(KnownProtocolKind::ExecutorFactory); +} + ProtocolConformance *SILGenModule::getNSErrorConformanceToError() { if (NSErrorConformanceToError) return *NSErrorConformanceToError; diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 3599eadf8acd7..daca3223491f6 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -562,6 +562,18 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { // Retrieve the _SwiftConcurrencyShims.exit intrinsic. FuncDecl *getExit(); + /// Get the ExecutorFactory type. + Type getExecutorFactory(); + + /// Get the DefaultExecutorFactory type. + Type getDefaultExecutorFactory(); + + /// Get the ExecutorFactory protocol. + ProtocolDecl *getExecutorFactoryProtocol(); + + /// Get the swift_createExecutors function. + FuncDecl *getCreateExecutors(); + SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess, KeyPathTypeKind typeKind); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index ab2aec62042ed..14c5eb98d82f5 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -486,7 +486,7 @@ SILGenFunction::getOrCreateScope(const ast_scope::ASTScopeImpl *ASTScope, // Collapse BraceStmtScopes whose parent is a .*BodyScope. if (auto Parent = ASTScope->getParent().getPtrOrNull()) if (Parent->getSourceRangeOfThisASTNode() == - ASTScope->getSourceRangeOfThisASTNode()) + ASTScope->getSourceRangeOfThisASTNode()) return cache(getOrCreateScope(Parent, FnScope, InlinedAt)); // The calls to defer closures have cleanup source locations pointing to the @@ -1387,6 +1387,28 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { } } +static bool isCreateExecutorsFunctionAvailable(SILGenModule &SGM) { + FuncDecl *createExecutorsFuncDecl = SGM.getCreateExecutors(); + if (!createExecutorsFuncDecl) + return false; + + auto &ctx = createExecutorsFuncDecl->getASTContext(); + + if (ctx.LangOpts.hasFeature(Feature::Embedded)) + return true; + + if (!ctx.LangOpts.DisableAvailabilityChecking) { + auto deploymentAvailability = AvailabilityRange::forDeploymentTarget(ctx); + auto runtimeAvailability = AvailabilityRange::forRuntimeTarget(ctx); + auto declAvailability = ctx.getCustomExecutorsAvailability(); + auto declRtAvailability = ctx.getCustomExecutorsRuntimeAvailability(); + return deploymentAvailability.isContainedIn(declAvailability) + && runtimeAvailability.isContainedIn(declRtAvailability); + } + + return true; +} + void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) { auto moduleLoc = entryPoint.getAsRegularLocation(); auto *entryBlock = B.getInsertionBB(); @@ -1402,6 +1424,51 @@ void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) { B.setInsertionPoint(entryBlock); + // If we're using a new enough deployment target, call swift_createExecutors() + if (ctx.LangOpts.ExecutorFactory) { + if (!isCreateExecutorsFunctionAvailable(SGM)) { + ctx.Diags.diagnose(SourceLoc(), diag::executor_factory_not_supported); + } else { + CanType factoryTy = SGM.getExecutorFactory()->getCanonicalType(); + + if (!factoryTy) { + ctx.Diags.diagnose(SourceLoc(), diag::cannot_find_executor_factory_type, + *ctx.LangOpts.ExecutorFactory); + } + + ProtocolDecl *executorFactoryProtocol = SGM.getExecutorFactoryProtocol(); + auto conformance = lookupConformance(factoryTy, executorFactoryProtocol); + + if (conformance.isInvalid()) { + // If this type doesn't conform, ignore it and use the default factory + SourceLoc loc = extractNearestSourceLoc(factoryTy); + + ctx.Diags.diagnose(loc, diag::executor_factory_must_conform, factoryTy); + + factoryTy = SGM.getDefaultExecutorFactory()->getCanonicalType(); + conformance = lookupConformance(factoryTy, executorFactoryProtocol); + + assert(!conformance.isInvalid()); + } + + FuncDecl *createExecutorsFuncDecl = SGM.getCreateExecutors(); + assert(createExecutorsFuncDecl + && "Failed to find swift_createExecutors function decl"); + SILFunction *createExecutorsSILFunc = + SGM.getFunction(SILDeclRef(createExecutorsFuncDecl, SILDeclRef::Kind::Func), + NotForDefinition); + SILValue createExecutorsFunc = + B.createFunctionRefFor(moduleLoc, createExecutorsSILFunc); + MetatypeType *factoryThickMetaTy + = MetatypeType::get(factoryTy, MetatypeRepresentation::Thick); + SILValue factorySILMetaTy + = B.createMetatype(moduleLoc, getLoweredType(factoryThickMetaTy)); + auto ceSubs = SubstitutionMap::getProtocolSubstitutions( + conformance.getRequirement(), factoryTy, conformance); + B.createApply(moduleLoc, createExecutorsFunc, ceSubs, { factorySILMetaTy }); + } + } + auto wrapCallArgs = [this, &moduleLoc](SILValue originalValue, FuncDecl *fd, uint32_t paramIndex) -> SILValue { Type parameterType = fd->getParameters()->get(paramIndex)->getTypeInContext(); diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index d9a493590563e..103af5a70ba0b 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -25,6 +25,7 @@ #include "../CompatibilityOverride/CompatibilityOverride.h" #include "swift/ABI/Actor.h" #include "swift/ABI/Task.h" +#include "ExecutorBridge.h" #include "TaskPrivate.h" #include "swift/Basic/HeaderFooterLayout.h" #include "swift/Basic/PriorityQueue.h" @@ -289,6 +290,40 @@ static SerialExecutorRef swift_task_getCurrentExecutorImpl() { return result; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" + +extern "C" SWIFT_CC(swift) +SerialExecutorRef _swift_getActiveExecutor() { + auto currentTracking = ExecutorTrackingInfo::current(); + if (currentTracking) { + SerialExecutorRef executor = currentTracking->getActiveExecutor(); + // This might be an actor, in which case return nil ("generic") + if (executor.isDefaultActor()) + return SerialExecutorRef::generic(); + return executor; + } + return swift_getMainExecutor(); +} + +extern "C" SWIFT_CC(swift) +TaskExecutorRef _swift_getCurrentTaskExecutor() { + auto currentTracking = ExecutorTrackingInfo::current(); + if (currentTracking) + return currentTracking->getTaskExecutor(); + return TaskExecutorRef::undefined(); +} + +extern "C" SWIFT_CC(swift) +TaskExecutorRef _swift_getPreferredTaskExecutor() { + AsyncTask *task = swift_task_getCurrent(); + if (!task) + return TaskExecutorRef::undefined(); + return task->getPreferredTaskExecutor(); +} + +#pragma clang diagnostic pop + /// Determine whether we are currently executing on the main thread /// independently of whether we know that we are on the main actor. static bool isExecutingOnMainThread() { diff --git a/stdlib/public/Concurrency/CFExecutor.swift b/stdlib/public/Concurrency/CFExecutor.swift new file mode 100644 index 0000000000000..451e0860e7975 --- /dev/null +++ b/stdlib/public/Concurrency/CFExecutor.swift @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)) + +import Swift + +internal import Darwin + +// .. Dynamic binding .......................................................... + +enum CoreFoundation { + static let path = + "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" + + static let handle = unsafe dlopen(path, RTLD_NOLOAD) + + static var isPresent: Bool { return unsafe handle != nil } + + static func symbol(_ name: String) -> T { + guard let result = unsafe dlsym(handle, name) else { + fatalError("Unable to look up \(name) in CoreFoundation") + } + return unsafe unsafeBitCast(result, to: T.self) + } + + static let CFRunLoopRun: @convention(c) () -> () = + symbol("CFRunLoopRun") + static let CFRunLoopGetMain: @convention(c) () -> OpaquePointer = + unsafe symbol("CFRunLoopGetMain") + static let CFRunLoopStop: @convention(c) (OpaquePointer) -> () = + unsafe symbol("CFRunLoopStop") +} + +// .. Main Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public final class CFMainExecutor: DispatchMainExecutor, @unchecked Sendable { + + override public func run() throws { + CoreFoundation.CFRunLoopRun() + } + + override public func stop() { + unsafe CoreFoundation.CFRunLoopStop(CoreFoundation.CFRunLoopGetMain()) + } + +} + +// .. Task Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public final class CFTaskExecutor: DispatchGlobalTaskExecutor, + @unchecked Sendable { + +} + +#endif diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 54f7daa100633..05f19620530ef 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -79,6 +79,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_C_SOURCES ConcurrencyHooks.cpp EmbeddedSupport.cpp Error.cpp + ExecutorBridge.cpp ExecutorChecks.cpp Setup.cpp Task.cpp @@ -92,33 +93,13 @@ set(SWIFT_RUNTIME_CONCURRENCY_C_SOURCES linker-support/magic-symbols-for-install-name.c ) -if("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "dispatch") - set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES - DispatchGlobalExecutor.cpp - ) -elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "singlethreaded") - set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES - CooperativeGlobalExecutor.cpp - ) -elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "hooked" OR - "${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "none") - set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES - NonDispatchGlobalExecutor.cpp - ) -endif() - -set(LLVM_OPTIONAL_SOURCES - CooperativeGlobalExecutor.cpp - DispatchGlobalExecutor.cpp - NonDispatchGlobalExecutor.cpp -) - set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES Actor.swift AsyncLet.swift CheckedContinuation.swift Errors.swift Executor.swift + ExecutorBridge.swift ExecutorAssertions.swift AsyncCompactMapSequence.swift AsyncDropFirstSequence.swift @@ -136,10 +117,11 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES AsyncThrowingFlatMapSequence.swift AsyncThrowingMapSequence.swift AsyncThrowingPrefixWhileSequence.swift + PartialAsyncTask.swift GlobalActor.swift GlobalConcurrentExecutor.swift MainActor.swift - PartialAsyncTask.swift + PriorityQueue.swift SourceCompatibilityShims.swift Task.swift Task+PriorityEscalation.swift @@ -176,12 +158,51 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES ContinuousClock.swift SuspendingClock.swift TaskSleepDuration.swift + DummyExecutor.swift + CooperativeExecutor.swift + PlatformExecutorDarwin.swift + PlatformExecutorLinux.swift + PlatformExecutorWindows.swift +) + +set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES) +set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES) +if("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "dispatch") + set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES + DispatchGlobalExecutor.cpp + ) + set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES + DispatchExecutor.swift + CFExecutor.swift + ExecutorImpl.swift + ) +elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "singlethreaded") + set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES + ExecutorImpl.swift + PlatformExecutorCooperative.swift + ) +else() + set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES + ExecutorImpl.swift + PlatformExecutorNone.swift + ) +endif() + +set(SWIFT_RUNTIME_CONCURRENCY_EMBEDDED_SWIFT_SOURCES + PlatformExecutorNone.swift + ) + +set(LLVM_OPTIONAL_SOURCES + DispatchGlobalExecutor.cpp + CooperativeGlobalExecutor.cpp + DispatchGlobalExecutor.cpp ) add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB ${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES} + ${SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES} GYB_SOURCES TaskGroup+addTask.swift.gyb @@ -279,6 +300,7 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC ${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES} + ${SWIFT_RUNTIME_CONCURRENCY_EMBEDDED_SWIFT_SOURCES} SWIFT_COMPILE_FLAGS ${extra_swift_compile_flags} -enable-experimental-feature Embedded @@ -358,4 +380,3 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC add_dependencies(embedded-concurrency "copy_executor_impl_header") endif() - diff --git a/stdlib/public/Concurrency/Clock.cpp b/stdlib/public/Concurrency/Clock.cpp index 2c9e43c8f4cdd..0ae357a5ddad5 100644 --- a/stdlib/public/Concurrency/Clock.cpp +++ b/stdlib/public/Concurrency/Clock.cpp @@ -21,6 +21,16 @@ #include #endif +#if __has_include() +#define WE_HAVE_STD_CHRONO 1 +#include + +#if __has_include() +#define WE_HAVE_STD_THIS_THREAD 1 +#include +#endif +#endif // __has_include() + #include "Error.h" using namespace swift; @@ -50,6 +60,14 @@ void swift_get_time( (void)QueryInterruptTimePrecise(&interruptTime); continuous.tv_sec = interruptTime / 10'000'000; continuous.tv_nsec = (interruptTime % 10'000'000) * 100; +#elif WE_HAVE_STD_CHRONO + auto now = std::chrono::steady_clock::now(); + auto epoch = std::chrono::steady_clock::min(); + auto timeSinceEpoch = now - epoch; + auto sec = std::chrono::duration_cast(timeSinceEpoch); + auto ns = std::chrono::duration_cast(timeSinceEpoch - sec); + continuous.tv_sec = sec; + continuous.tv_nsec = ns; #else #error Missing platform continuous time definition #endif @@ -77,6 +95,14 @@ void swift_get_time( (void)QueryUnbiasedInterruptTimePrecise(&unbiasedTime); suspending.tv_sec = unbiasedTime / 10'000'000; suspending.tv_nsec = (unbiasedTime % 10'000'000) * 100; +#elif WE_HAVE_STD_CHRONO + auto now = std::chrono::steady_clock::now(); + auto epoch = std::chrono::steady_clock::min(); + auto timeSinceEpoch = now - epoch; + auto sec = std::chrono::duration_cast(timeSinceEpoch); + auto ns = std::chrono::duration_cast(timeSinceEpoch - sec); + suspending.tv_sec = sec; + suspending.tv_nsec = ns; #else #error Missing platform suspending time definition #endif @@ -107,6 +133,11 @@ switch (clock_id) { #elif defined(_WIN32) continuous.tv_sec = 0; continuous.tv_nsec = 100; +#elif WE_HAVE_STD_CHRONO + auto num = std::chrono::steady_clock::period::num; + auto den = std::chrono::steady_clock::period::den; + continuous.tv_sec = num / den; + continuous.tv_nsec = (num * 1000000000ll) % den #else #error Missing platform continuous time definition #endif @@ -127,6 +158,11 @@ switch (clock_id) { #elif defined(_WIN32) suspending.tv_sec = 0; suspending.tv_nsec = 100; +#elif WE_HAVE_STD_CHRONO + auto num = std::chrono::steady_clock::period::num; + auto den = std::chrono::steady_clock::period::den; + continuous.tv_sec = num / den; + continuous.tv_nsec = (num * 1'000'000'000ll) % den #else #error Missing platform suspending time definition #endif @@ -138,3 +174,43 @@ switch (clock_id) { swift_Concurrency_fatalError(0, "Fatal error: invalid clock ID %d\n", clock_id); } + +SWIFT_EXPORT_FROM(swift_Concurrency) +SWIFT_CC(swift) +void swift_sleep( + long long seconds, + long long nanoseconds) { +#if defined(_WIN32) + ULONGLONG now; + (void)QueryInterruptTimePrecise(&now); + ULONGLONG delay = seconds * 10'000'000 + nanoseconds / 100; + ULONGLONG deadline = now + delay; + while (deadline > now) { + DWORD dwMsec = delay / 10'000; + + // For sleeps over 15ms, Windows may return up to 15ms early(!); + // for sleeps less than 15ms, Windows does a delay koop internally, + // which is acceptable here. + if (dwMsec > 15) + dwMsec += 15; + + (void)SleepEx(dwMsec, TRUE); + (void)QueryInterruptTimePrecise(&now); + delay = deadline - now; + } +#elif defined(__linux__) || defined(__APPLE__) || defined(__wasi__) \ + || defined(__OpenBSD) || defined(__FreeBSD__) + struct timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = nanoseconds; + while (nanosleep(&ts, &ts) == -1 && errno == EINTR); +#elif WE_HAVE_STD_THIS_THREAD && !defined(SWIFT_THREADING_NONE) + auto duration + = std::chrono::duration_cast( + std::chrono::seconds(seconds) + std::chrono::nanoseconds(nanoseconds) + ); + std::this_thread::sleep_for(duration); +#else + #error Missing platform sleep definition +#endif +} diff --git a/stdlib/public/Concurrency/Clock.swift b/stdlib/public/Concurrency/Clock.swift index 56a85e437fcb3..b1c790c55716d 100644 --- a/stdlib/public/Concurrency/Clock.swift +++ b/stdlib/public/Concurrency/Clock.swift @@ -11,24 +11,24 @@ //===----------------------------------------------------------------------===// import Swift -/// A mechanism in which to measure time, and delay work until a given point +/// A mechanism in which to measure time, and delay work until a given point /// in time. /// -/// Types that conform to the `Clock` protocol define a concept of "now" which +/// Types that conform to the `Clock` protocol define a concept of "now" which /// is the specific instant in time that property is accessed. Any pair of calls /// to the `now` property may have a minimum duration between them - this /// minimum resolution is exposed by the `minimumResolution` property to inform -/// any user of the type the expected granularity of accuracy. +/// any user of the type the expected granularity of accuracy. /// /// One of the primary uses for clocks is to schedule task sleeping. This method /// resumes the calling task after a given deadline has been met or passed with -/// a given tolerance value. The tolerance is expected as a leeway around the -/// deadline. The clock may reschedule tasks within the tolerance to ensure +/// a given tolerance value. The tolerance is expected as a leeway around the +/// deadline. The clock may reschedule tasks within the tolerance to ensure /// efficient execution of resumptions by reducing potential operating system /// wake-ups. If no tolerance is specified (i.e. nil is passed in) the sleep -/// function is expected to schedule with a default tolerance strategy. +/// function is expected to schedule with a default tolerance strategy. /// -/// For more information about specific clocks see `ContinuousClock` and +/// For more information about specific clocks see `ContinuousClock` and /// `SuspendingClock`. @available(SwiftStdlib 5.7, *) public protocol Clock: Sendable { @@ -41,8 +41,51 @@ public protocol Clock: Sendable { #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws #endif -} + /// The traits associated with this clock instance. + @available(SwiftStdlib 6.2, *) + var traits: ClockTraits { get } + + /// Convert a Clock-specific Duration to a Swift Duration + /// + /// Some clocks may define `C.Duration` to be something other than a + /// `Swift.Duration`, but that makes it tricky to convert timestamps + /// between clocks, which is something we want to be able to support. + /// This method will convert whatever `C.Duration` is to a `Swift.Duration`. + /// + /// Parameters: + /// + /// - from duration: The `Duration` to convert + /// + /// Returns: A `Swift.Duration` representing the equivalent duration, or + /// `nil` if this function is not supported. + @available(SwiftStdlib 6.2, *) + func convert(from duration: Duration) -> Swift.Duration? + + /// Convert a Swift Duration to a Clock-specific Duration + /// + /// Parameters: + /// + /// - from duration: The `Swift.Duration` to convert. + /// + /// Returns: A `Duration` representing the equivalent duration, or + /// `nil` if this function is not supported. + @available(SwiftStdlib 6.2, *) + func convert(from duration: Swift.Duration) -> Duration? + + /// Convert an `Instant` from some other clock's `Instant` + /// + /// Parameters: + /// + /// - instant: The instant to convert. + // - from clock: The clock to convert from. + /// + /// Returns: An `Instant` representing the equivalent instant, or + /// `nil` if this function is not supported. + @available(SwiftStdlib 6.2, *) + func convert(instant: OtherClock.Instant, + from clock: OtherClock) -> Instant? +} @available(SwiftStdlib 5.7, *) extension Clock { @@ -88,6 +131,44 @@ extension Clock { } } +@available(SwiftStdlib 6.2, *) +extension Clock { + // For compatibility, return `nil` if this is not implemented + public func convert(from duration: Duration) -> Swift.Duration? { + return nil + } + + public func convert(from duration: Swift.Duration) -> Duration? { + return nil + } + + public func convert(instant: OtherClock.Instant, + from clock: OtherClock) -> Instant? { + let ourNow = now + let otherNow = clock.now + let otherDuration = otherNow.duration(to: instant) + + // Convert to `Swift.Duration` + guard let duration = clock.convert(from: otherDuration) else { + return nil + } + + // Convert from `Swift.Duration` + guard let ourDuration = convert(from: duration) else { + return nil + } + + return ourNow.advanced(by: ourDuration) + } +} + +@available(SwiftStdlib 6.2, *) +extension Clock where Duration == Swift.Duration { + public func convert(from duration: Duration) -> Duration? { + return duration + } +} + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.7, *) extension Clock { @@ -106,6 +187,44 @@ extension Clock { } #endif +/// Represents traits of a particular Clock implementation. +/// +/// Clocks may be of a number of different varieties; executors will likely +/// have specific clocks that they can use to schedule jobs, and will +/// therefore need to be able to convert timestamps to an appropriate clock +/// when asked to enqueue a job with a delay or deadline. +/// +/// Choosing a clock in general requires the ability to tell which of their +/// clocks best matches the clock that the user is trying to specify a +/// time or delay in. Executors are expected to do this on a best effort +/// basis. +@available(SwiftStdlib 6.2, *) +public struct ClockTraits: OptionSet { + public let rawValue: UInt32 + + public init(rawValue: UInt32) { + self.rawValue = rawValue + } + + /// Clocks with this trait continue running while the machine is asleep. + public static let continuous = ClockTraits(rawValue: 1 << 0) + + /// Indicates that a clock's time will only ever increase. + public static let monotonic = ClockTraits(rawValue: 1 << 1) + + /// Clocks with this trait are tied to "wall time". + public static let wallTime = ClockTraits(rawValue: 1 << 2) +} + +@available(SwiftStdlib 6.2, *) +extension Clock { + /// The traits associated with this clock instance. + @available(SwiftStdlib 6.2, *) + public var traits: ClockTraits { + return [] + } +} + enum _ClockID: Int32 { case continuous = 1 case suspending = 2 @@ -124,3 +243,9 @@ internal func _getClockRes( seconds: UnsafeMutablePointer, nanoseconds: UnsafeMutablePointer, clock: CInt) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_sleep") +internal func _sleep( + seconds: Int64, + nanoseconds: Int64) diff --git a/stdlib/public/Concurrency/ContinuousClock.swift b/stdlib/public/Concurrency/ContinuousClock.swift index 604f0ff022430..979a3580d17ea 100644 --- a/stdlib/public/Concurrency/ContinuousClock.swift +++ b/stdlib/public/Concurrency/ContinuousClock.swift @@ -11,9 +11,8 @@ //===----------------------------------------------------------------------===// import Swift -#if !$Embedded -/// A clock that measures time that always increments and does not stop -/// incrementing while the system is asleep. +/// A clock that measures time that always increments and does not stop +/// incrementing while the system is asleep. /// /// `ContinuousClock` can be considered as a stopwatch style time. The frame of /// reference of the `Instant` may be bound to process launch, machine boot or @@ -22,9 +21,10 @@ import Swift /// /// This clock is suitable for high resolution measurements of execution. @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded public struct ContinuousClock: Sendable { /// A continuous point in time used for `ContinuousClock`. - public struct Instant: Codable, Sendable { + public struct Instant: Sendable { internal var _value: Swift.Duration internal init(_value: Swift.Duration) { @@ -34,6 +34,11 @@ public struct ContinuousClock: Sendable { public init() { } } + +#if !$Embedded +@available(SwiftStdlib 5.7, *) +extension ContinuousClock.Instant: Codable { +} #endif @available(SwiftStdlib 5.7, *) @@ -51,12 +56,11 @@ extension Duration { } } -#if !$Embedded - @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension Clock where Self == ContinuousClock { - /// A clock that measures time that always increments but does not stop - /// incrementing while the system is asleep. + /// A clock that measures time that always increments but does not stop + /// incrementing while the system is asleep. /// /// try await Task.sleep(until: .now + .seconds(3), clock: .continuous) /// @@ -65,6 +69,7 @@ extension Clock where Self == ContinuousClock { } @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension ContinuousClock: Clock { /// The current continuous instant. public var now: ContinuousClock.Instant { @@ -95,24 +100,33 @@ extension ContinuousClock: Clock { ) } + /// The continuous clock is continuous and monotonic + @available(SwiftStdlib 6.2, *) + public var traits: ClockTraits { + return [.continuous, .monotonic] + } + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY /// Suspend task execution until a given deadline within a tolerance. /// If no tolerance is specified then the system may adjust the deadline /// to coalesce CPU wake-ups to more efficiently process the wake-ups in /// a more power efficient manner. /// - /// If the task is canceled before the time ends, this function throws + /// If the task is canceled before the time ends, this function throws /// `CancellationError`. /// /// This function doesn't block the underlying thread. public func sleep( until deadline: Instant, tolerance: Swift.Duration? = nil ) async throws { - let (seconds, attoseconds) = deadline._value.components - let nanoseconds = attoseconds / 1_000_000_000 - try await Task._sleep(until:seconds, nanoseconds, - tolerance: tolerance, - clock: .continuous) + if #available(SwiftStdlib 6.2, *) { + try await Task._sleep(until: deadline, + tolerance: tolerance, + clock: self) + } else { + // Should never see this + Builtin.unreachable() + } } #else @available(SwiftStdlib 5.7, *) @@ -126,6 +140,7 @@ extension ContinuousClock: Clock { } @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension ContinuousClock.Instant: InstantProtocol { public static var now: ContinuousClock.Instant { ContinuousClock.now } @@ -193,5 +208,3 @@ extension ContinuousClock.Instant: InstantProtocol { rhs.duration(to: lhs) } } - -#endif diff --git a/stdlib/public/Concurrency/CooperativeExecutor.swift b/stdlib/public/Concurrency/CooperativeExecutor.swift new file mode 100644 index 0000000000000..794f9fc542a2a --- /dev/null +++ b/stdlib/public/Concurrency/CooperativeExecutor.swift @@ -0,0 +1,261 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + +import Swift + +// Store the Timestamp in the executor private data, if it will fit; otherwise, +// use the allocator to allocate space for it and stash a pointer in the private +// data area. +@available(SwiftStdlib 6.2, *) +extension ExecutorJob { + fileprivate var cooperativeExecutorTimestampIsIndirect: Bool { + return MemoryLayout<(Int, Int)>.size + < MemoryLayout.size + } + + fileprivate var cooperativeExecutorTimestampPointer: UnsafeMutablePointer { + get { + assert(cooperativeExecutorTimestampIsIndirect) + return unsafe withUnsafeExecutorPrivateData { + unsafe $0.withMemoryRebound(to: UnsafeMutablePointer.self) { + return unsafe $0[0] + } + } + } + set { + assert(cooperativeExecutorTimestampIsIndirect) + unsafe withUnsafeExecutorPrivateData { + unsafe $0.withMemoryRebound(to: UnsafeMutablePointer.self) { + unsafe $0[0] = newValue + } + } + } + } + + fileprivate var cooperativeExecutorTimestamp: CooperativeExecutor.Timestamp { + get { + if cooperativeExecutorTimestampIsIndirect { + let ptr = unsafe cooperativeExecutorTimestampPointer + return unsafe ptr.pointee + } else { + return unsafe withUnsafeExecutorPrivateData { + return unsafe $0.assumingMemoryBound( + to: CooperativeExecutor.Timestamp.self + )[0] + } + } + } + set { + if cooperativeExecutorTimestampIsIndirect { + let ptr = unsafe cooperativeExecutorTimestampPointer + unsafe ptr.pointee = newValue + } else { + unsafe withUnsafeExecutorPrivateData { + unsafe $0.withMemoryRebound(to: CooperativeExecutor.Timestamp.self) { + unsafe $0[0] = newValue + } + } + } + } + } + + fileprivate mutating func setupCooperativeExecutorTimestamp() { + // If a Timestamp won't fit, allocate + if cooperativeExecutorTimestampIsIndirect { + let ptr: UnsafeMutablePointer + // Try to use the task allocator if it has one + if let allocator { + unsafe ptr = allocator.allocate(as: CooperativeExecutor.Timestamp.self) + } else { + unsafe ptr = .allocate(capacity: 1) + } + unsafe self.cooperativeExecutorTimestampPointer = ptr + } + } + + fileprivate mutating func clearCooperativeExecutorTimestamp() { + // If a Timestamp won't fit, deallocate + if cooperativeExecutorTimestampIsIndirect { + let ptr = unsafe self.cooperativeExecutorTimestampPointer + if let allocator { + unsafe allocator.deallocate(ptr) + } else { + unsafe ptr.deallocate() + } + } + } +} + +/// A co-operative executor that can be used as the main executor or as a +/// task executor. +@available(SwiftStdlib 6.2, *) +class CooperativeExecutor: Executor, @unchecked Sendable { + var runQueue: PriorityQueue + var waitQueue: PriorityQueue + var shouldStop: Bool = false + + /// Internal representation of a duration for CooperativeExecutor + struct Duration { + var seconds: Int64 + var nanoseconds: Int64 + + init(seconds: Int64, nanoseconds: Int64) { + self.seconds = seconds + self.nanoseconds = nanoseconds + } + + init(from duration: Swift.Duration) { + let (seconds, attoseconds) = duration.components + self.seconds = seconds + self.nanoseconds = attoseconds / 1_000_000_000 + } + } + + /// Internal representation of a timestamp for CooperativeExecutor + struct Timestamp: Comparable { + var seconds: Int64 + var nanoseconds: Int64 + + static var zero: Timestamp { + return Timestamp(seconds: 0, nanoseconds: 0) + } + + static func == (lhs: Timestamp, rhs: Timestamp) -> Bool { + return lhs.seconds == rhs.seconds && lhs.nanoseconds == rhs.nanoseconds + } + static func < (lhs: Timestamp, rhs: Timestamp) -> Bool { + return lhs.seconds < rhs.seconds || ( + lhs.seconds == rhs.seconds + && lhs.nanoseconds < rhs.nanoseconds + ) + } + static func - (lhs: Timestamp, rhs: Timestamp) -> Duration { + if lhs.nanoseconds < rhs.nanoseconds { + return Duration(seconds: lhs.seconds - rhs.seconds - 1, + nanoseconds: 1_000_000_000 + lhs.nanoseconds + - rhs.nanoseconds) + } + return Duration(seconds: lhs.seconds - rhs.seconds, + nanoseconds: lhs.nanoseconds - rhs.nanoseconds) + } + static func + (lhs: Timestamp, rhs: Duration) -> Timestamp { + var seconds = lhs.seconds + rhs.seconds + var nanoseconds = lhs.nanoseconds + rhs.nanoseconds + // Normally will run only once + while nanoseconds > 1_000_000_000 { + seconds += 1 + nanoseconds -= 1_000_000_000 + } + return Timestamp(seconds: seconds, nanoseconds: nanoseconds) + } + } + + public init() { + runQueue = PriorityQueue(compare: { $0.priority > $1.priority }) + waitQueue = + PriorityQueue(compare: { + ExecutorJob($0).cooperativeExecutorTimestamp + < ExecutorJob($1).cooperativeExecutorTimestamp + }) + } + + public func enqueue(_ job: consuming ExecutorJob) { + runQueue.push(UnownedJob(job)) + } + + public var isMainExecutor: Bool { true } + + public var asSchedulable: any SchedulableExecutor { self } +} + +@available(SwiftStdlib 6.2, *) +extension CooperativeExecutor: SchedulableExecutor { + var currentTime: Timestamp { + var now: Timestamp = .zero + unsafe _getTime(seconds: &now.seconds, + nanoseconds: &now.nanoseconds, + clock: _ClockID.suspending.rawValue) + return now + } + + public func enqueue(_ job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + let duration = Duration(from: clock.convert(from: delay)!) + let deadline = self.currentTime + duration + + job.setupCooperativeExecutorTimestamp() + job.cooperativeExecutorTimestamp = deadline + waitQueue.push(UnownedJob(job)) + } +} + +@available(SwiftStdlib 6.2, *) +extension CooperativeExecutor: RunLoopExecutor { + public func run() throws { + try runUntil { false } + } + + public func runUntil(_ condition: () -> Bool) throws { + shouldStop = false + while !shouldStop && !condition() { + // Process the timer queue + let now = currentTime + while let job = waitQueue.pop(when: { + ExecutorJob($0).cooperativeExecutorTimestamp <= now + }) { + var theJob = ExecutorJob(job) + theJob.clearCooperativeExecutorTimestamp() + runQueue.push(job) + } + + // Now run any queued jobs + while let job = runQueue.pop() { + unsafe ExecutorJob(job).runSynchronously( + on: self.asUnownedSerialExecutor() + ) + } + + // Finally, wait until the next deadline + if let job = waitQueue.top { + let deadline = ExecutorJob(job).cooperativeExecutorTimestamp + let now = self.currentTime + if deadline > now { + let toWait = deadline - now + _sleep(seconds: toWait.seconds, + nanoseconds: toWait.nanoseconds) + } + } else { + // Stop if no more jobs are available + break + } + } + } + + public func stop() { + shouldStop = true + } +} + +@available(SwiftStdlib 6.2, *) +extension CooperativeExecutor: SerialExecutor {} + +@available(SwiftStdlib 6.2, *) +extension CooperativeExecutor: TaskExecutor {} + +@available(SwiftStdlib 6.2, *) +extension CooperativeExecutor: MainExecutor {} + +#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift new file mode 100644 index 0000000000000..3a5abbdf73cc6 --- /dev/null +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -0,0 +1,203 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && !os(WASI) + +import Swift + +// We can't import Dispatch from here, sadly, because it apparently has a +// transitive dependency on Combine (which in turn depends on _Concurrency). + +// import Dispatch + +// .. Dispatch Interface ....................................................... + +// .. Main Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public class DispatchMainExecutor: RunLoopExecutor, @unchecked Sendable { + var threaded = false + + public init() {} + + public func run() throws { + if threaded { + fatalError("DispatchMainExecutor does not support recursion") + } + + threaded = true + _dispatchMain() + } + + public func stop() { + fatalError("DispatchMainExecutor cannot be stopped") + } +} + +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: SerialExecutor { + + public func enqueue(_ job: consuming ExecutorJob) { + _dispatchEnqueueMain(UnownedJob(job)) + } + + public var isMainExecutor: Bool { true } + + public func checkIsolated() { + _dispatchAssertMainQueue() + } +} + +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: SchedulableExecutor { + public var asSchedulable: SchedulableExecutor? { + return self + } + + public func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration? = nil, + clock: C) { + let tolSec, tolNanosec: CLongLong + if let tolerance = tolerance { + (tolSec, tolNanosec) = delay(from: tolerance, clock: clock) + } else { + tolSec = 0 + tolNanosec = -1 + } + + let (clockID, seconds, nanoseconds) = timestamp(for: instant, clock: clock) + + _dispatchEnqueueWithDeadline(CBool(false), + CLongLong(seconds), CLongLong(nanoseconds), + CLongLong(tolSec), CLongLong(tolNanosec), + clockID.rawValue, + UnownedJob(job)) + } +} + +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: MainExecutor {} + +// .. Task Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public class DispatchGlobalTaskExecutor: TaskExecutor, SchedulableExecutor, + @unchecked Sendable { + public init() {} + + public func enqueue(_ job: consuming ExecutorJob) { + _dispatchEnqueueGlobal(UnownedJob(job)) + } + + public var isMainExecutor: Bool { false } + + public func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration? = nil, + clock: C) { + let tolSec, tolNanosec: CLongLong + if let tolerance = tolerance { + (tolSec, tolNanosec) = delay(from: tolerance, clock: clock) + } else { + tolSec = 0 + tolNanosec = -1 + } + + let (clockID, seconds, nanoseconds) = timestamp(for: instant, clock: clock) + + _dispatchEnqueueWithDeadline(CBool(true), + CLongLong(seconds), CLongLong(nanoseconds), + CLongLong(tolSec), CLongLong(tolNanosec), + clockID.rawValue, + UnownedJob(job)) + } +} + +// .. Clock Support ............................................................ + +/// DispatchMainExecutor and DispatchTaskExecutor both implement this +/// protocol. +/// +/// It is used to help convert instants and durations from arbitrary `Clock`s +/// to Dispatch's time base. +@available(SwiftStdlib 6.2, *) +protocol DispatchExecutorProtocol: Executor { + + /// Convert an `Instant` from the specified clock to a tuple identifying + /// the Dispatch clock and the seconds and nanoseconds components. + /// + /// Parameters: + /// + /// - for instant: The `Instant` to convert. + /// - clock: The `Clock` instant that the `Instant` came from. + /// + /// Returns: A tuple of `(clockID, seconds, nanoseconds)`. + func timestamp(for instant: C.Instant, clock: C) + -> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64) + + /// Convert a `Duration` from the specified clock to a tuple containing + /// seconds and nanosecond components. + func delay(from duration: C.Duration, clock: C) + -> (seconds: Int64, nanoseconds: Int64) + +} + +/// An enumeration identifying one of the Dispatch-supported clocks +enum DispatchClockID: CInt { + case suspending = 1 + case continuous = 2 +} + +@available(SwiftStdlib 6.2, *) +extension DispatchExecutorProtocol { + + func timestamp(for instant: C.Instant, clock: C) + -> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64) { + if clock.traits.contains(.continuous) { + let dispatchClock: ContinuousClock = .continuous + let instant = dispatchClock.convert(instant: instant, from: clock)! + let (seconds, attoseconds) = instant._value.components + let nanoseconds = attoseconds / 1_000_000_000 + return (clockID: .continuous, + seconds: Int64(seconds), + nanoseconds: Int64(nanoseconds)) + } else { + let dispatchClock: SuspendingClock = .suspending + let instant = dispatchClock.convert(instant: instant, from: clock)! + let (seconds, attoseconds) = instant._value.components + let nanoseconds = attoseconds / 1_000_000_000 + return (clockID: .suspending, + seconds: Int64(seconds), + nanoseconds: Int64(nanoseconds)) + } + } + + func delay(from duration: C.Duration, clock: C) + -> (seconds: Int64, nanoseconds: Int64) { + let swiftDuration = clock.convert(from: duration)! + let (seconds, attoseconds) = swiftDuration.components + let nanoseconds = attoseconds / 1_000_000_000 + return (seconds: seconds, nanoseconds: nanoseconds) + } + +} + +@available(SwiftStdlib 6.2, *) +extension DispatchGlobalTaskExecutor: DispatchExecutorProtocol { +} + +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: DispatchExecutorProtocol { +} + +#endif // !$Embedded && !os(WASI) diff --git a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp index 58fa5b09ab524..b5face3c0fbb3 100644 --- a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp +++ b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp @@ -200,8 +200,8 @@ static dispatch_queue_t getTimerQueue(SwiftJobPriority priority) { return dispatch_get_global_queue((dispatch_qos_class_t)priority, /*flags*/ 0); } -SWIFT_CC(swift) -void swift_task_enqueueGlobalImpl(SwiftJob *job) { +extern "C" SWIFT_CC(swift) +void swift_dispatchEnqueueGlobal(SwiftJob *job) { assert(job && "no job provided"); // We really want four things from the global execution service: // - Enqueuing work should have minimal runtime and memory overhead. @@ -240,33 +240,6 @@ void swift_task_enqueueGlobalImpl(SwiftJob *job) { DISPATCH_QUEUE_GLOBAL_EXECUTOR); } - -SWIFT_CC(swift) -void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay, - SwiftJob *job) { - assert(job && "no job provided"); - - dispatch_function_t dispatchFunction = &__swift_run_job; - void *dispatchContext = job; - - SwiftJobPriority priority = swift_job_getPriority(job); - - auto queue = getTimerQueue(priority); - - job->schedulerPrivate[SwiftJobDispatchQueueIndex] = - DISPATCH_QUEUE_GLOBAL_EXECUTOR; - - // dispatch_time takes a signed int64_t. SwiftJobDelay is unsigned, so - // extremely large values get interpreted as negative numbers, which results - // in zero delay. Clamp the value to INT64_MAX. That's about 292 years, so - // there should be no noticeable difference. - if (delay > (SwiftJobDelay)INT64_MAX) - delay = INT64_MAX; - - dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delay); - dispatch_after_f(when, queue, dispatchContext, dispatchFunction); -} - #define DISPATCH_UP_OR_MONOTONIC_TIME_MASK (1ULL << 63) #define DISPATCH_WALLTIME_MASK (1ULL << 62) #define DISPATCH_TIME_MAX_VALUE (DISPATCH_WALLTIME_MASK - 1) @@ -335,52 +308,70 @@ clock_and_value_to_time(int clock, long long deadline) { __builtin_unreachable(); } -SWIFT_CC(swift) -void swift_task_enqueueGlobalWithDeadlineImpl(long long sec, - long long nsec, - long long tsec, - long long tnsec, - int clock, SwiftJob *job) { +extern "C" SWIFT_CC(swift) +void swift_dispatchEnqueueWithDeadline(bool global, + long long sec, + long long nsec, + long long tsec, + long long tnsec, + int clock, SwiftJob *job) { assert(job && "no job provided"); SwiftJobPriority priority = swift_job_getPriority(job); - auto queue = getTimerQueue(priority); + dispatch_queue_t queue; + + if (global) { + queue = getTimerQueue(priority); - job->schedulerPrivate[SwiftJobDispatchQueueIndex] = + job->schedulerPrivate[SwiftJobDispatchQueueIndex] = DISPATCH_QUEUE_GLOBAL_EXECUTOR; + } else { + queue = dispatch_get_main_queue(); + + job->schedulerPrivate[SwiftJobDispatchQueueIndex] = queue; + } + + uint64_t deadline; + if (__builtin_mul_overflow(sec, NSEC_PER_SEC, &deadline) + || __builtin_add_overflow(nsec, deadline, &deadline)) { + deadline = UINT64_MAX; + } - uint64_t deadline = sec * NSEC_PER_SEC + nsec; dispatch_time_t when = clock_and_value_to_time(clock, deadline); - + if (tnsec != -1) { - uint64_t leeway = tsec * NSEC_PER_SEC + tnsec; + uint64_t leeway; + if (__builtin_mul_overflow(tsec, NSEC_PER_SEC, &leeway) + || __builtin_add_overflow(tnsec, deadline, &leeway)) { + leeway = UINT64_MAX; + } - dispatch_source_t source = + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer(source, when, DISPATCH_TIME_FOREVER, leeway); size_t sz = sizeof(struct __swift_job_source); - struct __swift_job_source *jobSource = + struct __swift_job_source *jobSource = (struct __swift_job_source *)swift_job_alloc(job, sz); jobSource->job = job; jobSource->source = source; dispatch_set_context(source, jobSource); - dispatch_source_set_event_handler_f(source, + dispatch_source_set_event_handler_f(source, (dispatch_function_t)&_swift_run_job_leeway); dispatch_activate(source); } else { - dispatch_after_f(when, queue, (void *)job, + dispatch_after_f(when, queue, (void *)job, (dispatch_function_t)&__swift_run_job); } } -SWIFT_CC(swift) -void swift_task_enqueueMainExecutorImpl(SwiftJob *job) { +extern "C" SWIFT_CC(swift) +void swift_dispatchEnqueueMain(SwiftJob *job) { assert(job && "no job provided"); SwiftJobPriority priority = swift_job_getPriority(job); @@ -397,116 +388,3 @@ void swift::swift_task_enqueueOnDispatchQueue(Job *job, auto queue = reinterpret_cast(_queue); dispatchEnqueue(queue, (SwiftJob *)job, (dispatch_qos_class_t)priority, queue); } - -/// Recognize if the SerialExecutor is specifically a `DispatchSerialQueue` -/// by comparing witness tables and return it if true. -static dispatch_queue_s *getAsDispatchSerialQueue(SwiftExecutorRef executor) { - if (!swift_executor_hasWitnessTable(executor)) { - return nullptr; - } - - auto executorWitnessTable = reinterpret_cast( - swift_executor_getWitnessTable(executor)); - auto serialQueueWitnessTable = reinterpret_cast( - _swift_task_getDispatchQueueSerialExecutorWitnessTable()); - - if (swift_compareWitnessTables(executorWitnessTable, - serialQueueWitnessTable)) { - auto identity = swift_executor_getIdentity(executor); - return reinterpret_cast(identity); - } else { - return nullptr; - } -} - -/// If the executor is a `DispatchSerialQueue` we're able to invoke the -/// dispatch's precondition API directly -- this is more efficient than going -/// through the runtime call to end up calling the same API, and also allows us -/// to perform this assertion on earlier platforms, where the `checkIsolated` -/// requirement/witness was not shipping yet. -SWIFT_CC(swift) -void swift_task_checkIsolatedImpl(SwiftExecutorRef executor) { - // If it is the main executor, compare with the Main queue - if (swift_executor_isMain(executor)) { - dispatch_assert_queue(dispatch_get_main_queue()); - return; - } - - // if able to, use the checkIsolated implementation in Swift - if (swift_executor_invokeSwiftCheckIsolated(executor)) - return; - - if (auto queue = getAsDispatchSerialQueue(executor)) { - // if the executor was not SerialExecutor for some reason but we're able - // to get a queue from it anyway, use the assert directly on it. - dispatch_assert_queue(queue); // TODO(concurrency): could we report a better message here somehow? - return; - } - - // otherwise, we have no way to check, so report an error - // TODO: can we swift_getTypeName(swift_getObjectType(executor.getIdentity()), false).data safely in the message here? - swift_Concurrency_fatalError(0, "Incorrect actor executor assumption"); -} - -SWIFT_CC(swift) -bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) { - return swift_executor_invokeSwiftIsIsolatingCurrentContext(executor); -} - - -SWIFT_CC(swift) -SwiftExecutorRef swift_task_getMainExecutorImpl() { - return swift_executor_ordinary( - reinterpret_cast(&_dispatch_main_q), - reinterpret_cast( - _swift_task_getDispatchQueueSerialExecutorWitnessTable())); -} - -SWIFT_CC(swift) -bool swift_task_isMainExecutorImpl(SwiftExecutorRef executor) { - return swift_executor_getIdentity(executor) - == reinterpret_cast(&_dispatch_main_q); -} - -SWIFT_CC(swift) void -swift_task_donateThreadToGlobalExecutorUntilImpl(bool (*condition)(void *), - void *conditionContext) { - swift_Concurrency_fatalError(0, "Not supported for Dispatch executor"); -} - -SWIFT_RUNTIME_ATTRIBUTE_NORETURN -SWIFT_CC(swift) -void swift_task_asyncMainDrainQueueImpl() { -#if defined(_WIN32) - HMODULE hModule = LoadLibraryW(L"dispatch.dll"); - if (hModule == NULL) { - swift_Concurrency_fatalError(0, - "unable to load dispatch.dll: %lu", GetLastError()); - } - - auto pfndispatch_main = reinterpret_cast( - GetProcAddress(hModule, "dispatch_main")); - if (pfndispatch_main == NULL) { - swift_Concurrency_fatalError(0, - "unable to locate dispatch_main in dispatch.dll: %lu", GetLastError()); - } - - pfndispatch_main(); - swift_unreachable("Returned from dispatch_main()"); -#else - // CFRunLoop is not available on non-Darwin targets. Foundation has an - // implementation, but CoreFoundation is not meant to be exposed. We can only - // assume the existence of `CFRunLoopRun` on Darwin platforms, where the - // system provides an implementation of CoreFoundation. -#if defined(__APPLE__) - auto runLoop = - reinterpret_cast(dlsym(RTLD_DEFAULT, "CFRunLoopRun")); - if (runLoop) { - runLoop(); - exit(0); - } -#endif - - dispatch_main(); -#endif -} diff --git a/stdlib/public/Concurrency/DummyExecutor.swift b/stdlib/public/Concurrency/DummyExecutor.swift new file mode 100644 index 0000000000000..ec7bc7897198a --- /dev/null +++ b/stdlib/public/Concurrency/DummyExecutor.swift @@ -0,0 +1,63 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +// .. Main Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public final class DummyMainExecutor: MainExecutor, @unchecked Sendable { + public init() {} + + public func run() throws { + fatalError("There is no executor implementation active") + } + + public func stop() { + fatalError("There is no executor implementation active") + } + + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + public func enqueue(_ job: UnownedJob) { + fatalError("There is no executor implementation active") + } + #else + public func enqueue(_ job: consuming ExecutorJob) { + fatalError("There is no executor implementation active") + } + #endif + + public var isMainExecutor: Bool { true } + + public func checkIsolated() { + // Do nothing + } +} + +// .. Task Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public final class DummyTaskExecutor: TaskExecutor, @unchecked Sendable { + public init() {} + + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + public func enqueue(_ job: UnownedJob) { + fatalError("There is no executor implementation active") + } + #else + public func enqueue(_ job: consuming ExecutorJob) { + fatalError("There is no executor implementation active") + } + #endif + + public var isMainExecutor: Bool { false } +} diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index c4ff1a319dc58..1c90ccccd83c7 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -35,6 +35,134 @@ public protocol Executor: AnyObject, Sendable { @available(SwiftStdlib 5.9, *) func enqueue(_ job: consuming ExecutorJob) #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + // The functions below could have been added to a separate protocol, + // but doing that would then mean doing an `as?` cast in e.g. + // enqueueOnGlobalExecutor (in ExecutorBridge.swift), which is + // undesirable from a performance perspective. + + /// `true` if this is the main executor. + @available(SwiftStdlib 6.2, *) + var isMainExecutor: Bool { get } + #endif +} + +@available(SwiftStdlib 6.2, *) +public protocol SchedulableExecutor: Executor { + + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + + /// Enqueue a job to run after a specified delay. + /// + /// You need only implement one of the two enqueue functions here; + /// the default implementation for the other will then call the one + /// you have implemented. + /// + /// Parameters: + /// + /// - job: The job to schedule. + /// - after: A `Duration` specifying the time after which the job + /// is to run. The job will not be executed before this + /// time has elapsed. + /// - tolerance: The maximum additional delay permissible before the + /// job is executed. `nil` means no limit. + /// - clock: The clock used for the delay. + @available(SwiftStdlib 6.2, *) + func enqueue(_ job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration?, + clock: C) + + /// Enqueue a job to run at a specified time. + /// + /// You need only implement one of the two enqueue functions here; + /// the default implementation for the other will then call the one + /// you have implemented. + /// + /// Parameters: + /// + /// - job: The job to schedule. + /// - at: The `Instant` at which the job should run. The job + /// will not be executed before this time. + /// - tolerance: The maximum additional delay permissible before the + /// job is executed. `nil` means no limit. + /// - clock: The clock used for the delay.. + @available(SwiftStdlib 6.2, *) + func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration?, + clock: C) + + #endif // !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + +} + +extension Executor { + /// Return this executable as a SchedulableExecutor, or nil if that is + /// unsupported. + /// + /// Executors that implement SchedulableExecutor should provide their + /// own copy of this method, which will allow the compiler to avoid a + /// potentially expensive runtime cast. + @available(SwiftStdlib 6.2, *) + var asSchedulable: SchedulableExecutor? { + return self as? SchedulableExecutor + } +} + +extension Executor { + @available(SwiftStdlib 6.2, *) + @usableFromInline + internal var _isComplexEquality: Bool { false } +} + +extension Executor where Self: Equatable { + @available(SwiftStdlib 6.2, *) + @usableFromInline + internal var _isComplexEquality: Bool { true } +} + +extension Executor { + + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + // This defaults to `false` so that existing third-party Executor + // implementations will work as expected. + @available(SwiftStdlib 6.2, *) + public var isMainExecutor: Bool { false } + #endif + +} + +// Delay support +@available(SwiftStdlib 6.2, *) +extension SchedulableExecutor { + + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + + @available(SwiftStdlib 6.2, *) + public func enqueue(_ job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + // If you crash here with a mutual recursion, it's because you didn't + // implement one of these two functions + enqueue(job, at: clock.now.advanced(by: delay), + tolerance: tolerance, clock: clock) + } + + @available(SwiftStdlib 6.2, *) + public func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration? = nil, + clock: C) { + // If you crash here with a mutual recursion, it's because you didn't + // implement one of these two functions + enqueue(job, after: clock.now.duration(to: instant), + tolerance: tolerance, clock: clock) + } + + #endif // !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY } /// A service that executes jobs. @@ -203,6 +331,11 @@ public protocol SerialExecutor: Executor { @available(SwiftStdlib 6.0, *) extension SerialExecutor { + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + @available(SwiftStdlib 6.2, *) + public var isMainExecutor: Bool { return MainActor.executor._isSameExecutor(self) } + #endif + @available(SwiftStdlib 6.0, *) public func checkIsolated() { #if !$Embedded @@ -211,6 +344,17 @@ extension SerialExecutor { Builtin.int_trap() #endif } + + @available(SwiftStdlib 6.2, *) + internal func _isSameExecutor(_ rhs: some SerialExecutor) -> Bool { + if rhs === self { + return true + } + if let rhs = rhs as? Self { + return isSameExclusiveExecutionContext(other: rhs) + } + return false + } } @available(SwiftStdlib 6.2, *) @@ -240,7 +384,6 @@ extension SerialExecutor { /// the provided task executor. /// /// Unstructured tasks do not inherit the task executor. -@_unavailableInEmbedded @available(SwiftStdlib 6.0, *) public protocol TaskExecutor: Executor { // This requirement is repeated here as a non-override so that we @@ -272,11 +415,10 @@ public protocol TaskExecutor: Executor { func asUnownedTaskExecutor() -> UnownedTaskExecutor } -@_unavailableInEmbedded @available(SwiftStdlib 6.0, *) extension TaskExecutor { public func asUnownedTaskExecutor() -> UnownedTaskExecutor { - UnownedTaskExecutor(ordinary: self) + unsafe UnownedTaskExecutor(ordinary: self) } } @@ -319,6 +461,192 @@ extension SerialExecutor { } +@available(SwiftStdlib 6.2, *) +extension SerialExecutor where Self: Equatable { + + @available(SwiftStdlib 6.2, *) + public func isSameExclusiveExecutionContext(other: Self) -> Bool { + return self == other + } + +} + +/// An executor that is backed by some kind of run loop. +/// +/// The idea here is that some executors may work by running a loop +/// that processes events of some sort; we want a way to enter that loop, +/// and we would also like a way to trigger the loop to exit. +@available(SwiftStdlib 6.2, *) +public protocol RunLoopExecutor: Executor { + /// Run the executor's run loop. + /// + /// This method will synchronously block the calling thread. Nested calls to + /// `run()` may be permitted, however it is not permitted to call `run()` on a + /// single executor instance from more than one thread. + func run() throws + + /// Run the executor's run loop until a condition is satisfied. + /// + /// Not every `RunLoopExecutor` will support this method; you must not call + /// it unless you *know* that it is supported. The default implementation + /// generates a fatal error. + /// + /// Parameters: + /// + /// - condition: A closure that returns `true` if the run loop should + /// stop. + func runUntil(_ condition: () -> Bool) throws + + /// Signal to the run loop to stop running and return. + /// + /// This method may be called from the same thread that is in the `run()` + /// method, or from some other thread. It will not wait for the run loop to + /// stop; calling this method simply signals that the run loop *should*, as + /// soon as is practicable, stop the innermost `run()` invocation and make + /// that `run()` invocation return. + func stop() +} + +@available(SwiftStdlib 6.2, *) +extension RunLoopExecutor { + + public func runUntil(_ condition: () -> Bool) throws { + fatalError("run(until condition:) not supported on this executor") + } + +} + + +/// The main executor must conform to these three protocols; we have to +/// make this a protocol for compatibility with Embedded Swift. +@available(SwiftStdlib 6.2, *) +public protocol MainExecutor: RunLoopExecutor, SerialExecutor { +} + + +/// An ExecutorFactory is used to create the default main and task +/// executors. +@available(SwiftStdlib 6.2, *) +public protocol ExecutorFactory { + #if !$Embedded + /// Constructs and returns the main executor, which is started implicitly + /// by the `async main` entry point and owns the "main" thread. + static var mainExecutor: any MainExecutor { get } + #endif + + /// Constructs and returns the default or global executor, which is the + /// default place in which we run tasks. + static var defaultExecutor: any TaskExecutor { get } +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_createExecutors") +public func _createExecutors(factory: F.Type) { + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + MainActor._executor = factory.mainExecutor + #endif + Task._defaultExecutor = factory.defaultExecutor +} + +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +extension MainActor { + @available(SwiftStdlib 6.2, *) + static var _executor: (any MainExecutor)? = nil + + /// The main executor, which is started implicitly by the `async main` + /// entry point and owns the "main" thread. + /// + /// Attempting to set this after the first `enqueue` on the main + /// executor is a fatal error. + @available(SwiftStdlib 6.2, *) + public static var executor: any MainExecutor { + if _executor == nil { + _executor = PlatformExecutorFactory.mainExecutor + } + return _executor! + } +} +#endif // !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + +extension Task where Success == Never, Failure == Never { + @available(SwiftStdlib 6.2, *) + static var _defaultExecutor: (any TaskExecutor)? = nil + + /// The default or global executor, which is the default place in which + /// we run tasks. + /// + /// Attempting to set this after the first `enqueue` on the global + /// executor is a fatal error. + @available(SwiftStdlib 6.2, *) + public static var defaultExecutor: any TaskExecutor { + if _defaultExecutor == nil { + _defaultExecutor = PlatformExecutorFactory.defaultExecutor + } + return _defaultExecutor! + } +} + +extension Task where Success == Never, Failure == Never { + /// Get the current executor; this is the executor that the currently + /// executing task is executing on. + /// + /// This will return, in order of preference: + /// + /// 1. The custom executor associated with an `Actor` on which we are + /// currently running, or + /// 2. The preferred executor for the currently executing `Task`, or + /// 3. The task executor for the current thread + /// + /// If none of these exist, this property will be `nil`. + @available(SwiftStdlib 6.2, *) + @_unavailableInEmbedded + public static var currentExecutor: any Executor { + if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor() { + return activeExecutor + } else if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() { + return taskExecutor + } else if let taskExecutor = unsafe _getCurrentTaskExecutor().asTaskExecutor() { + return taskExecutor + } + return defaultExecutor + } + + /// Get the preferred executor for the current `Task`, if any. + @available(SwiftStdlib 6.2, *) + public static var preferredExecutor: (any TaskExecutor)? { + if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() { + return taskExecutor + } + return nil + } + + /// Get the current *schedulable* executor, if any. + /// + /// This follows the same logic as `currentExecutor`, except that it ignores + /// any executor that isn't a `SchedulableExecutor`. + @available(SwiftStdlib 6.2, *) + @_unavailableInEmbedded + public static var currentSchedulableExecutor: (any SchedulableExecutor)? { + if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor(), + let schedulable = activeExecutor.asSchedulable { + return schedulable + } + if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor(), + let schedulable = taskExecutor.asSchedulable { + return schedulable + } + if let taskExecutor = unsafe _getCurrentTaskExecutor().asTaskExecutor(), + let schedulable = taskExecutor.asSchedulable { + return schedulable + } + if let schedulable = defaultExecutor.asSchedulable { + return schedulable + } + return nil + } +} + + /// An unowned reference to a serial executor (a `SerialExecutor` /// value). /// @@ -370,17 +698,33 @@ public struct UnownedSerialExecutor: Sendable { unsafe self.executor = Builtin.buildComplexEqualitySerialExecutorRef(executor) } + /// Automatically opt-in to complex equality semantics if the Executor + /// implements `Equatable`. + @available(SwiftStdlib 6.2, *) + @inlinable + public init(_ executor: __shared E) { + if executor._isComplexEquality { + unsafe self.executor = Builtin.buildComplexEqualitySerialExecutorRef(executor) + } else { + unsafe self.executor = Builtin.buildOrdinarySerialExecutorRef(executor) + } + } + @_spi(ConcurrencyExecutors) @available(SwiftStdlib 5.9, *) public var _isComplexEquality: Bool { unsafe _executor_isComplexEquality(self) } + @available(SwiftStdlib 6.2, *) + public func asSerialExecutor() -> (any SerialExecutor)? { + return unsafe unsafeBitCast(executor, to: (any SerialExecutor)?.self) + } } -@_unavailableInEmbedded @available(SwiftStdlib 6.0, *) +@unsafe @frozen public struct UnownedTaskExecutor: Sendable { @usableFromInline @@ -390,21 +734,31 @@ public struct UnownedTaskExecutor: Sendable { /// which needs to reach for this from an @_transparent function which prevents @_spi use. @available(SwiftStdlib 6.0, *) public var _executor: Builtin.Executor { - self.executor + unsafe self.executor } @inlinable public init(_ executor: Builtin.Executor) { - self.executor = executor + unsafe self.executor = executor } @inlinable public init(ordinary executor: __shared E) { - self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) + unsafe self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) + } + + @available(SwiftStdlib 6.2, *) + @inlinable + public init(_ executor: __shared E) { + unsafe self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) + } + + @available(SwiftStdlib 6.2, *) + public func asTaskExecutor() -> (any TaskExecutor)? { + return unsafe unsafeBitCast(executor, to: (any TaskExecutor)?.self) } } -@_unavailableInEmbedded @available(SwiftStdlib 6.0, *) extension UnownedTaskExecutor: Equatable { @inlinable @@ -503,7 +857,7 @@ internal func _task_serialExecutor_getExecutorRef(_ executor: E) -> Builtin.E @_silgen_name("_task_taskExecutor_getTaskExecutorRef") internal func _task_taskExecutor_getTaskExecutorRef(_ taskExecutor: E) -> Builtin.Executor where E: TaskExecutor { - return taskExecutor.asUnownedTaskExecutor().executor + return unsafe taskExecutor.asUnownedTaskExecutor().executor } // Used by the concurrency runtime diff --git a/stdlib/public/Concurrency/ExecutorBridge.cpp b/stdlib/public/Concurrency/ExecutorBridge.cpp new file mode 100644 index 0000000000000..50595d7180b82 --- /dev/null +++ b/stdlib/public/Concurrency/ExecutorBridge.cpp @@ -0,0 +1,134 @@ +//===--- ExecutorBridge.cpp - C++ side of executor bridge -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if SWIFT_CONCURRENCY_USES_DISPATCH +#include +#endif + +#include "Error.h" +#include "ExecutorBridge.h" + +using namespace swift; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" + +extern "C" SWIFT_CC(swift) +void _swift_exit(int result) { + exit(result); +} + +extern "C" SWIFT_CC(swift) +void _swift_task_checkIsolatedSwift( + HeapObject *executor, + const Metadata *executorType, + const SerialExecutorWitnessTable *witnessTable +); + +extern "C" SWIFT_CC(swift) +void _swift_task_checkIsolatedSwift(HeapObject *executor, + const Metadata *executorType, + const SerialExecutorWitnessTable *witnessTable); + +extern "C" SWIFT_CC(swift) +bool _swift_task_isIsolatingCurrentContextSwift( + HeapObject *executor, + const Metadata *executorType, + const SerialExecutorWitnessTable *witnessTable +); + +extern "C" SWIFT_CC(swift) +bool _swift_task_isMainExecutorSwift( + HeapObject *executor, + const Metadata *executorType, + const SerialExecutorWitnessTable *witnessTable +); + +extern "C" SWIFT_CC(swift) +void swift_task_checkIsolatedImpl(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()) { + _swift_task_checkIsolatedSwift(identity, + swift_getObjectType(identity), + executor.getSerialExecutorWitnessTable()); + } else { + const Metadata *objectType = swift_getObjectType(executor.getIdentity()); + auto typeName = swift_getTypeName(objectType, true); + + swift_Concurrency_fatalError( + 0, "Incorrect actor executor assumption; expected '%.*s' executor.\n", + (int)typeName.length, typeName.data); + } +} + +extern "C" SWIFT_CC(swift) +bool 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); + + swift_Concurrency_fatalError( + 0, "Incorrect actor executor assumption; expected '%.*s' executor.\n", + (int)typeName.length, typeName.data); + } +} + +extern "C" SWIFT_CC(swift) +bool swift_task_isMainExecutorImpl(SerialExecutorRef executor) { + HeapObject *identity = executor.getIdentity(); + return executor.hasSerialExecutorWitnessTable() + && _swift_task_isMainExecutorSwift(identity, + swift_getObjectType(identity), + executor.getSerialExecutorWitnessTable()); +} + +extern "C" SWIFT_CC(swift) +uint8_t swift_job_getPriority(Job *job) { + return (uint8_t)(job->getPriority()); +} + +extern "C" SWIFT_CC(swift) +uint8_t swift_job_getKind(Job *job) { + return (uint8_t)(job->Flags.getKind()); +} + +extern "C" SWIFT_CC(swift) +void *swift_job_getExecutorPrivateData(Job *job) { + return &job->SchedulerPrivate[0]; +} + +#if SWIFT_CONCURRENCY_USES_DISPATCH +extern "C" SWIFT_CC(swift) __attribute__((noreturn)) +void swift_dispatchMain() { + dispatch_main(); +} + +extern "C" SWIFT_CC(swift) +void swift_dispatchAssertMainQueue() { + dispatch_assert_queue(dispatch_get_main_queue()); +} +#endif // SWIFT_CONCURRENCY_ENABLE_DISPATCH + +#pragma clang diagnostic pop diff --git a/stdlib/public/Concurrency/ExecutorBridge.h b/stdlib/public/Concurrency/ExecutorBridge.h new file mode 100644 index 0000000000000..aa2f720eb264f --- /dev/null +++ b/stdlib/public/Concurrency/ExecutorBridge.h @@ -0,0 +1,44 @@ +//===--- ExecutorBridge.h - C++ side of executor bridge -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_EXECUTOR_BRIDGE_H_ +#define SWIFT_EXECUTOR_BRIDGE_H_ + +#include "swift/Runtime/Concurrency.h" + +namespace swift { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" + +extern "C" SWIFT_CC(swift) +SerialExecutorRef swift_getMainExecutor(); + +#if !SWIFT_CONCURRENCY_EMBEDDED +extern "C" SWIFT_CC(swift) +void *swift_createDispatchEventC(void (*handler)(void *), void *context); + +extern "C" SWIFT_CC(swift) +void swift_destroyDispatchEventC(void *event); + +extern "C" SWIFT_CC(swift) +void swift_signalDispatchEvent(void *event); +#endif // !SWIFT_CONCURRENCY_EMBEDDED + +extern "C" SWIFT_CC(swift) __attribute__((noreturn)) +void swift_dispatchMain(); + +#pragma clang diagnostic pop + +} // namespace swift + +#endif /* SWIFT_EXECUTOR_BRIDGE_H_ */ diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift new file mode 100644 index 0000000000000..1caa4e31aee57 --- /dev/null +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -0,0 +1,114 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Functions to bridge between C++ and Swift. This does not include the +// *Impl functions because we need them separate for Embedded Swift. +// +//===----------------------------------------------------------------------===// + +import Swift + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_exit") +internal func _exit(result: CInt) + +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_task_isMainExecutorSwift") +internal func _isMainExecutor(_ executor: E) -> Bool where E: SerialExecutor { + return executor.isMainExecutor +} +#endif + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_task_checkIsolatedSwift") +internal func checkIsolated(executor: E) where E: SerialExecutor { + executor.checkIsolated() +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_task_isIsolatingCurrentContextSwift") +internal func isIsolatingCurrentContext(executor: E) -> Bool + where E: SerialExecutor +{ + return executor.isIsolatingCurrentContext() +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_getActiveExecutor") +internal func _getActiveExecutor() -> UnownedSerialExecutor + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_getCurrentTaskExecutor") +internal func _getCurrentTaskExecutor() -> UnownedTaskExecutor + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_getPreferredTaskExecutor") +internal func _getPreferredTaskExecutor() -> UnownedTaskExecutor + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_allocate") +internal func _jobAllocate(_ job: Builtin.Job, + _ capacity: Int) -> UnsafeMutableRawPointer + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_deallocate") +internal func _jobDeallocate(_ job: Builtin.Job, + _ address: UnsafeMutableRawPointer) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_getPriority") +internal func _jobGetPriority(_ job: Builtin.Job) -> UInt8 + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_getKind") +internal func _jobGetKind(_ job: Builtin.Job) -> UInt8 + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_getExecutorPrivateData") +internal func _jobGetExecutorPrivateData( + _ job: Builtin.Job +) -> UnsafeMutableRawPointer + +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_getMainExecutor") +internal func _getMainExecutor() -> any MainExecutor { + return MainActor.executor +} +#endif + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchMain") +internal func _dispatchMain() + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchEnqueueMain") +internal func _dispatchEnqueueMain(_ job: UnownedJob) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchEnqueueGlobal") +internal func _dispatchEnqueueGlobal(_ job: UnownedJob) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchEnqueueWithDeadline") +internal func _dispatchEnqueueWithDeadline(_ global: CBool, + _ sec: CLongLong, + _ nsec: CLongLong, + _ tsec: CLongLong, + _ tnsec: CLongLong, + _ clock: CInt, + _ job: UnownedJob) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchAssertMainQueue") +internal func _dispatchAssertMainQueue() diff --git a/stdlib/public/Concurrency/ExecutorImpl.h b/stdlib/public/Concurrency/ExecutorImpl.h index fe461743cb10d..a068c456b3c05 100644 --- a/stdlib/public/Concurrency/ExecutorImpl.h +++ b/stdlib/public/Concurrency/ExecutorImpl.h @@ -92,9 +92,9 @@ static inline int swift_priority_getBucketIndex(SwiftJobPriority priority) { /// Used by the Concurrency runtime to represent a job. The `schedulerPrivate` /// field may be freely used by the executor implementation. typedef struct { - SwiftHeapMetadata const *__ptrauth_objc_isa_pointer metadata; + SwiftHeapMetadata const *__ptrauth_objc_isa_pointer _Nonnull metadata; uintptr_t refCounts; - void *schedulerPrivate[2]; + void * _Nullable schedulerPrivate[2]; SwiftJobFlags flags; } __attribute__((aligned(2 * sizeof(void *)))) SwiftJob; @@ -109,30 +109,35 @@ enum { }; /// Get the kind of a job, by directly accessing the flags field. -static inline SwiftJobKind swift_job_getKind(SwiftJob *job) { +static inline SwiftJobKind swift_job_getKind(SwiftJob * _Nonnull job) { return (SwiftJobKind)(job->flags & 0xff); } /// Get the priority of a job, by directly accessing the flags field. -static inline SwiftJobPriority swift_job_getPriority(SwiftJob *job) { +static inline SwiftJobPriority swift_job_getPriority(SwiftJob * _Nonnull job) { return (SwiftJobPriority)((job->flags >> 8) & 0xff); } +/// Get a pointer to the scheduler private data. +static inline void * _Nonnull * _Nonnull swift_job_getPrivateData(SwiftJob * _Nonnull job) { + return &job->schedulerPrivate[0]; +} + /// Allocate memory associated with a job. -void *swift_job_alloc(SwiftJob *job, size_t size); +void * _Nullable swift_job_alloc(SwiftJob * _Nonnull job, size_t size); /// Release memory allocated using `swift_job_alloc()`. -void swift_job_dealloc(SwiftJob *job, void *ptr); +void swift_job_dealloc(SwiftJob * _Nonnull job, void * _Nonnull ptr); /// Swift's refcounted objects start with this header typedef struct { - SwiftHeapMetadata const *__ptrauth_objc_isa_pointer metadata; + SwiftHeapMetadata const *__ptrauth_objc_isa_pointer _Nonnull metadata; } SwiftHeapObject; /// A reference to an executor consists of two words; the first is a pointer /// which may or may not be to a Swift heap object. typedef struct { - SwiftHeapObject *identity; + SwiftHeapObject * _Nullable identity; uintptr_t implementation; } SwiftExecutorRef; @@ -153,8 +158,8 @@ static inline SwiftExecutorRef swift_executor_generic(void) { /// Return an ordinary executor with the specified identity and witness table. static inline SwiftExecutorRef -swift_executor_ordinary(SwiftHeapObject *identity, - SwiftExecutorWitnessTable *witnessTable) { +swift_executor_ordinary(SwiftHeapObject * _Nullable identity, + SwiftExecutorWitnessTable * _Nullable witnessTable) { return (SwiftExecutorRef){ identity, (uintptr_t)witnessTable | SwiftExecutorOrdinaryKind }; @@ -163,8 +168,8 @@ swift_executor_ordinary(SwiftHeapObject *identity, /// Return a complex equality executor with the specified identity and /// witness table. static inline SwiftExecutorRef -swift_executor_complexEquality(SwiftHeapObject *identity, - SwiftExecutorWitnessTable *witnessTable) { +swift_executor_complexEquality(SwiftHeapObject * _Nullable identity, + SwiftExecutorWitnessTable * _Nullable witnessTable) { return (SwiftExecutorRef){ identity, (uintptr_t)witnessTable | SwiftExecutorComplexEqualityKind }; @@ -188,7 +193,7 @@ static inline bool swift_executor_isDefaultActor(SwiftExecutorRef executor) { } /// Retrieve the identity of an executor. -static inline SwiftHeapObject * +static inline SwiftHeapObject * _Nullable swift_executor_getIdentity(SwiftExecutorRef executor) { return executor.identity; } @@ -200,7 +205,7 @@ static inline bool swift_executor_hasWitnessTable(SwiftExecutorRef executor) { } /// Retrieve the witness table of an executor. -static inline const SwiftExecutorWitnessTable * +static inline const SwiftExecutorWitnessTable * _Nullable swift_executor_getWitnessTable(SwiftExecutorRef executor) { const uintptr_t mask = ~(uintptr_t)(sizeof(void *) - 1); return (const SwiftExecutorWitnessTable *)(executor.implementation & mask); @@ -230,8 +235,10 @@ swift_executor_invokeSwiftIsIsolatingCurrentContext(SwiftExecutorRef executor) { } /// Execute the specified job while running on the specified executor. -static inline void swift_job_run(SwiftJob *job, SwiftExecutorRef executor) { - extern void _swift_job_run_c(SwiftJob *job, SwiftExecutorRef executor); +static inline void swift_job_run(SwiftJob * _Nonnull job, + SwiftExecutorRef executor) { + extern void _swift_job_run_c(SwiftJob * _Nonnull job, + SwiftExecutorRef executor); _swift_job_run_c(job, executor); } @@ -261,12 +268,12 @@ extern SwiftTime swift_time_getResolution(SwiftClockId clock); // -- Functions that executors must implement ---------------------------------- /// Enqueue a job on the global executor. -SWIFT_CC(swift) void swift_task_enqueueGlobalImpl(SwiftJob *job); +SWIFT_CC(swift) void swift_task_enqueueGlobalImpl(SwiftJob * _Nonnull job); /// Enqueue a job on the global executor, with a specific delay before it /// should execute. SWIFT_CC(swift) void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay, - SwiftJob *job); + SwiftJob * _Nonnull job); /// Enqueue a job on the global executor, with a specific deadline before /// which it must execute. @@ -275,11 +282,11 @@ SWIFT_CC(swift) void swift_task_enqueueGlobalWithDeadlineImpl(long long sec, long long tsec, long long tnsec, int clock, - SwiftJob *job); + SwiftJob * _Nonnull job); /// Enqueue a job on the main executor (which may or may not be the same as /// the global executor). -SWIFT_CC(swift) void swift_task_enqueueMainExecutorImpl(SwiftJob *job); +SWIFT_CC(swift) void swift_task_enqueueMainExecutorImpl(SwiftJob * _Nonnull job); /// Assert that the specified executor is the current executor. SWIFT_CC(swift) void swift_task_checkIsolatedImpl(SwiftExecutorRef executor); @@ -303,8 +310,8 @@ SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_CC(swift) void /// but you should assert or provide a dummy implementation if your executor /// does not support it. SWIFT_CC(swift) void - swift_task_donateThreadToGlobalExecutorUntilImpl(bool (*condition)(void *), - void *conditionContext); + swift_task_donateThreadToGlobalExecutorUntilImpl(bool (* _Nonnull condition)(void * _Nullable), + void * _Nullable conditionContext); #ifdef __cplusplus } // extern "C" diff --git a/stdlib/public/Concurrency/ExecutorImpl.swift b/stdlib/public/Concurrency/ExecutorImpl.swift new file mode 100644 index 0000000000000..d50aaa60e1d93 --- /dev/null +++ b/stdlib/public/Concurrency/ExecutorImpl.swift @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Implementations of the *Impl functions that bridge to Swift. +// +// These are separate from the functions in ExecutorBridge.swift so that we +// can omit this file from the Embedded Swift version of Concurrency for now. +// (This means that the existing co-operative executor will still work.) +// +//===----------------------------------------------------------------------===// + +import Swift + +#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_asyncMainDrainQueueImpl") +internal func drainMainQueue() { + try! MainActor.executor.run() + _exit(result: 0) +} +#endif + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_donateThreadToGlobalExecutorUntilImpl") +internal func dontateToGlobalExecutor( + condition: @convention(c) (_ ctx: UnsafeMutableRawPointer) -> CBool, + context: UnsafeMutableRawPointer +) { + if let runnableExecutor = Task.defaultExecutor as? RunLoopExecutor { + try! runnableExecutor.runUntil { unsafe Bool(condition(context)) } + } else { + fatalError("Global executor does not support thread donation") + } +} + +#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_getMainExecutorImpl") +internal func getMainExecutor() -> UnownedSerialExecutor { + return unsafe UnownedSerialExecutor(MainActor.executor) +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_enqueueMainExecutorImpl") +internal func enqueueOnMainExecutor(job unownedJob: UnownedJob) { + MainActor.executor.enqueue(unownedJob) +} +#endif + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_enqueueGlobalImpl") +internal func enqueueOnGlobalExecutor(job unownedJob: UnownedJob) { + Task.defaultExecutor.enqueue(unownedJob) +} + +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_enqueueGlobalWithDelayImpl") +internal func enqueueOnGlobalExecutor(delay: CUnsignedLongLong, + job unownedJob: UnownedJob) { + Task.defaultExecutor.asSchedulable!.enqueue(ExecutorJob(unownedJob), + after: .nanoseconds(delay), + clock: .continuous) +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_enqueueGlobalWithDeadlineImpl") +internal func enqueueOnGlobalExecutor(seconds: CLongLong, + nanoseconds: CLongLong, + leewaySeconds: CLongLong, + leewayNanoseconds: CLongLong, + clock: CInt, + job unownedJob: UnownedJob) { + let delay = Duration.seconds(seconds) + Duration.nanoseconds(nanoseconds) + let leeway = Duration.seconds(leewaySeconds) + Duration.nanoseconds(leewayNanoseconds) + switch clock { + case _ClockID.suspending.rawValue: + Task.defaultExecutor.asSchedulable!.enqueue(ExecutorJob(unownedJob), + after: delay, + tolerance: leeway, + clock: .suspending) + case _ClockID.continuous.rawValue: + Task.defaultExecutor.asSchedulable!.enqueue(ExecutorJob(unownedJob), + after: delay, + tolerance: leeway, + clock: .continuous) + default: + fatalError("Unknown clock ID \(clock)") + } +} +#endif diff --git a/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift b/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift index 2588cbde2aa94..7c876a99472d1 100644 --- a/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift +++ b/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift @@ -31,13 +31,16 @@ import Swift /// /// Customizing the global concurrent executor is currently not supported. @available(SwiftStdlib 6.0, *) +@available(*, deprecated, renamed: "Task.defaultExecutor") @_unavailableInEmbedded public var globalConcurrentExecutor: any TaskExecutor { get { - _DefaultGlobalConcurrentExecutor.shared + if #available(SwiftStdlib 6.2, *) { + return Task.defaultExecutor + } else { + return _DefaultGlobalConcurrentExecutor.shared + } } - // TODO: introduce a set {} once we are ready to allow customizing the - // default global executor. This should be done the same for main actor } /// A task executor which enqueues all work on the default global concurrent @@ -51,7 +54,7 @@ internal final class _DefaultGlobalConcurrentExecutor: TaskExecutor { private init() {} public func enqueue(_ job: consuming ExecutorJob) { - _enqueueJobGlobal(job.context) + _enqueueJobGlobal(UnownedJob(job)._context) } public func asUnownedTaskExecutor() -> UnownedTaskExecutor { @@ -59,7 +62,7 @@ internal final class _DefaultGlobalConcurrentExecutor: TaskExecutor { // We represent it as the `(0, 0)` ExecutorRef and it is handled properly // by the runtime, without having to call through to the // `_DefaultGlobalConcurrentExecutor` declared in Swift. - UnownedTaskExecutor(_getUndefinedTaskExecutor()) + unsafe UnownedTaskExecutor(_getUndefinedTaskExecutor()) } } diff --git a/stdlib/public/Concurrency/MainActor.swift b/stdlib/public/Concurrency/MainActor.swift index 232de7bc17fb7..cddb4f83162b6 100644 --- a/stdlib/public/Concurrency/MainActor.swift +++ b/stdlib/public/Concurrency/MainActor.swift @@ -22,12 +22,12 @@ import Swift @inlinable public nonisolated var unownedExecutor: UnownedSerialExecutor { - return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) + return unsafe UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) } @inlinable public static var sharedUnownedExecutor: UnownedSerialExecutor { - return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) + return unsafe UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) } @inlinable @@ -137,7 +137,11 @@ extension MainActor { let executor: Builtin.Executor = unsafe Self.shared.unownedExecutor.executor guard _taskIsCurrentExecutor(executor) else { // TODO: offer information which executor we actually got + #if !$Embedded fatalError("Incorrect actor executor assumption; Expected same executor as \(self).", file: file, line: line) + #else + Builtin.int_trap() + #endif } // To do the unsafe cast, we have to pretend it's @escaping. @@ -158,6 +162,6 @@ extension MainActor { try assumeIsolated(operation, file: file, line: line) } } -#endif +#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY -#endif +#endif // !$Embedded diff --git a/stdlib/public/Concurrency/NonDispatchGlobalExecutor.cpp b/stdlib/public/Concurrency/NonDispatchGlobalExecutor.cpp deleted file mode 100644 index d2f4439b67cae..0000000000000 --- a/stdlib/public/Concurrency/NonDispatchGlobalExecutor.cpp +++ /dev/null @@ -1,108 +0,0 @@ -///===--- NonDispatchGlobalExecutor.inc ---------------------*- C++ -*--===/// -/// -/// This source file is part of the Swift.org open source project -/// -/// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors -/// Licensed under Apache License v2.0 with Runtime Library Exception -/// -/// See https:///swift.org/LICENSE.txt for license information -/// See https:///swift.org/CONTRIBUTORS.txt for the list of Swift project authors -/// -///===------------------------------------------------------------------===/// -/// -/// The implementation of the global executor when not using Dispatch but -/// also not using the cooperative global executor. The general assumption -/// is that clients will be installing the appropriate hooks when all of -/// the functions here are called. -/// -/// This file is included into GlobalExecutor.cpp only when both -/// Dispatch integration and the cooperative global executor are disabled. -/// It is expected to define the following functions: -/// swift_task_asyncMainDrainQueueImpl -/// swift_task_checkIsolatedImpl -/// swift_task_donateThreadToGlobalExecutorUntilImpl -/// swift_task_enqueueGlobalImpl -/// swift_task_enqueueGlobalWithDeadlineImpl -/// swift_task_enqueueGlobalWithDelayImpl -/// swift_task_enqueueMainExecutorImpl -/// swift_task_getMainExecutorImpl -/// swift_task_isMainExecutorImpl -/// -///===------------------------------------------------------------------===/// - -#include "swift/Runtime/Debug.h" - -#include "ExecutorImpl.h" - -using namespace swift; - -SWIFT_CC(swift) -void swift_task_enqueueGlobalImpl(SwiftJob *job) { - assert(job && "no job provided"); - - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_enqueueGlobal"); -} - -SWIFT_CC(swift) -void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay, - SwiftJob *job) { - assert(job && "no job provided"); - - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_enqueueGlobalWithDelay"); -} - -SWIFT_CC(swift) -void swift_task_enqueueGlobalWithDeadlineImpl(long long sec, - long long nsec, - long long tsec, - long long tnsec, - int clock, SwiftJob *job) { - assert(job && "no job provided"); - - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_enqueueGlobalWithDeadline"); -} - -/// Enqueues a task on the main executor. -SWIFT_CC(swift) -void swift_task_enqueueMainExecutorImpl(SwiftJob *job) { - assert(job && "no job provided"); - - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_enqueueMainExecutor"); -} - -SWIFT_CC(swift) -void swift_task_checkIsolatedImpl(SwiftExecutorRef executor) { - swift_executor_invokeSwiftCheckIsolated(executor); -} - -SWIFT_CC(swift) -bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) { - return swift_executor_invokeSwiftIsIsolatingCurrentContext(executor); -} - -SWIFT_CC(swift) -SwiftExecutorRef swift_task_getMainExecutorImpl() { - return swift_executor_generic(); -} - -SWIFT_CC(swift) -bool swift_task_isMainExecutorImpl(SwiftExecutorRef executor) { - return swift_executor_isGeneric(executor); -} - -SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_CC(swift) -void swift_task_asyncMainDrainQueueImpl() { - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_asyncMainDrainQueue"); -} - -SWIFT_CC(swift) void -swift_task_donateThreadToGlobalExecutorUntilImpl(bool (*condition)(void *), - void *conditionContext) { - swift_reportError(0, "operation unsupported: " - "swift_task_donateThreadToGlobalExecutorUntilImpl"); -} diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift index 977ebe82f63ef..026657d59187f 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2020 - 2021 Apple Inc. and the Swift project authors +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -12,7 +12,19 @@ import Swift -// TODO(swift): rename the file to ExecutorJob.swift eventually, we don't use PartialTask terminology anymore +// N.B. It would be nice to rename this file to ExecutorJob.swift, but when +// trying that we hit an error from the API digester claiming that +// +// +Var UnownedJob.context has mangled name changing from +// 'Swift.UnownedJob.(context in #UNSTABLE ID#) : Builtin.Job' to +// 'Swift.UnownedJob.(context in #UNSTABLE ID#) : Builtin.Job' +// +// This is odd because `context` is private, so it isn't really part of +// the module API. + +// TODO: It would be better if some of the functions here could be inlined into +// the Swift code. In particular, some of the ones that deal with data held on +// ExecutorJob. @available(SwiftStdlib 5.1, *) @_silgen_name("swift_job_run") @@ -75,7 +87,14 @@ public struct UnownedJob: Sendable { /// The priority of this job. @available(SwiftStdlib 5.9, *) public var priority: JobPriority { - let raw = _swift_concurrency_jobPriority(self) + let raw: UInt8 + if #available(SwiftStdlib 6.2, *) { + raw = _jobGetPriority(context) + } else { + // Since we're building the new version of the stdlib, we should + // never get here. + Builtin.unreachable() + } return JobPriority(rawValue: raw) } @@ -130,7 +149,7 @@ public struct UnownedJob: Sendable { @_alwaysEmitIntoClient @inlinable public func runSynchronously(on executor: UnownedTaskExecutor) { - _swiftJobRunOnTaskExecutor(self, executor) + unsafe _swiftJobRunOnTaskExecutor(self, executor) } /// Run this job isolated to the passed in serial executor, while executing it on the specified task executor. @@ -209,7 +228,14 @@ public struct Job: Sendable, ~Copyable { } public var priority: JobPriority { - let raw = _swift_concurrency_jobPriority(UnownedJob(context: self.context)) + let raw: UInt8 + if #available(SwiftStdlib 6.2, *) { + raw = _jobGetPriority(self.context) + } else { + // We are building the new version of the code, so we should never + // get here. + Builtin.unreachable() + } return JobPriority(rawValue: raw) } @@ -277,10 +303,60 @@ public struct ExecutorJob: Sendable, ~Copyable { } public var priority: JobPriority { - let raw = _swift_concurrency_jobPriority(UnownedJob(context: self.context)) + let raw: UInt8 + if #available(SwiftStdlib 6.2, *) { + raw = _jobGetPriority(self.context) + } else { + // We are building the new version of the code, so we should never + // get here. + Builtin.unreachable() + } return JobPriority(rawValue: raw) } + /// Execute a closure, passing it the bounds of the executor private data + /// for the job. + /// + /// Parameters: + /// + /// - body: The closure to execute. + /// + /// Returns the result of executing the closure. + @available(SwiftStdlib 6.2, *) + public func withUnsafeExecutorPrivateData(body: (UnsafeMutableRawBufferPointer) throws(E) -> R) throws(E) -> R { + let base = unsafe _jobGetExecutorPrivateData(self.context) + let size = unsafe 2 * MemoryLayout.stride + return unsafe try body(UnsafeMutableRawBufferPointer(start: base, + count: size)) + } + + /// Kinds of schedulable jobs + @available(SwiftStdlib 6.2, *) + @frozen + public struct Kind: Sendable, RawRepresentable { + public typealias RawValue = UInt8 + + /// The raw job kind value. + public var rawValue: RawValue + + /// Creates a new instance with the specified raw value. + public init?(rawValue: Self.RawValue) { + self.rawValue = rawValue + } + + /// A task. + public static let task = Kind(rawValue: RawValue(0))! + + // Job kinds >= 192 are private to the implementation. + public static let firstReserved = Kind(rawValue: RawValue(192))! + } + + /// What kind of job this is. + @available(SwiftStdlib 6.2, *) + public var kind: Kind { + return Kind(rawValue: _jobGetKind(self.context))! + } + // TODO: move only types cannot conform to protocols, so we can't conform to CustomStringConvertible; // we can still offer a description to be called explicitly though. @_unavailableInEmbedded @@ -343,7 +419,7 @@ extension ExecutorJob { @_alwaysEmitIntoClient @inlinable __consuming public func runSynchronously(on executor: UnownedTaskExecutor) { - _swiftJobRunOnTaskExecutor(UnownedJob(self), executor) + unsafe _swiftJobRunOnTaskExecutor(UnownedJob(self), executor) } /// Run this job isolated to the passed in serial executor, while executing it on the specified task executor. @@ -370,6 +446,82 @@ extension ExecutorJob { unsafe _swiftJobRunOnTaskExecutor(UnownedJob(self), serialExecutor, taskExecutor) } } + +// Stack-disciplined job-local allocator support +@available(SwiftStdlib 6.2, *) +extension ExecutorJob { + + /// Obtain a stack-disciplined job-local allocator. + /// + /// If the job does not support allocation, this property will be `nil`. + public var allocator: LocalAllocator? { + guard self.kind == .task else { + return nil + } + + return LocalAllocator(context: self.context) + } + + /// A job-local stack-disciplined allocator. + /// + /// This can be used to allocate additional data required by an + /// executor implementation; memory allocated in this manner will + /// be released automatically when the job is disposed of by the + /// runtime. + /// + /// N.B. Because this allocator is stack disciplined, explicitly + /// deallocating memory will also deallocate all memory allocated + /// after the block being deallocated. + public struct LocalAllocator { + internal var context: Builtin.Job + + /// Allocate a specified number of bytes of uninitialized memory. + public func allocate(capacity: Int) -> UnsafeMutableRawBufferPointer { + let base = unsafe _jobAllocate(context, capacity) + return unsafe UnsafeMutableRawBufferPointer(start: base, count: capacity) + } + + /// Allocate uninitialized memory for a single instance of type `T`. + public func allocate(as type: T.Type) -> UnsafeMutablePointer { + let base = unsafe _jobAllocate(context, MemoryLayout.size) + return unsafe base.bindMemory(to: type, capacity: 1) + } + + /// Allocate uninitialized memory for the specified number of + /// instances of type `T`. + public func allocate(capacity: Int, as type: T.Type) + -> UnsafeMutableBufferPointer { + let base = unsafe _jobAllocate(context, MemoryLayout.stride * capacity) + let typedBase = unsafe base.bindMemory(to: T.self, capacity: capacity) + return unsafe UnsafeMutableBufferPointer(start: typedBase, count: capacity) + } + + /// Deallocate previously allocated memory. Note that the task + /// allocator is stack disciplined, so if you deallocate a block of + /// memory, all memory allocated after that block is also deallocated. + public func deallocate(_ buffer: UnsafeMutableRawBufferPointer) { + unsafe _jobDeallocate(context, buffer.baseAddress!) + } + + /// Deallocate previously allocated memory. Note that the task + /// allocator is stack disciplined, so if you deallocate a block of + /// memory, all memory allocated after that block is also deallocated. + public func deallocate(_ pointer: UnsafeMutablePointer) { + unsafe _jobDeallocate(context, UnsafeMutableRawPointer(pointer)) + } + + /// Deallocate previously allocated memory. Note that the task + /// allocator is stack disciplined, so if you deallocate a block of + /// memory, all memory allocated after that block is also deallocated. + public func deallocate(_ buffer: UnsafeMutableBufferPointer) { + unsafe _jobDeallocate(context, UnsafeMutableRawPointer(buffer.baseAddress!)) + } + + } + +} + + #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY // ==== ----------------------------------------------------------------------- @@ -764,10 +916,3 @@ public func _unsafeInheritExecutor_withUnsafeThrowingContinuation( public func _abiEnableAwaitContinuation() { fatalError("never use this function") } - -// ==== ----------------------------------------------------------------------- -// MARK: Runtime functions - -@available(SwiftStdlib 5.9, *) -@_silgen_name("swift_concurrency_jobPriority") -internal func _swift_concurrency_jobPriority(_ job: UnownedJob) -> UInt8 diff --git a/stdlib/public/Concurrency/PlatformExecutorCooperative.swift b/stdlib/public/Concurrency/PlatformExecutorCooperative.swift new file mode 100644 index 0000000000000..5c17e1d2ba084 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorCooperative.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +// This platform uses a single, global, CooperativeExecutor +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + static let executor = CooperativeExecutor() + public static var mainExecutor: any MainExecutor { executor } + public static var defaultExecutor: any TaskExecutor { executor } +} diff --git a/stdlib/public/Concurrency/PlatformExecutorDarwin.swift b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift new file mode 100644 index 0000000000000..90017beb45106 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)) + +import Swift + +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + public static var mainExecutor: any MainExecutor { + if CoreFoundation.isPresent { + return CFMainExecutor() + } else { + return DispatchMainExecutor() + } + } + + public static var defaultExecutor: any TaskExecutor { + if CoreFoundation.isPresent { + return CFTaskExecutor() + } else { + return DispatchGlobalTaskExecutor() + } + } +} + +#endif // os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) diff --git a/stdlib/public/Concurrency/PlatformExecutorLinux.swift b/stdlib/public/Concurrency/PlatformExecutorLinux.swift new file mode 100644 index 0000000000000..3ceb1d1c413e1 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorLinux.swift @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && os(Linux) + +import Swift + +// The default executors for now are Dispatch-based +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + public static let mainExecutor: any MainExecutor = DispatchMainExecutor() + public static let defaultExecutor: any TaskExecutor + = DispatchGlobalTaskExecutor() +} + +#endif // os(Linux) diff --git a/stdlib/public/Concurrency/PlatformExecutorNone.swift b/stdlib/public/Concurrency/PlatformExecutorNone.swift new file mode 100644 index 0000000000000..a524f07005d85 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorNone.swift @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + public static let mainExecutor: any MainExecutor = DummyMainExecutor() + public static let defaultExecutor: any TaskExecutor = DummyTaskExecutor() +} diff --git a/stdlib/public/Concurrency/PlatformExecutorWindows.swift b/stdlib/public/Concurrency/PlatformExecutorWindows.swift new file mode 100644 index 0000000000000..e64665a94da37 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorWindows.swift @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && os(Windows) + +import Swift + +// The default executors for now are Dispatch-based +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + public static let mainExecutor: any MainExecutor = DispatchMainExecutor() + public static let defaultExecutor: any TaskExecutor = + DispatchGlobalTaskExecutor() +} + +#endif // os(Windows) diff --git a/stdlib/public/Concurrency/PriorityQueue.swift b/stdlib/public/Concurrency/PriorityQueue.swift new file mode 100644 index 0000000000000..744c9c4c257a2 --- /dev/null +++ b/stdlib/public/Concurrency/PriorityQueue.swift @@ -0,0 +1,198 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +/// A generic priority queue, with a user-defined comparison function. +struct PriorityQueue { + /// The backing store. + var storage: [T] = [] + + /// The comparison function; this should compare the priorities of + /// two items in the queue, returning `true` if they satisfy the + /// desired condition. + /// + /// A `>` test will produce a max-queue, while a `<` test will + /// result in a min-queue. + var compare: (borrowing T, borrowing T) -> Bool + + /// Initialise the queue. + /// + /// The queue is set up such that if the comparison function in use + /// is `>`, it will be a max-queue, while if the comparison function + /// is `<`, it will be a min-queue. + /// + /// Parameters: + /// + /// - compare: A closure that takes two arguments of type T, and + /// returns true if some condition holds. + /// + init(compare: @escaping (borrowing T, borrowing T) -> Bool) { + self.compare = compare + } + + /// Push an item onto the queue. + /// + /// Parameters: + /// + /// - _ value: The item to push onto the queue. + /// + mutating func push(_ value: T) { + storage.append(value) + upHeap(ndx: storage.count - 1) + } + + /// The highest priority item from the queue, or `nil` if none. + var top: T? { + if storage.isEmpty { + return nil + } + return storage[0] + } + + /// Conditionally pop the highest priority item from the queue. + /// + /// If the comparison function is `>`, this will return the largest + /// item in the queue. If the comparison function is `<`, it will + /// return the smallest. + /// + /// Parameters: + /// + /// - when: A closure that allows code to test the top item before + /// popping. + /// + /// Returns: The next item in the queue, following the comparison + /// rule. + /// + mutating func pop(when condition: (borrowing T) -> Bool) -> T? { + if storage.isEmpty { + return nil + } + if !condition(storage[0]) { + return nil + } + storage.swapAt(0, storage.count - 1) + let result = storage.removeLast() + if !storage.isEmpty { + downHeap(ndx: 0) + } + return result + } + + /// Pop the highest priority item from the queue. + /// + /// If the comparison function is `>`, this will return the largest + /// item in the queue. If the comparison function is `<`, it will + /// return the smallest. + /// + /// Returns: The next item in the queue, following the comparison + /// rule. + /// + mutating func pop() -> T? { + if storage.isEmpty { + return nil + } + storage.swapAt(0, storage.count - 1) + let result = storage.removeLast() + if !storage.isEmpty { + downHeap(ndx: 0) + } + return result + } + + /// Fix the heap condition by iterating upwards. + /// + /// Iterate upwards from the specified entry, exchanging it with + /// its parent if the parent and child do not satisfy the comparison + /// function. + /// + /// This is used when pushing items onto the queue. + /// + /// Parameters: + /// + /// - ndx: The index at which to start. + /// + private mutating func upHeap(ndx: Int) { + var theNdx = ndx + while theNdx > 0 { + let parentNdx = theNdx / 2 + + if !compare(storage[theNdx], storage[parentNdx]) { + break + } + + storage.swapAt(theNdx, parentNdx) + theNdx = parentNdx + } + } + + /// Fix the heap condition by iterating downwards. + /// + /// Iterate downwards from the specified entry, checking that its + /// children satisfy the comparison function. If they do, stop, + /// otherwise exchange the parent entry with the highest priority + /// child. + /// + /// This is used when popping items from the queue. + /// + /// Parameters: + /// + /// - ndx: The index at which to start. + /// + private mutating func downHeap(ndx: Int) { + var theNdx = ndx + while true { + let leftNdx = 2 * theNdx + + if leftNdx >= storage.count { + break + } + + let rightNdx = 2 * theNdx + 1 + var largestNdx = theNdx + + if compare(storage[leftNdx], storage[largestNdx]) { + largestNdx = leftNdx + } + + if rightNdx < storage.count + && compare(storage[rightNdx], storage[largestNdx]) { + largestNdx = rightNdx + } + + if largestNdx == theNdx { + break + } + + storage.swapAt(theNdx, largestNdx) + theNdx = largestNdx + } + } +} + +extension PriorityQueue where T: Comparable { + /// Initialise the priority queue. + /// + /// `Comparable` types have a default implementation, which passes the + /// `>` operator as the comparison function, thus providing a max-queue. + /// + /// If you are using a `Comparable` type and require a min-queue, you + /// can make one with + /// + /// ```swift + /// let minQueue = PriorityQueue(compare: <) + /// ``` + /// + init() { + self.init(compare: >) + } +} diff --git a/stdlib/public/Concurrency/SuspendingClock.swift b/stdlib/public/Concurrency/SuspendingClock.swift index c31e043387469..cec63ed99f55e 100644 --- a/stdlib/public/Concurrency/SuspendingClock.swift +++ b/stdlib/public/Concurrency/SuspendingClock.swift @@ -11,20 +11,19 @@ //===----------------------------------------------------------------------===// import Swift -#if !$Embedded - -/// A clock that measures time that always increments but stops incrementing -/// while the system is asleep. +/// A clock that measures time that always increments but stops incrementing +/// while the system is asleep. /// -/// `SuspendingClock` can be considered as a system awake time clock. The frame -/// of reference of the `Instant` may be bound machine boot or some other +/// `SuspendingClock` can be considered as a system awake time clock. The frame +/// of reference of the `Instant` may be bound machine boot or some other /// locally defined reference point. This means that the instants are /// only comparable on the same machine in the same booted session. /// /// This clock is suitable for high resolution measurements of execution. @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded public struct SuspendingClock: Sendable { - public struct Instant: Codable, Sendable { + public struct Instant: Sendable { internal var _value: Swift.Duration internal init(_value: Swift.Duration) { @@ -35,10 +34,17 @@ public struct SuspendingClock: Sendable { public init() { } } +#if !$Embedded +@available(SwiftStdlib 5.7, *) +extension SuspendingClock.Instant: Codable { +} +#endif + @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension Clock where Self == SuspendingClock { - /// A clock that measures time that always increments but stops incrementing - /// while the system is asleep. + /// A clock that measures time that always increments but stops incrementing + /// while the system is asleep. /// /// try await Task.sleep(until: .now + .seconds(3), clock: .suspending) /// @@ -47,6 +53,7 @@ extension Clock where Self == SuspendingClock { } @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension SuspendingClock: Clock { /// The current instant accounting for machine suspension. @available(SwiftStdlib 5.7, *) @@ -80,13 +87,19 @@ extension SuspendingClock: Clock { return Duration(_seconds: seconds, nanoseconds: nanoseconds) } + /// The suspending clock is monotonic + @available(SwiftStdlib 6.2, *) + public var traits: ClockTraits { + return [.monotonic] + } + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY /// Suspend task execution until a given deadline within a tolerance. /// If no tolerance is specified then the system may adjust the deadline /// to coalesce CPU wake-ups to more efficiently process the wake-ups in /// a more power efficient manner. /// - /// If the task is canceled before the time ends, this function throws + /// If the task is canceled before the time ends, this function throws /// `CancellationError`. /// /// This function doesn't block the underlying thread. @@ -94,11 +107,14 @@ extension SuspendingClock: Clock { public func sleep( until deadline: Instant, tolerance: Swift.Duration? = nil ) async throws { - let (seconds, attoseconds) = deadline._value.components - let nanoseconds = attoseconds / 1_000_000_000 - try await Task._sleep(until:seconds, nanoseconds, - tolerance: tolerance, - clock: .suspending) + if #available(SwiftStdlib 6.2, *) { + try await Task._sleep(until: deadline, + tolerance: tolerance, + clock: self) + } else { + // Should never see this + Builtin.unreachable() + } } #else @available(SwiftStdlib 5.7, *) @@ -112,6 +128,7 @@ extension SuspendingClock: Clock { } @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension SuspendingClock.Instant: InstantProtocol { @available(SwiftStdlib 5.7, *) public static var now: SuspendingClock.Instant { SuspendingClock().now } @@ -180,5 +197,3 @@ extension SuspendingClock.Instant: InstantProtocol { rhs.duration(to: lhs) } } - -#endif diff --git a/stdlib/public/Concurrency/Task+TaskExecutor.swift b/stdlib/public/Concurrency/Task+TaskExecutor.swift index be1919da9f540..611fe9265630f 100644 --- a/stdlib/public/Concurrency/Task+TaskExecutor.swift +++ b/stdlib/public/Concurrency/Task+TaskExecutor.swift @@ -146,7 +146,7 @@ public func withTaskExecutorPreference( } let taskExecutorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor + unsafe taskExecutor.asUnownedTaskExecutor().executor let record = unsafe _pushTaskExecutorPreference(taskExecutorBuiltin) defer { @@ -177,7 +177,7 @@ public func _unsafeInheritExecutor_withTaskExecutorPreference( } let taskExecutorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor + unsafe taskExecutor.asUnownedTaskExecutor().executor let record = unsafe _pushTaskExecutorPreference(taskExecutorBuiltin) defer { @@ -457,8 +457,8 @@ extension UnsafeCurrentTask { /// means to guarantee the executor remains alive while it is in use. @available(SwiftStdlib 6.0, *) public var unownedTaskExecutor: UnownedTaskExecutor? { - let ref = _getPreferredTaskExecutor() - return UnownedTaskExecutor(ref) + let ref = _getPreferredUnownedTaskExecutor() + return unsafe UnownedTaskExecutor(ref) } } @@ -466,7 +466,7 @@ extension UnsafeCurrentTask { @available(SwiftStdlib 6.0, *) @_silgen_name("swift_task_getPreferredTaskExecutor") -internal func _getPreferredTaskExecutor() -> Builtin.Executor +internal func _getPreferredUnownedTaskExecutor() -> Builtin.Executor typealias TaskExecutorPreferenceStatusRecord = UnsafeRawPointer diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index 43a17e08a2269..3771b01bff4a9 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -377,11 +377,17 @@ static SerialExecutorRef executorForEnqueuedJob(Job *job) { return SerialExecutorRef::generic(); } - if (auto identity = reinterpret_cast(jobQueue)) { - return SerialExecutorRef::forOrdinary( - identity, _swift_task_getDispatchQueueSerialExecutorWitnessTable()); + if (jobQueue == (void *)dispatch_get_main_queue()) { + return swift_task_getMainExecutor(); } + // It is no longer valid to return a Dispatch Queue as the "identity" here. + // + //if (auto identity = reinterpret_cast(jobQueue)) { + // return SerialExecutorRef::forOrdinary( + // identity, _swift_task_getDispatchQueueSerialExecutorWitnessTable()); + //} + return SerialExecutorRef::generic(); #endif } diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 27a6f156eaafd..f1f7c7e742cb0 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -227,7 +227,7 @@ extension Task { /// recommended that callers release any locks they might be /// holding before they call cancel. /// - /// If the task has already run past the last point where it could have + /// If the task has already run past the last point where it could have /// performed a cancellation check, cancelling it may have no observable effects. /// /// - SeeAlso: `Task.checkCancellation()` @@ -1253,7 +1253,18 @@ extension Task where Success == Never, Failure == Never { let job = _taskCreateNullaryContinuationJob( priority: Int(Task.currentPriority.rawValue), continuation: continuation) + + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + if #available(SwiftStdlib 6.2, *) { + let executor = Task.currentExecutor + + executor.enqueue(ExecutorJob(context: job)) + } else { + _enqueueJobGlobal(job) + } + #else _enqueueJobGlobal(job) + #endif } } } @@ -1414,17 +1425,27 @@ func getJobFlags(_ task: Builtin.NativeObject) -> JobFlags @usableFromInline func _enqueueJobGlobal(_ task: Builtin.Job) +@available(SwiftStdlib 5.9, *) +func _enqueueJobGlobal(_ task: UnownedJob) { + _enqueueJobGlobal(task._context) +} + @available(SwiftStdlib 5.1, *) @_silgen_name("swift_task_enqueueGlobalWithDelay") @usableFromInline func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: Builtin.Job) +@available(SwiftStdlib 5.9, *) +func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: UnownedJob) { + return _enqueueJobGlobalWithDelay(delay, task._context) +} + @available(SwiftStdlib 5.7, *) @_silgen_name("swift_task_enqueueGlobalWithDeadline") @usableFromInline func _enqueueJobGlobalWithDeadline(_ seconds: Int64, _ nanoseconds: Int64, _ toleranceSec: Int64, _ toleranceNSec: Int64, - _ clock: Int32, _ task: Builtin.Job) + _ clock: Int32, _ task: UnownedJob) @usableFromInline @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/TaskAlloc.cpp b/stdlib/public/Concurrency/TaskAlloc.cpp index a9c20e442d416..6e92643805a66 100644 --- a/stdlib/public/Concurrency/TaskAlloc.cpp +++ b/stdlib/public/Concurrency/TaskAlloc.cpp @@ -117,3 +117,17 @@ void swift::swift_coro_dealloc(CoroAllocator *allocator, void *ptr) { } allocator->deallocate(ptr); } + +void *swift::swift_job_allocate(Job *job, size_t size) { + if (!job->isAsyncTask()) + return nullptr; + + return allocator(static_cast(job)).alloc(size); +} + +void swift::swift_job_deallocate(Job *job, void *ptr) { + if (!job->isAsyncTask()) + return; + + allocator(static_cast(job)).dealloc(ptr); +} diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift index d1fa4fa59ed59..653a343c546d8 100644 --- a/stdlib/public/Concurrency/TaskSleep.swift +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -27,6 +27,20 @@ extension Task where Success == Never, Failure == Never { let job = _taskCreateNullaryContinuationJob( priority: Int(Task.currentPriority.rawValue), continuation: continuation) + + if #available(SwiftStdlib 6.2, *) { + #if !$Embedded + if let executor = Task.currentSchedulableExecutor { + executor.enqueue(ExecutorJob(context: job), + after: .nanoseconds(duration), + clock: .continuous) + return + } + #endif + } + + // If there is no current schedulable executor, fall back to + // _enqueueJobGlobalWithDelay() _enqueueJobGlobalWithDelay(duration, job) } } @@ -255,8 +269,23 @@ extension Task where Success == Never, Failure == Never { let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) { unsafe onSleepWake(token) } - _enqueueJobGlobalWithDelay( - duration, Builtin.convertTaskToJob(sleepTask)) + + let job = Builtin.convertTaskToJob(sleepTask) + + if #available(SwiftStdlib 6.2, *) { + #if !$Embedded + if let executor = Task.currentSchedulableExecutor { + executor.enqueue(ExecutorJob(context: job), + after: .nanoseconds(duration), + clock: .continuous) + return + } + #endif + } + + // If there is no current schedulable executor, fall back to + // _enqueueJobGlobalWithDelay() + _enqueueJobGlobalWithDelay(duration, job) return case .activeContinuation, .finished: diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift index 2354c11eb8f43..6f60c4eeb6631 100644 --- a/stdlib/public/Concurrency/TaskSleepDuration.swift +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -13,14 +13,55 @@ import Swift #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +@available(SwiftStdlib 5.7, *) +fileprivate func timestamp(for instant: C.Instant, clock: C) + -> (clockID: _ClockID, seconds: Int64, nanoseconds: Int64) { + var clockID: _ClockID + if #available(SwiftStdlib 6.2, *) { + if clock.traits.contains(.continuous) { + clockID = .continuous + } else { + clockID = .suspending + } + } else { + Builtin.unreachable() + } + + var seconds: Int64 = 0 + var nanoseconds: Int64 = 0 + unsafe _getTime(seconds: &seconds, + nanoseconds: &nanoseconds, + clock: clockID.rawValue) + + let delta: Swift.Duration + if #available(SwiftStdlib 6.2, *) { + delta = clock.convert(from: clock.now.duration(to: instant))! + } else { + Builtin.unreachable() + } + + let (deltaSeconds, deltaAttoseconds) = delta.components + let deltaNanoseconds = deltaAttoseconds / 1_000_000_000 + seconds += deltaSeconds + nanoseconds += deltaNanoseconds + if nanoseconds > 1_000_000_000 { + seconds += 1 + nanoseconds -= 1_000_000_000 + } + + return (clockID: clockID, + seconds: Int64(seconds), + nanoseconds: Int64(nanoseconds)) +} + @available(SwiftStdlib 5.7, *) @_unavailableInEmbedded extension Task where Success == Never, Failure == Never { @available(SwiftStdlib 5.7, *) - internal static func _sleep( - until seconds: Int64, _ nanoseconds: Int64, - tolerance: Duration?, - clock: _ClockID + internal static func _sleep( + until instant: C.Instant, + tolerance: C.Duration?, + clock: C ) async throws { // Create a token which will initially have the value "not started", which // means the continuation has neither been created nor completed. @@ -53,20 +94,50 @@ extension Task where Success == Never, Failure == Never { let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) { unsafe onSleepWake(token) } + + let job = Builtin.convertTaskToJob(sleepTask) + + if #available(SwiftStdlib 6.2, *) { + #if !$Embedded + if let executor = Task.currentSchedulableExecutor { + executor.enqueue(ExecutorJob(context: job), + at: instant, + tolerance: tolerance, + clock: clock) + return + } + #endif + } else { + Builtin.unreachable() + } + + // If there is no current schedulable executor, fall back to + // calling _enqueueJobGlobalWithDeadline(). + let (clockID, seconds, nanoseconds) = timestamp(for: instant, + clock: clock) let toleranceSeconds: Int64 let toleranceNanoseconds: Int64 - if let components = tolerance?.components { - toleranceSeconds = components.seconds - toleranceNanoseconds = components.attoseconds / 1_000_000_000 + if #available(SwiftStdlib 6.2, *) { + if let tolerance = tolerance, + let components = clock.convert(from: tolerance)?.components { + toleranceSeconds = components.seconds + toleranceNanoseconds = components.attoseconds / 1_000_000_000 + } else { + toleranceSeconds = 0 + toleranceNanoseconds = -1 + } } else { - toleranceSeconds = 0 - toleranceNanoseconds = -1 + Builtin.unreachable() } - _enqueueJobGlobalWithDeadline( + if #available(SwiftStdlib 5.9, *) { + _enqueueJobGlobalWithDeadline( seconds, nanoseconds, toleranceSeconds, toleranceNanoseconds, - clock.rawValue, Builtin.convertTaskToJob(sleepTask)) + clockID.rawValue, UnownedJob(context: job)) + } else { + Builtin.unreachable() + } return case .activeContinuation, .finished: @@ -119,7 +190,7 @@ extension Task where Success == Never, Failure == Never { /// Suspends the current task until the given deadline within a tolerance. /// - /// If the task is canceled before the time ends, this function throws + /// If the task is canceled before the time ends, this function throws /// `CancellationError`. /// /// This function doesn't block the underlying thread. @@ -130,14 +201,14 @@ extension Task where Success == Never, Failure == Never { public static func sleep( until deadline: C.Instant, tolerance: C.Instant.Duration? = nil, - clock: C = ContinuousClock() + clock: C = .continuous ) async throws { try await clock.sleep(until: deadline, tolerance: tolerance) } - + /// Suspends the current task for the given duration. /// - /// If the task is cancelled before the time ends, this function throws + /// If the task is cancelled before the time ends, this function throws /// `CancellationError`. /// /// This function doesn't block the underlying thread. @@ -149,7 +220,7 @@ extension Task where Success == Never, Failure == Never { public static func sleep( for duration: C.Instant.Duration, tolerance: C.Instant.Duration? = nil, - clock: C = ContinuousClock() + clock: C = .continuous ) async throws { try await clock.sleep(for: duration, tolerance: tolerance) } @@ -163,7 +234,7 @@ extension Task where Success == Never, Failure == Never { public static func sleep( until deadline: C.Instant, tolerance: C.Instant.Duration? = nil, - clock: C = ContinuousClock() + clock: C = .continuous ) async throws { fatalError("Unavailable in task-to-thread concurrency model") } @@ -173,7 +244,7 @@ extension Task where Success == Never, Failure == Never { public static func sleep( for duration: C.Instant.Duration, tolerance: C.Instant.Duration? = nil, - clock: C = ContinuousClock() + clock: C = .continuous ) async throws { fatalError("Unavailable in task-to-thread concurrency model") } diff --git a/test/Concurrency/Runtime/actor_counters.swift b/test/Concurrency/Runtime/actor_counters.swift index ef36940e663e9..5749f6012db42 100644 --- a/test/Concurrency/Runtime/actor_counters.swift +++ b/test/Concurrency/Runtime/actor_counters.swift @@ -44,7 +44,7 @@ nonisolated var randomPriority: TaskPriority? { @available(SwiftStdlib 5.1, *) func worker(identity: Int, counters: [Counter], numIterations: Int) async { - for i in 0..] = [] + var workers: [Task] = [] for i in 0.. Int { async let second = await asyncFib(n-1) // Sleep a random amount of time waiting on the result producing a result. - await Task.sleep(UInt64.random(in: 0..<100) * 1_000_000) + try! await Task.sleep(nanoseconds: UInt64.random(in: 0..<100) * 1_000_000) let result = await first + second // Sleep a random amount of time before producing a result. - await Task.sleep(UInt64.random(in: 0..<100) * 1_000_000) + try! await Task.sleep(nanoseconds: UInt64.random(in: 0..<100) * 1_000_000) return result } diff --git a/test/Concurrency/Runtime/async_task_executor_nsobject.swift b/test/Concurrency/Runtime/async_task_executor_nsobject.swift index ae8f31b3bb7dd..d652f7d246f01 100644 --- a/test/Concurrency/Runtime/async_task_executor_nsobject.swift +++ b/test/Concurrency/Runtime/async_task_executor_nsobject.swift @@ -22,13 +22,37 @@ import Darwin // This test specifically checks that our reference counting accounts for existence of // objective-c types as TaskExecutors -- which was a bug where we'd swift_release // obj-c excecutors by accident (rdar://131151645). -final class NSQueueTaskExecutor: NSData, TaskExecutor, @unchecked Sendable { +final class NSQueueTaskExecutor: NSData, TaskExecutor, SchedulableExecutor, @unchecked Sendable { public func enqueue(_ _job: consuming ExecutorJob) { let job = UnownedJob(_job) DispatchQueue.main.async { job.runSynchronously(on: self.asUnownedTaskExecutor()) } } + + public func enqueue(_ _job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + // Convert to `Swift.Duration` + let duration = clock.convert(from: delay)! + + // Now turn that into nanoseconds + let (seconds, attoseconds) = duration.components + let nanoseconds = attoseconds / 1_000_000_000 + + // Get a Dispatch time + let deadline = DispatchTime.now().advanced( + by: .seconds(Int(seconds)) + ).advanced( + by: .nanoseconds(Int(nanoseconds)) + ) + + let job = UnownedJob(_job) + DispatchQueue.main.asyncAfter(deadline: deadline) { + job.runSynchronously(on: self.asUnownedTaskExecutor()) + } + } } @main struct Main { diff --git a/test/Concurrency/Runtime/clocks.swift b/test/Concurrency/Runtime/clocks.swift new file mode 100644 index 0000000000000..5e8c188cd7b4e --- /dev/null +++ b/test/Concurrency/Runtime/clocks.swift @@ -0,0 +1,276 @@ +// RUN: %target-run-simple-swift(%import-libdispatch -parse-as-library) + +// REQUIRES: concurrency +// REQUIRES: executable_test + +// rdar://106849189 move-only types should be supported in freestanding mode +// UNSUPPORTED: freestanding + +// UNSUPPORTED: back_deployment_runtime +// REQUIRES: concurrency_runtime + +import StdlibUnittest + +@available(SwiftStdlib 6.2, *) +struct TickingClock: Clock { + struct Duration: DurationProtocol { + var ticks: Int + + static func / (_ lhs: Self, _ rhs: Int) -> Self { + return Duration(ticks: lhs.ticks / rhs) + } + static func * (_ lhs: Self, rhs: Int) -> Self { + return Duration(ticks: lhs.ticks * rhs) + } + static func / (_ lhs: Self, _ rhs: Self) -> Double { + return Double(lhs.ticks) / Double(rhs.ticks) + } + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + return lhs.ticks < rhs.ticks + } + static func + (_ lhs: Self, _ rhs: Self) -> Self { + return Duration(ticks: lhs.ticks + rhs.ticks) + } + static func - (_ lhs: Self, _ rhs: Self) -> Self { + return Duration(ticks: lhs.ticks - rhs.ticks) + } + + static var zero: Self { + return Duration(ticks: 0) + } + } + struct Instant: InstantProtocol { + typealias Duration = TickingClock.Duration + var ticksFromStart: Int + + func advanced(by duration: Duration) -> Self { + return Instant(ticksFromStart: self.ticksFromStart + duration.ticks) + } + func duration(to other: Self) -> Duration { + return Duration(ticks: other.ticksFromStart - self.ticksFromStart) + } + + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + return lhs.ticksFromStart < rhs.ticksFromStart + } + } + + private var _now: Instant + var now: Instant { return _now } + var minimumResolution: Duration { return Duration(ticks: 1) } + var traits: ClockTraits { [.monotonic] } + + init() { + _now = Instant(ticksFromStart: 0) + } + + // These are a bit of a lie, since this clock is weird and doesn't + // actually tell the time; for the purposes of this test, we pretend + // that the ticks are 20ms. + func convert(from duration: Duration) -> Swift.Duration? { + return .seconds(Double(duration.ticks) / 50) + } + + func convert(from duration: Swift.Duration) -> Duration? { + let (seconds, attoseconds) = duration.components + let extraTicks = attoseconds / 20_000_000_000_000_000 + return Duration(ticks: Int(seconds * 50) + Int(extraTicks)) + } + + mutating func tick() { + _now.ticksFromStart += 1 + } + + func sleep( + until instant: Instant, + tolerance: Duration? = nil + ) async throws { + // Do nothing + } +} + +@available(SwiftStdlib 6.2, *) +struct TockingClock: Clock { + struct Duration: DurationProtocol { + var tocks: Int + + static func / (_ lhs: Self, _ rhs: Int) -> Self { + return Duration(tocks: lhs.tocks / rhs) + } + static func * (_ lhs: Self, rhs: Int) -> Self { + return Duration(tocks: lhs.tocks * rhs) + } + static func / (_ lhs: Self, _ rhs: Self) -> Double { + return Double(lhs.tocks) / Double(rhs.tocks) + } + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + return lhs.tocks < rhs.tocks + } + static func + (_ lhs: Self, _ rhs: Self) -> Self { + return Duration(tocks: lhs.tocks + rhs.tocks) + } + static func - (_ lhs: Self, _ rhs: Self) -> Self { + return Duration(tocks: lhs.tocks - rhs.tocks) + } + + static var zero: Self { + return Duration(tocks: 0) + } + } + struct Instant: InstantProtocol { + typealias Duration = TockingClock.Duration + var tocksFromStart: Int + + func advanced(by duration: Duration) -> Self { + return Instant(tocksFromStart: self.tocksFromStart + duration.tocks) + } + func duration(to other: Self) -> Duration { + return Duration(tocks: other.tocksFromStart - self.tocksFromStart) + } + + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + return lhs.tocksFromStart < rhs.tocksFromStart + } + } + + private var _now: Instant + var now: Instant { return _now } + var minimumResolution: Duration { return Duration(tocks: 1) } + var traits: ClockTraits { [.monotonic] } + + init() { + _now = Instant(tocksFromStart: 1000) + } + + // These are a bit of a lie, since this clock is weird and doesn't + // actually tell the time; for the purposes of this test, we pretend + // that the tocks are 20ms. + func convert(from duration: Duration) -> Swift.Duration? { + return .seconds(Double(duration.tocks) / 100) + } + + func convert(from duration: Swift.Duration) -> Duration? { + let (seconds, attoseconds) = duration.components + let extraTocks = attoseconds / 10_000_000_000_000_000 + return Duration(tocks: Int(seconds * 100) + Int(extraTocks)) + } + + mutating func tock() { + _now.tocksFromStart += 1 + } + + func sleep( + until instant: Instant, + tolerance: Duration? = nil + ) async throws { + // Do nothing + } +} + +@available(SwiftStdlib 6.2, *) +@main struct Main { + static func main() { + let tests = TestSuite("clocks") + + var clockA = TickingClock() + let clockB = TickingClock() + + clockA.tick() + clockA.tick() + clockA.tick() + + var clockC = TockingClock() + + clockC.tock() + + tests.test("Convert instants from one clock to another") { + let nowA = clockA.now + let nowB = clockB.now + + expectEqual(nowA.ticksFromStart, 3) + expectEqual(nowB.ticksFromStart, 0) + + let futureA = nowA.advanced(by: TickingClock.Duration(ticks: 23)) + let futureB = nowB.advanced(by: TickingClock.Duration(ticks: 42)) + + expectEqual(futureA.ticksFromStart, 26) + expectEqual(futureB.ticksFromStart, 42) + + let futureAinB = clockB.convert(instant: futureA, from: clockA)! + let futureBinA = clockA.convert(instant: futureB, from: clockB)! + + expectEqual(futureAinB.ticksFromStart, 23) + expectEqual(futureBinA.ticksFromStart, 45) + + let futureAinBinA = clockA.convert(instant: futureAinB, from: clockB)! + let futureBinAinB = clockB.convert(instant: futureBinA, from: clockA)! + + expectEqual(futureAinBinA.ticksFromStart, futureA.ticksFromStart) + expectEqual(futureBinAinB.ticksFromStart, futureB.ticksFromStart) + } + + tests.test("Convert instants between clocks with different representations") { + let nowA = clockA.now + let nowC = clockC.now + + expectEqual(nowA.ticksFromStart, 3) + expectEqual(nowC.tocksFromStart, 1001) + + let futureA = nowA.advanced(by: TickingClock.Duration(ticks: 23)) + let futureC = nowC.advanced(by: TockingClock.Duration(tocks: 42)) + + expectEqual(futureA.ticksFromStart, 26) + expectEqual(futureC.tocksFromStart, 1043) + + let futureAinC = clockC.convert(instant: futureA, from: clockA)! + let futureCinA = clockA.convert(instant: futureC, from: clockC)! + + expectEqual(futureAinC.tocksFromStart, 1047) + expectEqual(futureCinA.ticksFromStart, 24) + + let futureAinCinA = clockA.convert(instant: futureAinC, from: clockC)! + let futureCinAinC = clockC.convert(instant: futureCinA, from: clockA)! + + expectEqual(futureAinCinA.ticksFromStart, futureA.ticksFromStart) + expectEqual(futureCinAinC.tocksFromStart, futureC.tocksFromStart) + } + + tests.test("Convert instants between continuous and suspending clocks") { + let continuous = ContinuousClock() + let suspending = SuspendingClock() + + let nowC = continuous.now + let nowS = suspending.now + + let futureC = nowC.advanced(by: .seconds(5.3)) + let futureS = nowS.advanced(by: .seconds(4.2)) + + let futureCinS = suspending.convert(instant: futureC, + from: .continuous)! + let futureSinC = continuous.convert(instant: futureS, + from: .suspending)! + + let futureCinSinC = continuous.convert(instant: futureCinS, + from: .suspending)! + let futureSinCinS = suspending.convert(instant: futureSinC, + from: .continuous)! + + // These clocks may not be exact, so allow differences of up to 50ms + var delta1 = futureCinSinC - futureC + var delta2 = futureSinCinS - futureS + + // Duration is not SignedNumeric, so we have to do things this way + if delta1 < .zero { + delta1 = .zero - delta1 + } + if delta2 < .zero { + delta2 = .zero - delta2 + } + + expectLT(delta1, .milliseconds(50)) + expectLT(delta2, .milliseconds(50)) + } + + runAllTests() + } +} diff --git a/test/Concurrency/Runtime/custom_main_executor.swift b/test/Concurrency/Runtime/custom_main_executor.swift new file mode 100644 index 0000000000000..bb5c0f5ef1b21 --- /dev/null +++ b/test/Concurrency/Runtime/custom_main_executor.swift @@ -0,0 +1,92 @@ +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library -executor-factory SimpleExecutorFactory) | %FileCheck %s + +// REQUIRES: concurrency +// REQUIRES: executable_test + +// rdar://106849189 move-only types should be supported in freestanding mode +// UNSUPPORTED: freestanding + +// UNSUPPORTED: back_deployment_runtime +// REQUIRES: concurrency_runtime +// REQUIRES: synchronization + +import StdlibUnittest +import Synchronization + +struct SimpleExecutorFactory: ExecutorFactory { + public static var mainExecutor: any MainExecutor { + print("Creating main executor") + return SimpleMainExecutor() + } + public static var defaultExecutor: any TaskExecutor { + print("Creating task executor") + return SimpleTaskExecutor() + } +} + +@available(SwiftStdlib 6.2, *) +final class SimpleMainExecutor: MainExecutor, @unchecked Sendable { + public var isRunning: Bool = false + + var shouldStop: Bool = false + let queue = Mutex<[UnownedJob]>([]) + + func enqueue(_ job: consuming ExecutorJob) { + print("Enqueued job") + let unownedJob = UnownedJob(job) + queue.withLock { + $0.append(unownedJob) + } + } + + func run() throws { + print("Running") + isRunning = true + while !shouldStop { + let jobs = queue.withLock { + let jobs = $0 + $0.removeAll() + return jobs + } + for job in jobs { + print("Running job") + job.runSynchronously(on: self.asUnownedSerialExecutor()) + } + } + isRunning = false + } + + func stop() { + shouldStop = true + } +} + +@available(SwiftStdlib 6.2, *) +final class SimpleTaskExecutor: TaskExecutor, @unchecked Sendable { + func enqueue(_ job: consuming ExecutorJob) { + MainActor.executor.enqueue(job) + } +} + +func myAsyncFunction() async { + print("Hello World") +} + +@available(SwiftStdlib 6.2, *) +@main struct Main { + static func main() async { + print("Hello") + await myAsyncFunction() + print("Goodbye") + } +} + +// CHECK: Creating main executor +// CHECK-NEXT: Creating task executor +// CHECK-NEXT: Hello +// CHECK-NEXT: Enqueued job +// CHECK-NEXT: Running +// CHECK-NEXT: Running job +// CHECK-NEXT: Hello World +// CHECK-NEXT: Goodbye + diff --git a/test/Concurrency/Runtime/mainactor.swift b/test/Concurrency/Runtime/mainactor.swift index 160abae2c0c4b..f2be405fd3152 100644 --- a/test/Concurrency/Runtime/mainactor.swift +++ b/test/Concurrency/Runtime/mainactor.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-parse-as-library -target %target-swift-5.1-abi-triple %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -g -target %target-swift-5.1-abi-triple %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency diff --git a/test/Concurrency/Runtime/sleep_executor.swift b/test/Concurrency/Runtime/sleep_executor.swift new file mode 100644 index 0000000000000..f4767e5a068b3 --- /dev/null +++ b/test/Concurrency/Runtime/sleep_executor.swift @@ -0,0 +1,92 @@ +// RUN: %target-run-simple-swift(%import-libdispatch -parse-as-library) + +// REQUIRES: concurrency +// REQUIRES: executable_test +// REQUIRES: libdispatch + +// rdar://106849189 move-only types should be supported in freestanding mode +// UNSUPPORTED: freestanding + +// UNSUPPORTED: back_deployment_runtime +// REQUIRES: concurrency_runtime + +import Dispatch +import StdlibUnittest + +@available(SwiftStdlib 6.2, *) +actor MyActor { + public func doSleep() async { + try! await Task.sleep(for: .seconds(0.1)) + } +} + +@available(SwiftStdlib 6.2, *) +final class TestExecutor: TaskExecutor, SchedulableExecutor, @unchecked Sendable { + var asSchedulable: SchedulableExecutor? { + return self + } + + public func enqueue(_ _job: consuming ExecutorJob) { + let job = UnownedJob(_job) + DispatchQueue.main.async { + job.runSynchronously(on: self.asUnownedTaskExecutor()) + } + } + + public func enqueue(_ _job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + // Convert to `Swift.Duration` + let duration = clock.convert(from: delay)! + + // Now turn that into nanoseconds + let (seconds, attoseconds) = duration.components + let nanoseconds = attoseconds / 1_000_000_000 + + // Get a Dispatch time + let deadline = DispatchTime.now() + + .seconds(Int(seconds)) + + .nanoseconds(Int(nanoseconds)) + + let job = UnownedJob(_job) + DispatchQueue.main.asyncAfter(deadline: deadline) { + job.runSynchronously(on: self.asUnownedTaskExecutor()) + } + } +} + +@available(SwiftStdlib 6.2, *) +@main struct Main { + static func main() async { + let tests = TestSuite("sleep_executor") + + tests.test("Task.sleep on the main executor") { + try! await Task.sleep(for: .seconds(0.1)) + } + + tests.test("Task.sleep on the global executor") { + let task = Task.detached { + try! await Task.sleep(for: .seconds(0.1)) + } + + await task.value + } + + tests.test("Task.sleep on a custom executor") { + let taskExecutor = TestExecutor() + + let task = Task(executorPreference: taskExecutor) { + try! await Task.sleep(for: .seconds(0.1)) + } + + await task.value + } + + tests.test("Task.sleep on an actor") { + await MyActor().doSleep() + } + + await runAllTestsAsync() + } +} diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index 38037f92cc94b..557c626a2cada 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -23,6 +23,8 @@ _$sScEMa _$sScEMn _$sScEN _$sScEs5ErrorsMc +_$sScF14isMainExecutorSbvgTj +_$sScF14isMainExecutorSbvgTq _$sScF7enqueueyyScJFTj _$sScF7enqueueyyScJFTq _$sScF7enqueueyys11ExecutorJobVnFTj @@ -31,9 +33,15 @@ _$sScF7enqueueyys3JobVnFTj _$sScF7enqueueyys3JobVnFTq _$sScFMp _$sScFTL +_$sScFsE14isMainExecutorSbvg +_$sScFsE14isMainExecutorSbvpMV +_$sScFsE18_isComplexEqualitySbvg +_$sScFsE18_isComplexEqualitySbvpMV _$sScFsE7enqueueyyScJF _$sScFsE7enqueueyys11ExecutorJobVnF _$sScFsE7enqueueyys3JobVnF +_$sScFsSQRzrlE18_isComplexEqualitySbvg +_$sScFsSQRzrlE18_isComplexEqualitySbvpMV _$sScG11isCancelledSbvg _$sScG11isCancelledSbvpMV _$sScG17makeAsyncIteratorScG0C0Vyx_GyF @@ -88,6 +96,8 @@ _$sScM3run10resultType4bodyxxm_xyYbKScMYcXEtYaKlFZ _$sScM3run10resultType4bodyxxm_xyYbKScMYcXEtYaKlFZTu _$sScM6sharedScMvgZ _$sScM7enqueueyyScJF +_$sScM8executors12MainExecutor_pvgZ +_$sScM8executors12MainExecutor_pvpZMV _$sScMMa _$sScMMm _$sScMMn @@ -195,8 +205,16 @@ _$sScTss5NeverORs_rlE5valuexvgTu _$sScTss5NeverORs_rlE5valuexvpMV _$sScTss5NeverORszABRs_rlE11isCancelledSbvgZ _$sScTss5NeverORszABRs_rlE12basePriorityScPSgvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pvpZMV _$sScTss5NeverORszABRs_rlE15currentPriorityScPvgZ +_$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvgZ +_$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvpZMV _$sScTss5NeverORszABRs_rlE17checkCancellationyyKFZ +_$sScTss5NeverORszABRs_rlE17preferredExecutorSch_pSgvgZ +_$sScTss5NeverORszABRs_rlE17preferredExecutorSch_pSgvpZMV +_$sScTss5NeverORszABRs_rlE26currentSchedulableExecutors0cD0_pSgvgZ +_$sScTss5NeverORszABRs_rlE26currentSchedulableExecutors0cD0_pSgvpZMV _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZ _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZTu _$sScTss5NeverORszABRs_rlE5sleep5until9tolerance5clocky7InstantQyd___8DurationQyd__Sgqd__tYaKs5ClockRd__lFZ @@ -214,6 +232,7 @@ _$sScc7contextBcvs _$sSccMa _$sSccMn _$sSce15complexEqualityScexh_tcScfRzlufC +_$sSce16asSerialExecutorScf_pSgyF _$sSce18_isComplexEqualitySbvg _$sSce18_isComplexEqualitySbvpMV _$sSce8executorBevM @@ -227,6 +246,7 @@ _$sSceMa _$sSceMn _$sSceN _$sSceySceBecfC +_$sSceyScexhcScfRzlufC _$sScf23asUnownedSerialExecutorSceyFTj _$sScf23asUnownedSerialExecutorSceyFTq _$sScf31isSameExclusiveExecutionContext5otherSbx_tFTj @@ -241,9 +261,12 @@ _$sScfMp _$sScfScFTb _$sScfTL _$sScfsE14assertIsolated_4file4lineySSyXK_s12StaticStringVSutF +_$sScfsE14isMainExecutorSbvg +_$sScfsE14isMainExecutorSbvpMV _$sScfsE20preconditionIsolated_4file4lineySSyXK_s12StaticStringVSutF _$sScfsE23asUnownedSerialExecutorSceyF _$sScfsE31isSameExclusiveExecutionContext5otherSbx_tF +_$sScfsSQRzrlE31isSameExclusiveExecutionContext5otherSbx_tF _$sScg10nextResults0B0Oyxq_GSgyYaKF _$sScg10nextResults0B0Oyxq_GSgyYaKFTu _$sScg11_waitForAllyyYaKF @@ -370,9 +393,51 @@ _$sSctN _$sSctSHsMc _$sSctSQsMc _$ss039_checkIllegalTaskLocalBindingWithinWithC5Group4file4lineySS_SutF +_$ss11ClockTraitsV10continuousABvgZ +_$ss11ClockTraitsV10continuousABvpZMV +_$ss11ClockTraitsV8rawValueABs6UInt32V_tcfC +_$ss11ClockTraitsV8rawValues6UInt32Vvg +_$ss11ClockTraitsV8rawValues6UInt32VvpMV +_$ss11ClockTraitsV8wallTimeABvgZ +_$ss11ClockTraitsV8wallTimeABvpZMV +_$ss11ClockTraitsV9monotonicABvgZ +_$ss11ClockTraitsV9monotonicABvpZMV +_$ss11ClockTraitsVMa +_$ss11ClockTraitsVMn +_$ss11ClockTraitsVN +_$ss11ClockTraitsVSQsMc +_$ss11ClockTraitsVSYsMc +_$ss11ClockTraitsVs10SetAlgebrasMc +_$ss11ClockTraitsVs25ExpressibleByArrayLiteralsMc +_$ss11ClockTraitsVs9OptionSetsMc +_$ss11ExecutorJobV010withUnsafeA11PrivateData4bodyxxSwq_YKXE_tq_YKs5ErrorR_r0_lF _$ss11ExecutorJobV11descriptionSSvg +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySpyxGlF +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySryxGlF +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySwF +_$ss11ExecutorJobV14LocalAllocatorV8allocate2asSpyxGxm_tlF +_$ss11ExecutorJobV14LocalAllocatorV8allocate8capacity2asSryxGSi_xmtlF +_$ss11ExecutorJobV14LocalAllocatorV8allocate8capacitySwSi_tF +_$ss11ExecutorJobV14LocalAllocatorVMa +_$ss11ExecutorJobV14LocalAllocatorVMn +_$ss11ExecutorJobV14LocalAllocatorVN +_$ss11ExecutorJobV4KindV13firstReservedADvgZ +_$ss11ExecutorJobV4KindV13firstReservedADvpZMV +_$ss11ExecutorJobV4KindV4taskADvgZ +_$ss11ExecutorJobV4KindV4taskADvpZMV +_$ss11ExecutorJobV4KindV8rawValueADSgs5UInt8V_tcfC +_$ss11ExecutorJobV4KindV8rawValues5UInt8VvM +_$ss11ExecutorJobV4KindV8rawValues5UInt8Vvg +_$ss11ExecutorJobV4KindV8rawValues5UInt8VvpMV +_$ss11ExecutorJobV4KindV8rawValues5UInt8Vvs +_$ss11ExecutorJobV4KindVMa +_$ss11ExecutorJobV4KindVMn +_$ss11ExecutorJobV4KindVN +_$ss11ExecutorJobV4KindVSYsMc +_$ss11ExecutorJobV4kindAB4KindVvg _$ss11ExecutorJobV7contextABBjn_tcfC _$ss11ExecutorJobV8prioritys0B8PriorityVvg +_$ss11ExecutorJobV9allocatorAB14LocalAllocatorVSgvg _$ss11ExecutorJobVMa _$ss11ExecutorJobVMn _$ss11ExecutorJobVN @@ -403,9 +468,33 @@ _$ss11JobPriorityVMn _$ss11JobPriorityVN _$ss11JobPriorityVSLsMc _$ss11JobPriorityVSQsMc +_$ss12MainExecutorMp +_$ss12MainExecutorPScfTb +_$ss12MainExecutorPs07RunLoopB0Tb +_$ss12MainExecutorTL _$ss13_runAsyncMainyyyyYaKcF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lFTu +_$ss14CFMainExecutorC3runyyKF +_$ss14CFMainExecutorC4stopyyF +_$ss14CFMainExecutorCABycfC +_$ss14CFMainExecutorCABycfc +_$ss14CFMainExecutorCMa +_$ss14CFMainExecutorCMm +_$ss14CFMainExecutorCMn +_$ss14CFMainExecutorCMo +_$ss14CFMainExecutorCN +_$ss14CFMainExecutorCfD +_$ss14CFMainExecutorCfd +_$ss14CFTaskExecutorCABycfC +_$ss14CFTaskExecutorCABycfc +_$ss14CFTaskExecutorCMa +_$ss14CFTaskExecutorCMm +_$ss14CFTaskExecutorCMn +_$ss14CFTaskExecutorCMo +_$ss14CFTaskExecutorCN +_$ss14CFTaskExecutorCfD +_$ss14CFTaskExecutorCfd _$ss15ContinuousClockV17minimumResolutions8DurationVvg _$ss15ContinuousClockV17minimumResolutions8DurationVvpMV _$ss15ContinuousClockV3nowAB7InstantVvg @@ -413,6 +502,8 @@ _$ss15ContinuousClockV3nowAB7InstantVvgZ _$ss15ContinuousClockV3nowAB7InstantVvpMV _$ss15ContinuousClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKF _$ss15ContinuousClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKFTu +_$ss15ContinuousClockV6traitss0B6TraitsVvg +_$ss15ContinuousClockV6traitss0B6TraitsVvpMV _$ss15ContinuousClockV7InstantV1loiySbAD_ADtFZ _$ss15ContinuousClockV7InstantV2eeoiySbAD_ADtFZ _$ss15ContinuousClockV7InstantV3nowADvgZ @@ -438,6 +529,22 @@ _$ss15ContinuousClockVMn _$ss15ContinuousClockVN _$ss15ContinuousClockVs0B0sMc _$ss15ContinuousClockVs0B0sWP +_$ss15ExecutorFactoryMp +_$ss15ExecutorFactoryP04mainA0s04MainA0_pvgZTj +_$ss15ExecutorFactoryP04mainA0s04MainA0_pvgZTq +_$ss15ExecutorFactoryP07defaultA0Sch_pvgZTj +_$ss15ExecutorFactoryP07defaultA0Sch_pvgZTq +_$ss15ExecutorFactoryTL +_$ss15RunLoopExecutorMp +_$ss15RunLoopExecutorP3runyyKFTj +_$ss15RunLoopExecutorP3runyyKFTq +_$ss15RunLoopExecutorP4stopyyFTj +_$ss15RunLoopExecutorP4stopyyFTq +_$ss15RunLoopExecutorP8runUntilyySbyXEKFTj +_$ss15RunLoopExecutorP8runUntilyySbyXEKFTq +_$ss15RunLoopExecutorPScFTb +_$ss15RunLoopExecutorPsE8runUntilyySbyXEKF +_$ss15RunLoopExecutorTL _$ss15SuspendingClockV17minimumResolutions8DurationVvg _$ss15SuspendingClockV17minimumResolutions8DurationVvpMV _$ss15SuspendingClockV3nowAB7InstantVvg @@ -445,6 +552,8 @@ _$ss15SuspendingClockV3nowAB7InstantVvgZ _$ss15SuspendingClockV3nowAB7InstantVvpMV _$ss15SuspendingClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKF _$ss15SuspendingClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKFTu +_$ss15SuspendingClockV6traitss0B6TraitsVvg +_$ss15SuspendingClockV6traitss0B6TraitsVvpMV _$ss15SuspendingClockV7InstantV1loiySbAD_ADtFZ _$ss15SuspendingClockV7InstantV1poiyA2D_s8DurationVtFZ _$ss15SuspendingClockV7InstantV1soiyA2D_s8DurationVtFZ @@ -496,6 +605,51 @@ _$ss16AsyncMapSequenceVMa _$ss16AsyncMapSequenceVMn _$ss16AsyncMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss16AsyncMapSequenceVyxq_GScisMc +_$ss17DummyMainExecutorC02isbC0Sbvg +_$ss17DummyMainExecutorC02isbC0SbvpMV +_$ss17DummyMainExecutorC13checkIsolatedyyF +_$ss17DummyMainExecutorC3runyyKF +_$ss17DummyMainExecutorC4stopyyF +_$ss17DummyMainExecutorC7enqueueyys0C3JobVnF +_$ss17DummyMainExecutorCABycfC +_$ss17DummyMainExecutorCABycfCTj +_$ss17DummyMainExecutorCABycfCTq +_$ss17DummyMainExecutorCABycfc +_$ss17DummyMainExecutorCMa +_$ss17DummyMainExecutorCMm +_$ss17DummyMainExecutorCMn +_$ss17DummyMainExecutorCMo +_$ss17DummyMainExecutorCMu +_$ss17DummyMainExecutorCN +_$ss17DummyMainExecutorCScFsMc +_$ss17DummyMainExecutorCScFsWP +_$ss17DummyMainExecutorCScfsMc +_$ss17DummyMainExecutorCScfsWP +_$ss17DummyMainExecutorCfD +_$ss17DummyMainExecutorCfd +_$ss17DummyMainExecutorCs07RunLoopC0sMc +_$ss17DummyMainExecutorCs07RunLoopC0sWP +_$ss17DummyMainExecutorCs0bC0sMc +_$ss17DummyMainExecutorCs0bC0sWP +_$ss17DummyTaskExecutorC06isMainC0Sbvg +_$ss17DummyTaskExecutorC06isMainC0SbvpMV +_$ss17DummyTaskExecutorC7enqueueyys0C3JobVnF +_$ss17DummyTaskExecutorCABycfC +_$ss17DummyTaskExecutorCABycfCTj +_$ss17DummyTaskExecutorCABycfCTq +_$ss17DummyTaskExecutorCABycfc +_$ss17DummyTaskExecutorCMa +_$ss17DummyTaskExecutorCMm +_$ss17DummyTaskExecutorCMn +_$ss17DummyTaskExecutorCMo +_$ss17DummyTaskExecutorCMu +_$ss17DummyTaskExecutorCN +_$ss17DummyTaskExecutorCScFsMc +_$ss17DummyTaskExecutorCScFsWP +_$ss17DummyTaskExecutorCSchsMc +_$ss17DummyTaskExecutorCSchsWP +_$ss17DummyTaskExecutorCfD +_$ss17DummyTaskExecutorCfd _$ss19AsyncFilterSequenceV04makeA8IteratorAB0E0Vyx_GyF _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvg _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvpMV @@ -553,6 +707,17 @@ _$ss19DiscardingTaskGroupV9cancelAllyyF _$ss19DiscardingTaskGroupVMa _$ss19DiscardingTaskGroupVMn _$ss19DiscardingTaskGroupVN +_$ss19SchedulableExecutorMp +_$ss19SchedulableExecutorP7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj +_$ss19SchedulableExecutorP7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq +_$ss19SchedulableExecutorP7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lFTj +_$ss19SchedulableExecutorP7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lFTq +_$ss19SchedulableExecutorPScFTb +_$ss19SchedulableExecutorPsE7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF +_$ss19SchedulableExecutorPsE7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lF +_$ss19SchedulableExecutorTL +_$ss19UnownedTaskExecutorV02asbC0Sch_pSgyF +_$ss19UnownedTaskExecutorVyABxhcSchRzlufC _$ss20AsyncFlatMapSequenceV04makeA8IteratorAB0F0Vyxq__GyF _$ss20AsyncFlatMapSequenceV4basexvg _$ss20AsyncFlatMapSequenceV4basexvpMV @@ -582,6 +747,39 @@ _$ss20AsyncFlatMapSequenceVMa _$ss20AsyncFlatMapSequenceVMn _$ss20AsyncFlatMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss20AsyncFlatMapSequenceVyxq_GScisMc +_$ss20DispatchMainExecutorC02isbC0Sbvg +_$ss20DispatchMainExecutorC02isbC0SbvpMV +_$ss20DispatchMainExecutorC13asSchedulables0eC0_pSgvg +_$ss20DispatchMainExecutorC13asSchedulables0eC0_pSgvpMV +_$ss20DispatchMainExecutorC13checkIsolatedyyF +_$ss20DispatchMainExecutorC3runyyKFTj +_$ss20DispatchMainExecutorC3runyyKFTq +_$ss20DispatchMainExecutorC4stopyyFTj +_$ss20DispatchMainExecutorC4stopyyFTq +_$ss20DispatchMainExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlF +_$ss20DispatchMainExecutorC7enqueueyys0C3JobVnF +_$ss20DispatchMainExecutorCABycfC +_$ss20DispatchMainExecutorCABycfCTj +_$ss20DispatchMainExecutorCABycfCTq +_$ss20DispatchMainExecutorCABycfc +_$ss20DispatchMainExecutorCMa +_$ss20DispatchMainExecutorCMm +_$ss20DispatchMainExecutorCMn +_$ss20DispatchMainExecutorCMo +_$ss20DispatchMainExecutorCMu +_$ss20DispatchMainExecutorCN +_$ss20DispatchMainExecutorCScFsMc +_$ss20DispatchMainExecutorCScFsWP +_$ss20DispatchMainExecutorCScfsMc +_$ss20DispatchMainExecutorCScfsWP +_$ss20DispatchMainExecutorCfD +_$ss20DispatchMainExecutorCfd +_$ss20DispatchMainExecutorCs011SchedulableC0sMc +_$ss20DispatchMainExecutorCs011SchedulableC0sWP +_$ss20DispatchMainExecutorCs07RunLoopC0sMc +_$ss20DispatchMainExecutorCs07RunLoopC0sWP +_$ss20DispatchMainExecutorCs0bC0sMc +_$ss20DispatchMainExecutorCs0bC0sWP _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lF _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lFTu _$ss21withUnsafeCurrentTask4bodyxxSctSgKXE_tKlF @@ -654,6 +852,15 @@ _$ss23AsyncCompactMapSequenceVMa _$ss23AsyncCompactMapSequenceVMn _$ss23AsyncCompactMapSequenceV_9transformAByxq_Gx_q_Sg7ElementQzYactcfC _$ss23AsyncCompactMapSequenceVyxq_GScisMc +_$ss23PlatformExecutorFactoryV04mainB0s04MainB0_pvgZ +_$ss23PlatformExecutorFactoryV04mainB0s04MainB0_pvpZMV +_$ss23PlatformExecutorFactoryV07defaultB0Sch_pvgZ +_$ss23PlatformExecutorFactoryV07defaultB0Sch_pvpZMV +_$ss23PlatformExecutorFactoryVMa +_$ss23PlatformExecutorFactoryVMn +_$ss23PlatformExecutorFactoryVN +_$ss23PlatformExecutorFactoryVs0bC0sMc +_$ss23PlatformExecutorFactoryVs0bC0sWP _$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalF _$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalFTu _$ss23withDiscardingTaskGroup9returning4bodyxxm_xs0bcD0VzYaXEtYalF @@ -708,6 +915,31 @@ _$ss24AsyncThrowingMapSequenceVMa _$ss24AsyncThrowingMapSequenceVMn _$ss24AsyncThrowingMapSequenceV_9transformAByxq_Gx_q_7ElementQzYaKctcfC _$ss24AsyncThrowingMapSequenceVyxq_GScisMc +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTj +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTq +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvpMV +_$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj +_$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq +_$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTj +_$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTq +_$ss26DispatchGlobalTaskExecutorCABycfC +_$ss26DispatchGlobalTaskExecutorCABycfCTj +_$ss26DispatchGlobalTaskExecutorCABycfCTq +_$ss26DispatchGlobalTaskExecutorCABycfc +_$ss26DispatchGlobalTaskExecutorCMa +_$ss26DispatchGlobalTaskExecutorCMm +_$ss26DispatchGlobalTaskExecutorCMn +_$ss26DispatchGlobalTaskExecutorCMo +_$ss26DispatchGlobalTaskExecutorCMu +_$ss26DispatchGlobalTaskExecutorCN +_$ss26DispatchGlobalTaskExecutorCScFsMc +_$ss26DispatchGlobalTaskExecutorCScFsWP +_$ss26DispatchGlobalTaskExecutorCSchsMc +_$ss26DispatchGlobalTaskExecutorCSchsWP +_$ss26DispatchGlobalTaskExecutorCfD +_$ss26DispatchGlobalTaskExecutorCfd +_$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sMc +_$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sWP _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvpMV @@ -875,12 +1107,26 @@ _$ss5ClockP3now7InstantQzvgTq _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTj _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTjTu _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTq +_$ss5ClockP6traitss0A6TraitsVvgTj +_$ss5ClockP6traitss0A6TraitsVvgTq _$ss5ClockP7InstantAB_s0B8ProtocolTn +_$ss5ClockP7convert4from8DurationQzSgsAEV_tFTj +_$ss5ClockP7convert4from8DurationQzSgsAEV_tFTq +_$ss5ClockP7convert4froms8DurationVSgAEQz_tFTj +_$ss5ClockP7convert4froms8DurationVSgAEQz_tFTq +_$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTj +_$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTq +_$ss5ClockPsE6traitss0A6TraitsVvg +_$ss5ClockPsE6traitss0A6TraitsVvpMV +_$ss5ClockPsE7convert4from8DurationQzSgsAEV_tF +_$ss5ClockPsE7convert4froms8DurationVSgAEQz_tF +_$ss5ClockPsE7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lF _$ss5ClockPsE7measurey8DurationQzyyKXEKF _$ss5ClockPsE7measurey8DurationQzyyYaKXEYaKF _$ss5ClockPsE7measurey8DurationQzyyYaKXEYaKFTu _$ss5ClockPss010ContinuousA0VRszrlE10continuousADvgZ _$ss5ClockPss010SuspendingA0VRszrlE10suspendingADvgZ +_$ss5ClockPss8DurationVACRtzrlE7convert4fromADSgAD_tF _$ss5ClockTL _$ss6_first_5where7ElementQzSgx_SbADYaKXEtYaKSciRzlF _$ss6_first_5where7ElementQzSgx_SbADYaKXEtYaKSciRzlFTu @@ -938,6 +1184,7 @@ _swift_continuation_init _swift_continuation_resume _swift_continuation_throwingResume _swift_continuation_throwingResumeWithError +_swift_createExecutors _swift_defaultActor_deallocate _swift_defaultActor_deallocateResilient _swift_defaultActor_destroy @@ -950,8 +1197,11 @@ _swift_distributed_actor_is_remote _swift_executor_isComplexEquality _swift_get_clock_res _swift_get_time +_swift_job_allocate +_swift_job_deallocate _swift_job_run _swift_nonDefaultDistributedActor_initialize +_swift_sleep _swift_taskGroup_addPending _swift_taskGroup_attachChild _swift_taskGroup_cancelAll diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index 38037f92cc94b..557c626a2cada 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -23,6 +23,8 @@ _$sScEMa _$sScEMn _$sScEN _$sScEs5ErrorsMc +_$sScF14isMainExecutorSbvgTj +_$sScF14isMainExecutorSbvgTq _$sScF7enqueueyyScJFTj _$sScF7enqueueyyScJFTq _$sScF7enqueueyys11ExecutorJobVnFTj @@ -31,9 +33,15 @@ _$sScF7enqueueyys3JobVnFTj _$sScF7enqueueyys3JobVnFTq _$sScFMp _$sScFTL +_$sScFsE14isMainExecutorSbvg +_$sScFsE14isMainExecutorSbvpMV +_$sScFsE18_isComplexEqualitySbvg +_$sScFsE18_isComplexEqualitySbvpMV _$sScFsE7enqueueyyScJF _$sScFsE7enqueueyys11ExecutorJobVnF _$sScFsE7enqueueyys3JobVnF +_$sScFsSQRzrlE18_isComplexEqualitySbvg +_$sScFsSQRzrlE18_isComplexEqualitySbvpMV _$sScG11isCancelledSbvg _$sScG11isCancelledSbvpMV _$sScG17makeAsyncIteratorScG0C0Vyx_GyF @@ -88,6 +96,8 @@ _$sScM3run10resultType4bodyxxm_xyYbKScMYcXEtYaKlFZ _$sScM3run10resultType4bodyxxm_xyYbKScMYcXEtYaKlFZTu _$sScM6sharedScMvgZ _$sScM7enqueueyyScJF +_$sScM8executors12MainExecutor_pvgZ +_$sScM8executors12MainExecutor_pvpZMV _$sScMMa _$sScMMm _$sScMMn @@ -195,8 +205,16 @@ _$sScTss5NeverORs_rlE5valuexvgTu _$sScTss5NeverORs_rlE5valuexvpMV _$sScTss5NeverORszABRs_rlE11isCancelledSbvgZ _$sScTss5NeverORszABRs_rlE12basePriorityScPSgvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pvpZMV _$sScTss5NeverORszABRs_rlE15currentPriorityScPvgZ +_$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvgZ +_$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvpZMV _$sScTss5NeverORszABRs_rlE17checkCancellationyyKFZ +_$sScTss5NeverORszABRs_rlE17preferredExecutorSch_pSgvgZ +_$sScTss5NeverORszABRs_rlE17preferredExecutorSch_pSgvpZMV +_$sScTss5NeverORszABRs_rlE26currentSchedulableExecutors0cD0_pSgvgZ +_$sScTss5NeverORszABRs_rlE26currentSchedulableExecutors0cD0_pSgvpZMV _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZ _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZTu _$sScTss5NeverORszABRs_rlE5sleep5until9tolerance5clocky7InstantQyd___8DurationQyd__Sgqd__tYaKs5ClockRd__lFZ @@ -214,6 +232,7 @@ _$sScc7contextBcvs _$sSccMa _$sSccMn _$sSce15complexEqualityScexh_tcScfRzlufC +_$sSce16asSerialExecutorScf_pSgyF _$sSce18_isComplexEqualitySbvg _$sSce18_isComplexEqualitySbvpMV _$sSce8executorBevM @@ -227,6 +246,7 @@ _$sSceMa _$sSceMn _$sSceN _$sSceySceBecfC +_$sSceyScexhcScfRzlufC _$sScf23asUnownedSerialExecutorSceyFTj _$sScf23asUnownedSerialExecutorSceyFTq _$sScf31isSameExclusiveExecutionContext5otherSbx_tFTj @@ -241,9 +261,12 @@ _$sScfMp _$sScfScFTb _$sScfTL _$sScfsE14assertIsolated_4file4lineySSyXK_s12StaticStringVSutF +_$sScfsE14isMainExecutorSbvg +_$sScfsE14isMainExecutorSbvpMV _$sScfsE20preconditionIsolated_4file4lineySSyXK_s12StaticStringVSutF _$sScfsE23asUnownedSerialExecutorSceyF _$sScfsE31isSameExclusiveExecutionContext5otherSbx_tF +_$sScfsSQRzrlE31isSameExclusiveExecutionContext5otherSbx_tF _$sScg10nextResults0B0Oyxq_GSgyYaKF _$sScg10nextResults0B0Oyxq_GSgyYaKFTu _$sScg11_waitForAllyyYaKF @@ -370,9 +393,51 @@ _$sSctN _$sSctSHsMc _$sSctSQsMc _$ss039_checkIllegalTaskLocalBindingWithinWithC5Group4file4lineySS_SutF +_$ss11ClockTraitsV10continuousABvgZ +_$ss11ClockTraitsV10continuousABvpZMV +_$ss11ClockTraitsV8rawValueABs6UInt32V_tcfC +_$ss11ClockTraitsV8rawValues6UInt32Vvg +_$ss11ClockTraitsV8rawValues6UInt32VvpMV +_$ss11ClockTraitsV8wallTimeABvgZ +_$ss11ClockTraitsV8wallTimeABvpZMV +_$ss11ClockTraitsV9monotonicABvgZ +_$ss11ClockTraitsV9monotonicABvpZMV +_$ss11ClockTraitsVMa +_$ss11ClockTraitsVMn +_$ss11ClockTraitsVN +_$ss11ClockTraitsVSQsMc +_$ss11ClockTraitsVSYsMc +_$ss11ClockTraitsVs10SetAlgebrasMc +_$ss11ClockTraitsVs25ExpressibleByArrayLiteralsMc +_$ss11ClockTraitsVs9OptionSetsMc +_$ss11ExecutorJobV010withUnsafeA11PrivateData4bodyxxSwq_YKXE_tq_YKs5ErrorR_r0_lF _$ss11ExecutorJobV11descriptionSSvg +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySpyxGlF +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySryxGlF +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySwF +_$ss11ExecutorJobV14LocalAllocatorV8allocate2asSpyxGxm_tlF +_$ss11ExecutorJobV14LocalAllocatorV8allocate8capacity2asSryxGSi_xmtlF +_$ss11ExecutorJobV14LocalAllocatorV8allocate8capacitySwSi_tF +_$ss11ExecutorJobV14LocalAllocatorVMa +_$ss11ExecutorJobV14LocalAllocatorVMn +_$ss11ExecutorJobV14LocalAllocatorVN +_$ss11ExecutorJobV4KindV13firstReservedADvgZ +_$ss11ExecutorJobV4KindV13firstReservedADvpZMV +_$ss11ExecutorJobV4KindV4taskADvgZ +_$ss11ExecutorJobV4KindV4taskADvpZMV +_$ss11ExecutorJobV4KindV8rawValueADSgs5UInt8V_tcfC +_$ss11ExecutorJobV4KindV8rawValues5UInt8VvM +_$ss11ExecutorJobV4KindV8rawValues5UInt8Vvg +_$ss11ExecutorJobV4KindV8rawValues5UInt8VvpMV +_$ss11ExecutorJobV4KindV8rawValues5UInt8Vvs +_$ss11ExecutorJobV4KindVMa +_$ss11ExecutorJobV4KindVMn +_$ss11ExecutorJobV4KindVN +_$ss11ExecutorJobV4KindVSYsMc +_$ss11ExecutorJobV4kindAB4KindVvg _$ss11ExecutorJobV7contextABBjn_tcfC _$ss11ExecutorJobV8prioritys0B8PriorityVvg +_$ss11ExecutorJobV9allocatorAB14LocalAllocatorVSgvg _$ss11ExecutorJobVMa _$ss11ExecutorJobVMn _$ss11ExecutorJobVN @@ -403,9 +468,33 @@ _$ss11JobPriorityVMn _$ss11JobPriorityVN _$ss11JobPriorityVSLsMc _$ss11JobPriorityVSQsMc +_$ss12MainExecutorMp +_$ss12MainExecutorPScfTb +_$ss12MainExecutorPs07RunLoopB0Tb +_$ss12MainExecutorTL _$ss13_runAsyncMainyyyyYaKcF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lFTu +_$ss14CFMainExecutorC3runyyKF +_$ss14CFMainExecutorC4stopyyF +_$ss14CFMainExecutorCABycfC +_$ss14CFMainExecutorCABycfc +_$ss14CFMainExecutorCMa +_$ss14CFMainExecutorCMm +_$ss14CFMainExecutorCMn +_$ss14CFMainExecutorCMo +_$ss14CFMainExecutorCN +_$ss14CFMainExecutorCfD +_$ss14CFMainExecutorCfd +_$ss14CFTaskExecutorCABycfC +_$ss14CFTaskExecutorCABycfc +_$ss14CFTaskExecutorCMa +_$ss14CFTaskExecutorCMm +_$ss14CFTaskExecutorCMn +_$ss14CFTaskExecutorCMo +_$ss14CFTaskExecutorCN +_$ss14CFTaskExecutorCfD +_$ss14CFTaskExecutorCfd _$ss15ContinuousClockV17minimumResolutions8DurationVvg _$ss15ContinuousClockV17minimumResolutions8DurationVvpMV _$ss15ContinuousClockV3nowAB7InstantVvg @@ -413,6 +502,8 @@ _$ss15ContinuousClockV3nowAB7InstantVvgZ _$ss15ContinuousClockV3nowAB7InstantVvpMV _$ss15ContinuousClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKF _$ss15ContinuousClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKFTu +_$ss15ContinuousClockV6traitss0B6TraitsVvg +_$ss15ContinuousClockV6traitss0B6TraitsVvpMV _$ss15ContinuousClockV7InstantV1loiySbAD_ADtFZ _$ss15ContinuousClockV7InstantV2eeoiySbAD_ADtFZ _$ss15ContinuousClockV7InstantV3nowADvgZ @@ -438,6 +529,22 @@ _$ss15ContinuousClockVMn _$ss15ContinuousClockVN _$ss15ContinuousClockVs0B0sMc _$ss15ContinuousClockVs0B0sWP +_$ss15ExecutorFactoryMp +_$ss15ExecutorFactoryP04mainA0s04MainA0_pvgZTj +_$ss15ExecutorFactoryP04mainA0s04MainA0_pvgZTq +_$ss15ExecutorFactoryP07defaultA0Sch_pvgZTj +_$ss15ExecutorFactoryP07defaultA0Sch_pvgZTq +_$ss15ExecutorFactoryTL +_$ss15RunLoopExecutorMp +_$ss15RunLoopExecutorP3runyyKFTj +_$ss15RunLoopExecutorP3runyyKFTq +_$ss15RunLoopExecutorP4stopyyFTj +_$ss15RunLoopExecutorP4stopyyFTq +_$ss15RunLoopExecutorP8runUntilyySbyXEKFTj +_$ss15RunLoopExecutorP8runUntilyySbyXEKFTq +_$ss15RunLoopExecutorPScFTb +_$ss15RunLoopExecutorPsE8runUntilyySbyXEKF +_$ss15RunLoopExecutorTL _$ss15SuspendingClockV17minimumResolutions8DurationVvg _$ss15SuspendingClockV17minimumResolutions8DurationVvpMV _$ss15SuspendingClockV3nowAB7InstantVvg @@ -445,6 +552,8 @@ _$ss15SuspendingClockV3nowAB7InstantVvgZ _$ss15SuspendingClockV3nowAB7InstantVvpMV _$ss15SuspendingClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKF _$ss15SuspendingClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKFTu +_$ss15SuspendingClockV6traitss0B6TraitsVvg +_$ss15SuspendingClockV6traitss0B6TraitsVvpMV _$ss15SuspendingClockV7InstantV1loiySbAD_ADtFZ _$ss15SuspendingClockV7InstantV1poiyA2D_s8DurationVtFZ _$ss15SuspendingClockV7InstantV1soiyA2D_s8DurationVtFZ @@ -496,6 +605,51 @@ _$ss16AsyncMapSequenceVMa _$ss16AsyncMapSequenceVMn _$ss16AsyncMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss16AsyncMapSequenceVyxq_GScisMc +_$ss17DummyMainExecutorC02isbC0Sbvg +_$ss17DummyMainExecutorC02isbC0SbvpMV +_$ss17DummyMainExecutorC13checkIsolatedyyF +_$ss17DummyMainExecutorC3runyyKF +_$ss17DummyMainExecutorC4stopyyF +_$ss17DummyMainExecutorC7enqueueyys0C3JobVnF +_$ss17DummyMainExecutorCABycfC +_$ss17DummyMainExecutorCABycfCTj +_$ss17DummyMainExecutorCABycfCTq +_$ss17DummyMainExecutorCABycfc +_$ss17DummyMainExecutorCMa +_$ss17DummyMainExecutorCMm +_$ss17DummyMainExecutorCMn +_$ss17DummyMainExecutorCMo +_$ss17DummyMainExecutorCMu +_$ss17DummyMainExecutorCN +_$ss17DummyMainExecutorCScFsMc +_$ss17DummyMainExecutorCScFsWP +_$ss17DummyMainExecutorCScfsMc +_$ss17DummyMainExecutorCScfsWP +_$ss17DummyMainExecutorCfD +_$ss17DummyMainExecutorCfd +_$ss17DummyMainExecutorCs07RunLoopC0sMc +_$ss17DummyMainExecutorCs07RunLoopC0sWP +_$ss17DummyMainExecutorCs0bC0sMc +_$ss17DummyMainExecutorCs0bC0sWP +_$ss17DummyTaskExecutorC06isMainC0Sbvg +_$ss17DummyTaskExecutorC06isMainC0SbvpMV +_$ss17DummyTaskExecutorC7enqueueyys0C3JobVnF +_$ss17DummyTaskExecutorCABycfC +_$ss17DummyTaskExecutorCABycfCTj +_$ss17DummyTaskExecutorCABycfCTq +_$ss17DummyTaskExecutorCABycfc +_$ss17DummyTaskExecutorCMa +_$ss17DummyTaskExecutorCMm +_$ss17DummyTaskExecutorCMn +_$ss17DummyTaskExecutorCMo +_$ss17DummyTaskExecutorCMu +_$ss17DummyTaskExecutorCN +_$ss17DummyTaskExecutorCScFsMc +_$ss17DummyTaskExecutorCScFsWP +_$ss17DummyTaskExecutorCSchsMc +_$ss17DummyTaskExecutorCSchsWP +_$ss17DummyTaskExecutorCfD +_$ss17DummyTaskExecutorCfd _$ss19AsyncFilterSequenceV04makeA8IteratorAB0E0Vyx_GyF _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvg _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvpMV @@ -553,6 +707,17 @@ _$ss19DiscardingTaskGroupV9cancelAllyyF _$ss19DiscardingTaskGroupVMa _$ss19DiscardingTaskGroupVMn _$ss19DiscardingTaskGroupVN +_$ss19SchedulableExecutorMp +_$ss19SchedulableExecutorP7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj +_$ss19SchedulableExecutorP7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq +_$ss19SchedulableExecutorP7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lFTj +_$ss19SchedulableExecutorP7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lFTq +_$ss19SchedulableExecutorPScFTb +_$ss19SchedulableExecutorPsE7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF +_$ss19SchedulableExecutorPsE7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lF +_$ss19SchedulableExecutorTL +_$ss19UnownedTaskExecutorV02asbC0Sch_pSgyF +_$ss19UnownedTaskExecutorVyABxhcSchRzlufC _$ss20AsyncFlatMapSequenceV04makeA8IteratorAB0F0Vyxq__GyF _$ss20AsyncFlatMapSequenceV4basexvg _$ss20AsyncFlatMapSequenceV4basexvpMV @@ -582,6 +747,39 @@ _$ss20AsyncFlatMapSequenceVMa _$ss20AsyncFlatMapSequenceVMn _$ss20AsyncFlatMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss20AsyncFlatMapSequenceVyxq_GScisMc +_$ss20DispatchMainExecutorC02isbC0Sbvg +_$ss20DispatchMainExecutorC02isbC0SbvpMV +_$ss20DispatchMainExecutorC13asSchedulables0eC0_pSgvg +_$ss20DispatchMainExecutorC13asSchedulables0eC0_pSgvpMV +_$ss20DispatchMainExecutorC13checkIsolatedyyF +_$ss20DispatchMainExecutorC3runyyKFTj +_$ss20DispatchMainExecutorC3runyyKFTq +_$ss20DispatchMainExecutorC4stopyyFTj +_$ss20DispatchMainExecutorC4stopyyFTq +_$ss20DispatchMainExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlF +_$ss20DispatchMainExecutorC7enqueueyys0C3JobVnF +_$ss20DispatchMainExecutorCABycfC +_$ss20DispatchMainExecutorCABycfCTj +_$ss20DispatchMainExecutorCABycfCTq +_$ss20DispatchMainExecutorCABycfc +_$ss20DispatchMainExecutorCMa +_$ss20DispatchMainExecutorCMm +_$ss20DispatchMainExecutorCMn +_$ss20DispatchMainExecutorCMo +_$ss20DispatchMainExecutorCMu +_$ss20DispatchMainExecutorCN +_$ss20DispatchMainExecutorCScFsMc +_$ss20DispatchMainExecutorCScFsWP +_$ss20DispatchMainExecutorCScfsMc +_$ss20DispatchMainExecutorCScfsWP +_$ss20DispatchMainExecutorCfD +_$ss20DispatchMainExecutorCfd +_$ss20DispatchMainExecutorCs011SchedulableC0sMc +_$ss20DispatchMainExecutorCs011SchedulableC0sWP +_$ss20DispatchMainExecutorCs07RunLoopC0sMc +_$ss20DispatchMainExecutorCs07RunLoopC0sWP +_$ss20DispatchMainExecutorCs0bC0sMc +_$ss20DispatchMainExecutorCs0bC0sWP _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lF _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lFTu _$ss21withUnsafeCurrentTask4bodyxxSctSgKXE_tKlF @@ -654,6 +852,15 @@ _$ss23AsyncCompactMapSequenceVMa _$ss23AsyncCompactMapSequenceVMn _$ss23AsyncCompactMapSequenceV_9transformAByxq_Gx_q_Sg7ElementQzYactcfC _$ss23AsyncCompactMapSequenceVyxq_GScisMc +_$ss23PlatformExecutorFactoryV04mainB0s04MainB0_pvgZ +_$ss23PlatformExecutorFactoryV04mainB0s04MainB0_pvpZMV +_$ss23PlatformExecutorFactoryV07defaultB0Sch_pvgZ +_$ss23PlatformExecutorFactoryV07defaultB0Sch_pvpZMV +_$ss23PlatformExecutorFactoryVMa +_$ss23PlatformExecutorFactoryVMn +_$ss23PlatformExecutorFactoryVN +_$ss23PlatformExecutorFactoryVs0bC0sMc +_$ss23PlatformExecutorFactoryVs0bC0sWP _$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalF _$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalFTu _$ss23withDiscardingTaskGroup9returning4bodyxxm_xs0bcD0VzYaXEtYalF @@ -708,6 +915,31 @@ _$ss24AsyncThrowingMapSequenceVMa _$ss24AsyncThrowingMapSequenceVMn _$ss24AsyncThrowingMapSequenceV_9transformAByxq_Gx_q_7ElementQzYaKctcfC _$ss24AsyncThrowingMapSequenceVyxq_GScisMc +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTj +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTq +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvpMV +_$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj +_$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq +_$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTj +_$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTq +_$ss26DispatchGlobalTaskExecutorCABycfC +_$ss26DispatchGlobalTaskExecutorCABycfCTj +_$ss26DispatchGlobalTaskExecutorCABycfCTq +_$ss26DispatchGlobalTaskExecutorCABycfc +_$ss26DispatchGlobalTaskExecutorCMa +_$ss26DispatchGlobalTaskExecutorCMm +_$ss26DispatchGlobalTaskExecutorCMn +_$ss26DispatchGlobalTaskExecutorCMo +_$ss26DispatchGlobalTaskExecutorCMu +_$ss26DispatchGlobalTaskExecutorCN +_$ss26DispatchGlobalTaskExecutorCScFsMc +_$ss26DispatchGlobalTaskExecutorCScFsWP +_$ss26DispatchGlobalTaskExecutorCSchsMc +_$ss26DispatchGlobalTaskExecutorCSchsWP +_$ss26DispatchGlobalTaskExecutorCfD +_$ss26DispatchGlobalTaskExecutorCfd +_$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sMc +_$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sWP _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvpMV @@ -875,12 +1107,26 @@ _$ss5ClockP3now7InstantQzvgTq _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTj _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTjTu _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTq +_$ss5ClockP6traitss0A6TraitsVvgTj +_$ss5ClockP6traitss0A6TraitsVvgTq _$ss5ClockP7InstantAB_s0B8ProtocolTn +_$ss5ClockP7convert4from8DurationQzSgsAEV_tFTj +_$ss5ClockP7convert4from8DurationQzSgsAEV_tFTq +_$ss5ClockP7convert4froms8DurationVSgAEQz_tFTj +_$ss5ClockP7convert4froms8DurationVSgAEQz_tFTq +_$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTj +_$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTq +_$ss5ClockPsE6traitss0A6TraitsVvg +_$ss5ClockPsE6traitss0A6TraitsVvpMV +_$ss5ClockPsE7convert4from8DurationQzSgsAEV_tF +_$ss5ClockPsE7convert4froms8DurationVSgAEQz_tF +_$ss5ClockPsE7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lF _$ss5ClockPsE7measurey8DurationQzyyKXEKF _$ss5ClockPsE7measurey8DurationQzyyYaKXEYaKF _$ss5ClockPsE7measurey8DurationQzyyYaKXEYaKFTu _$ss5ClockPss010ContinuousA0VRszrlE10continuousADvgZ _$ss5ClockPss010SuspendingA0VRszrlE10suspendingADvgZ +_$ss5ClockPss8DurationVACRtzrlE7convert4fromADSgAD_tF _$ss5ClockTL _$ss6_first_5where7ElementQzSgx_SbADYaKXEtYaKSciRzlF _$ss6_first_5where7ElementQzSgx_SbADYaKXEtYaKSciRzlFTu @@ -938,6 +1184,7 @@ _swift_continuation_init _swift_continuation_resume _swift_continuation_throwingResume _swift_continuation_throwingResumeWithError +_swift_createExecutors _swift_defaultActor_deallocate _swift_defaultActor_deallocateResilient _swift_defaultActor_destroy @@ -950,8 +1197,11 @@ _swift_distributed_actor_is_remote _swift_executor_isComplexEquality _swift_get_clock_res _swift_get_time +_swift_job_allocate +_swift_job_deallocate _swift_job_run _swift_nonDefaultDistributedActor_initialize +_swift_sleep _swift_taskGroup_addPending _swift_taskGroup_attachChild _swift_taskGroup_cancelAll