From e84e9c3c238729473ffefb977bed734567f8c0a0 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Mon, 17 Feb 2025 22:09:38 +0900 Subject: [PATCH 1/9] [Concurrency] Tasks now have names! --- include/swift/ABI/MetadataValues.h | 9 ++ include/swift/ABI/Task.h | 22 +++ include/swift/ABI/TaskOptions.h | 20 +++ include/swift/ABI/TaskStatus.h | 16 ++ include/swift/AST/ASTSynthesis.h | 3 + include/swift/AST/Builtins.def | 3 +- include/swift/Basic/Features.def | 1 + include/swift/Runtime/Concurrency.h | 3 + lib/AST/Builtins.cpp | 48 ++++++ lib/IRGen/GenConcurrency.cpp | 47 +++++- lib/IRGen/GenConcurrency.h | 1 + lib/IRGen/IRGenModule.cpp | 5 + lib/IRGen/IRGenModule.h | 1 + lib/IRGen/IRGenSIL.cpp | 10 +- lib/SIL/Verifier/SILVerifier.cpp | 8 +- lib/SILGen/SILGenBuiltin.cpp | 9 ++ stdlib/public/Concurrency/Actor.cpp | 12 ++ stdlib/public/Concurrency/Task.cpp | 36 +++-- stdlib/public/Concurrency/Task.swift | 148 +++++++++++++++++- stdlib/public/Concurrency/TaskPrivate.h | 30 ++-- stdlib/public/Concurrency/TaskStatus.cpp | 106 +++++++++++-- stdlib/public/core/OverloadMacros.swift | 46 ++++++ .../include/Concurrency/TaskPrivate.h | 2 +- .../Runtime/async_task_naming.swift | 34 ++++ .../async_task_locals_basic_warnings.swift | 1 + 25 files changed, 576 insertions(+), 45 deletions(-) create mode 100644 stdlib/public/core/OverloadMacros.swift create mode 100644 test/Concurrency/Runtime/async_task_naming.swift diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index d514b103cd752..e39f963202e8f 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -2730,6 +2730,7 @@ class JobFlags : public FlagSet { // 27 is currently unused Task_IsAsyncLetTask = 28, Task_HasInitialTaskExecutorPreference = 29, + Task_HasInitialTaskName = 30, }; // clang-format on @@ -2766,6 +2767,9 @@ class JobFlags : public FlagSet { FLAGSET_DEFINE_FLAG_ACCESSORS(Task_HasInitialTaskExecutorPreference, task_hasInitialTaskExecutorPreference, task_setHasInitialTaskExecutorPreference) + FLAGSET_DEFINE_FLAG_ACCESSORS(Task_HasInitialTaskName, + task_hasInitialTaskName, + task_setHasInitialTaskName) }; /// Kinds of task status record. @@ -2795,6 +2799,9 @@ enum class TaskStatusRecordKind : uint8_t { /// enqueued on. TaskExecutorPreference = 5, + /// A human-readable task name. + TaskName = 6, + // Kinds >= 192 are private to the implementation. First_Reserved = 192, Private_RecordLock = 192 @@ -2818,6 +2825,8 @@ enum class TaskOptionRecordKind : uint8_t { /// Set the initial task executor preference of the task. InitialTaskExecutorUnowned = 5, InitialTaskExecutorOwned = 6, + // Set a human-readable task name. + InitialTaskName = 7, /// Request a child task for swift_task_run_inline. RunInline = UINT8_MAX, }; diff --git a/include/swift/ABI/Task.h b/include/swift/ABI/Task.h index e450332b6c3d6..cf585020abdd4 100644 --- a/include/swift/ABI/Task.h +++ b/include/swift/ABI/Task.h @@ -421,6 +421,28 @@ class AsyncTask : public Job { /// Checking this is, of course, inherently race-prone on its own. bool isCancelled() const; + // ==== INITIAL TASK RECORDS ================================================= + // A task may have a number of "initial" records set, they MUST be set in the + // following order to make the task-local allocation/deallocation's stack + // discipline easy to work out at the tasks destruction: + // + // - Initial TaskName + // - Initial ExecutorPreference + + // ==== Task Naming ---------------------------------------------------------- + + /// At task creation a task may be assigned a name. + void pushInitialTaskName(const char* taskName); + void dropInitialTaskNameRecord(); + + /// Get the initial task name that was given to this task during creation, + /// or nullptr if the task has no name + const char* getTaskName(); + + bool hasInitialTaskNameRecord() const { + return Flags.task_hasInitialTaskName(); + } + // ==== Task Executor Preference --------------------------------------------- /// Get the preferred task executor reference if there is one set for this diff --git a/include/swift/ABI/TaskOptions.h b/include/swift/ABI/TaskOptions.h index 421901fc9e4bb..f1b8448e3841f 100644 --- a/include/swift/ABI/TaskOptions.h +++ b/include/swift/ABI/TaskOptions.h @@ -131,6 +131,26 @@ class InitialTaskExecutorOwnedPreferenceTaskOptionRecord } }; +class InitialTaskNameTaskOptionRecord + : public TaskOptionRecord { + + const char* TaskName; + +public: + InitialTaskNameTaskOptionRecord( + const char* taskName) + : TaskOptionRecord(TaskOptionRecordKind::InitialTaskName), + TaskName(taskName) {} + + const char* getTaskName() const { + return TaskName; + } + + static bool classof(const TaskOptionRecord *record) { + return record->getKind() == TaskOptionRecordKind::InitialTaskName; + } +}; + /// Task option to specify the initial serial executor for the task. class InitialSerialExecutorTaskOptionRecord : public TaskOptionRecord { const SerialExecutorRef Executor; diff --git a/include/swift/ABI/TaskStatus.h b/include/swift/ABI/TaskStatus.h index 8a61b0de01b65..81406ceb1db91 100644 --- a/include/swift/ABI/TaskStatus.h +++ b/include/swift/ABI/TaskStatus.h @@ -325,6 +325,22 @@ class TaskExecutorPreferenceStatusRecord : public TaskStatusRecord { } }; +class TaskNameStatusRecord : public TaskStatusRecord { +private: + const char *Name; + +public: + TaskNameStatusRecord(const char *name) + : TaskStatusRecord(TaskStatusRecordKind::TaskName), + Name(name) {} + + const char *getName() { return Name; } + + static bool classof(const TaskStatusRecord *record) { + return record->getKind() == TaskStatusRecordKind::TaskName; + } +}; + // This record is allocated for a task to record what it is dependent on before // the task can make progress again. class TaskDependencyStatusRecord : public TaskStatusRecord { diff --git a/include/swift/AST/ASTSynthesis.h b/include/swift/AST/ASTSynthesis.h index d4ee25064e87f..340124f439d81 100644 --- a/include/swift/AST/ASTSynthesis.h +++ b/include/swift/AST/ASTSynthesis.h @@ -57,6 +57,7 @@ enum SingletonTypeSynthesizer { _taskExecutor, // the '_Concurrency.TaskExecutor' protocol _actor, // the '_Concurrency.Actor' protocol _distributedActor, // the 'Distributed.DistributedActor' protocol + _unsafeRawBufferPointer // UnsafeRawBufferPointer }; inline Type synthesizeType(SynthesisContext &SC, SingletonTypeSynthesizer kind) { @@ -89,6 +90,8 @@ inline Type synthesizeType(SynthesisContext &SC, case _distributedActor: return SC.Context.getProtocol(KnownProtocolKind::DistributedActor) ->getDeclaredInterfaceType(); + case _unsafeRawBufferPointer: + return SC.Context.getUnsafeRawBufferPointerType(); case _copyable: return SC.Context.getProtocol(KnownProtocolKind::Copyable) ->getDeclaredInterfaceType(); diff --git a/include/swift/AST/Builtins.def b/include/swift/AST/Builtins.def index 6b0351691f0ef..ff7327bf7de72 100644 --- a/include/swift/AST/Builtins.def +++ b/include/swift/AST/Builtins.def @@ -954,7 +954,8 @@ BUILTIN_MISC_OPERATION(UnprotectedAddressOfBorrowOpaque, "unprotectedAddressOfBo /// createTask(flags: Int, /// initialSerialExecutor: (Builtin.Executor)? = nil, /// taskGroup: Builtin.RawPointer? = nil, -/// initialTaskExecutor: (Builtin.Executor)? = nil, +/// initialTaskExecutorDeprecated: (Builtin.Executor)? = nil, +/// initialTaskExecutorOwned: (any TaskExecutor)? = nil, /// operation: sending @escaping () async throws -> T) /// -> Builtin.NativeObject, Builtin.RawPointer) /// diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index fe0484e628a2c..ce9ec50af00d0 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -151,6 +151,7 @@ BASELINE_LANGUAGE_FEATURE(BuiltinBuildExecutor, 0, "Executor-building builtins") BASELINE_LANGUAGE_FEATURE(BuiltinBuildComplexEqualityExecutor, 0, "Executor-building for 'complexEquality executor' builtins") BASELINE_LANGUAGE_FEATURE(BuiltinBuildMainExecutor, 0, "MainActor executor building builtin") BASELINE_LANGUAGE_FEATURE(BuiltinCreateAsyncTaskOwnedTaskExecutor, 0, "Task create with owned TaskExecutor") +BASELINE_LANGUAGE_FEATURE(BuiltinCreateAsyncTaskName, 0, "Task create with a name") BASELINE_LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroup, 0, "Task create in task group builtin with extra flags") BASELINE_LANGUAGE_FEATURE(BuiltinCreateAsyncTaskInGroupWithExecutor, 0, "Task create in task group builtin with extra flags") BASELINE_LANGUAGE_FEATURE(BuiltinCreateAsyncDiscardingTaskInGroup, 0, "Task create in discarding task group builtin, accounting for the Void return type") diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index ee4e713c592b7..e47231fc96def 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -1022,6 +1022,9 @@ void swift_task_reportUnexpectedExecutor( SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) JobPriority swift_task_getCurrentThreadPriority(void); +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +const char *swift_task_getCurrentTaskName(void); + SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_task_startOnMainActor(AsyncTask* job); diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index a6197ebb043f2..05c05ac7215ae 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -219,6 +219,19 @@ _generics(ParamS... params) { return {{params...}}; } +///// A conditional synthesizer which generates a generic parameter list. +///// If the 'condition' is false, no generic parameters are created. +//template +//struct ConditionalGenericParamListSynthesizer { +// bool condition; +// VariadicSynthesizerStorage Params; +//}; +//template +//constexpr ConditionalGenericParamListSynthesizer +//_ifGenerics(bool condition, ParamS... params) { +// return {condition, {params...}}; +//} + struct CountGenericParameters { unsigned &Count; @@ -292,6 +305,21 @@ static GenericParamList *synthesizeGenericParamList(SynthesisContext &SC, return paramList; } +//template +//static GenericParamList *synthesizeGenericParamList( +// SynthesisContext &SC, +// const ConditionalGenericParamListSynthesizer ¶ms) { +// if (params.condition) { +// unsigned count = 0; +// params.Params.visit(CountGenericParameters{count}); +// auto paramList = getGenericParams(SC.Context, count); +// SC.GenericParams = paramList; +// return paramList; +// } else { +// return GenericParamList::create(SC.Context, SourceLoc(), {}, SourceLoc()); +// } +//} + namespace { struct CollectGenericParams { SynthesisContext &SC; @@ -341,6 +369,24 @@ synthesizeGenericSignature(SynthesisContext &SC, /*allowInverses=*/false); } +//template +//static GenericSignature +//synthesizeGenericSignature(SynthesisContext &SC, +// const ConditionalGenericParamListSynthesizer &list) { +// CollectGenericParams collector(SC); +// if (list.condition) { +// list.Params.visit(collector); +// +// return buildGenericSignature(SC.Context, +// GenericSignature(), +// std::move(collector.GenericParamTypes), +// std::move(collector.AddedRequirements), +// /*allowInverses=*/false); +// } else { +// return GenericSignature(); +// } +//} + /// Build a builtin function declaration. /// /// This is a "legacy" interface; you should probably use one of @@ -1573,6 +1619,7 @@ static ValueDecl *getCreateTask(ASTContext &ctx, Identifier id) { _existential(_taskExecutor), /*else*/ _executor))), _nil)), + _label("taskName", _defaulted(_optional(_rawPointer), _nil)), _label("operation", _sending(_function(_async(_throws(_thick)), _typeparam(0), _parameters())))), @@ -1597,6 +1644,7 @@ static ValueDecl *getCreateDiscardingTask(ASTContext &ctx, Identifier id) { _existential(_taskExecutor), /*else*/ _executor))), _nil)), + _label("taskName", _defaulted(_optional(_rawPointer), _nil)), _label("operation", _sending(_function(_async(_throws(_thick)), _void, _parameters())))), _tuple(_nativeObject, _rawPointer)); diff --git a/lib/IRGen/GenConcurrency.cpp b/lib/IRGen/GenConcurrency.cpp index f110c45b81755..716d56098cfcd 100644 --- a/lib/IRGen/GenConcurrency.cpp +++ b/lib/IRGen/GenConcurrency.cpp @@ -686,9 +686,9 @@ struct TaskGroupRecordTraits { void initialize(IRGenFunction &IGF, Address recordAddr, Explosion &taskGroup) const { - IGF.Builder.CreateStore( - taskGroup.claimNext(), - IGF.Builder.CreateStructGEP(recordAddr, 1, 2 * IGF.IGM.getPointerSize())); + auto record = + IGF.Builder.CreateStructGEP(recordAddr, 1, 2 * IGF.IGM.getPointerSize()); + IGF.Builder.CreateStore(taskGroup.claimNext(), record); } }; @@ -747,6 +747,29 @@ struct InitialTaskExecutorOwnedRecordTraits { } }; +struct InitialTaskNameRecordTraits { + static StringRef getLabel() { + return "task_name"; + } + static llvm::StructType *getRecordType(IRGenModule &IGM) { + return IGM.SwiftInitialTaskNameTaskOptionRecordTy; + } + static TaskOptionRecordFlags getRecordFlags() { + return TaskOptionRecordFlags(TaskOptionRecordKind::InitialTaskName); + } + static CanType getValueType(ASTContext &ctx) { + return ctx.TheRawPointerType; + } + + // Create 'InitialTaskNameTaskOptionRecord' + void initialize(IRGenFunction &IGF, Address recordAddr, + Explosion &taskName) const { + auto record = + IGF.Builder.CreateStructGEP(recordAddr, 1, 2 * IGF.IGM.getPointerSize()); + IGF.Builder.CreateStore(taskName.claimNext(), record); + } +}; + } // end anonymous namespace static llvm::Value * @@ -783,12 +806,20 @@ maybeAddInitialTaskExecutorOwnedOptionRecord(IRGenFunction &IGF, taskExecutorExistential); } +static llvm::Value * +maybeAddTaskNameOptionRecord(IRGenFunction &IGF, llvm::Value *prevOptions, + OptionalExplosion &taskName) { + return maybeAddOptionRecord(IGF, prevOptions, InitialTaskNameRecordTraits(), + taskName); +} + std::pair irgen::emitTaskCreate(IRGenFunction &IGF, llvm::Value *flags, OptionalExplosion &serialExecutor, OptionalExplosion &taskGroup, OptionalExplosion &taskExecutorUnowned, OptionalExplosion &taskExecutorExistential, + OptionalExplosion &taskName, Explosion &taskFunction, SubstitutionMap subs) { llvm::Value *taskOptions = @@ -825,6 +856,16 @@ irgen::emitTaskCreate(IRGenFunction &IGF, llvm::Value *flags, IGF, taskOptions, taskExecutorExistential); } + // Add an option record for the initial task name, if present. + // + // (lldb) e taskName.value.Values[0] + // (llvm::Value *) $11 = 0x000060000220d1a0 + // (lldb) e taskName.value.Values[0]->dump() + // %16 = extractvalue { i64, i64 } %15, 0 + // (lldb) e taskName.value.Values[1]->dump() + // %17 = extractvalue { i64, i64 } %15, 1 + taskOptions = maybeAddTaskNameOptionRecord(IGF, taskOptions, taskName); + // In embedded Swift, create and pass result type info. taskOptions = maybeAddEmbeddedSwiftResultTypeInfo(IGF, taskOptions, resultType); diff --git a/lib/IRGen/GenConcurrency.h b/lib/IRGen/GenConcurrency.h index ec4bc110118b7..509e948c4c553 100644 --- a/lib/IRGen/GenConcurrency.h +++ b/lib/IRGen/GenConcurrency.h @@ -106,6 +106,7 @@ emitTaskCreate(IRGenFunction &IGF, llvm::Value *flags, OptionalExplosion &taskGroup, OptionalExplosion &taskExecutorUnowned, OptionalExplosion &taskExecutorExistential, + OptionalExplosion &taskName, Explosion &taskFunction, SubstitutionMap subs); diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 6052ea72b0242..21e0db0937f50 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -724,6 +724,11 @@ IRGenModule::IRGenModule(IRGenerator &irgen, SwiftTaskOptionRecordTy, // Base option record SwiftExecutorTy, // Executor }); + SwiftInitialTaskNameTaskOptionRecordTy = + createStructType(*this, "swift.task_name_task_option", { + SwiftTaskOptionRecordTy, // Base option record + Int8PtrTy, // Task name string (char*) + }); SwiftJobTy = createStructType(*this, "swift.job", { RefCountedStructTy, // object header Int8PtrTy, Int8PtrTy, // SchedulerPrivate diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index aed5c3f5e718d..64a41501c5e09 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -829,6 +829,7 @@ class IRGenModule { llvm::StructType *SwiftTaskGroupTaskOptionRecordTy; llvm::StructType *SwiftInitialTaskExecutorUnownedPreferenceTaskOptionRecordTy; llvm::StructType *SwiftInitialTaskExecutorOwnedPreferenceTaskOptionRecordTy; + llvm::StructType *SwiftInitialTaskNameTaskOptionRecordTy; llvm::StructType *SwiftResultTypeInfoTaskOptionRecordTy; llvm::PointerType *SwiftJobPtrTy; llvm::IntegerType *ExecutorFirstTy; diff --git a/lib/IRGen/IRGenSIL.cpp b/lib/IRGen/IRGenSIL.cpp index 8f54b1a579b6f..2f68da9171512 100644 --- a/lib/IRGen/IRGenSIL.cpp +++ b/lib/IRGen/IRGenSIL.cpp @@ -3734,19 +3734,21 @@ static void emitBuiltinStackDealloc(IRGenSILFunction &IGF, static void emitBuiltinCreateAsyncTask(IRGenSILFunction &IGF, swift::BuiltinInst *i) { - assert(i->getOperandValues().size() == 6 && - "createAsyncTask needs 6 operands"); + assert(i->getOperandValues().size() == 7 && + "createAsyncTask needs 7 operands"); auto flags = IGF.getLoweredSingletonExplosion(i->getOperand(0)); auto serialExecutor = IGF.getLoweredOptionalExplosion(i->getOperand(1)); auto taskGroup = IGF.getLoweredOptionalExplosion(i->getOperand(2)); auto taskExecutorUnowned = IGF.getLoweredOptionalExplosion(i->getOperand(3)); auto taskExecutorOwned = IGF.getLoweredOptionalExplosion(i->getOperand(4)); - Explosion taskFunction = IGF.getLoweredExplosion(i->getOperand(5)); + // %11 = enum $Optional, #Optional.some!enumelt, %10 : $UnsafeRawBufferPointer // user: %20 + auto taskName = IGF.getLoweredOptionalExplosion(i->getOperand(5)); + Explosion taskFunction = IGF.getLoweredExplosion(i->getOperand(6)); auto taskAndContext = emitTaskCreate(IGF, flags, serialExecutor, taskGroup, taskExecutorUnowned, taskExecutorOwned, - taskFunction, i->getSubstitutions()); + taskName, taskFunction, i->getSubstitutions()); Explosion out; out.add(taskAndContext.first); out.add(taskAndContext.second); diff --git a/lib/SIL/Verifier/SILVerifier.cpp b/lib/SIL/Verifier/SILVerifier.cpp index 1c196448ae884..799c73a863c3f 100644 --- a/lib/SIL/Verifier/SILVerifier.cpp +++ b/lib/SIL/Verifier/SILVerifier.cpp @@ -2427,8 +2427,8 @@ class SILVerifier : public SILVerifierBase { if (builtinKind == BuiltinValueKind::CreateAsyncTask) { requireType(BI->getType(), _object(_tuple(_nativeObject, _rawPointer)), "result of createAsyncTask"); - require(arguments.size() == 6, - "createAsyncTask expects six arguments"); + require(arguments.size() == 7, + "createAsyncTask expects seven arguments"); requireType(arguments[0]->getType(), _object(_swiftInt), "first argument of createAsyncTask"); requireType(arguments[1]->getType(), _object(_optional(_executor)), @@ -2448,7 +2448,9 @@ class SILVerifier : public SILVerifierBase { _object(_optional(_executor)), "fifth argument of createAsyncTask"); } - auto fnType = requireObjectType(SILFunctionType, arguments[5], + requireType(arguments[5]->getType(), _object(_optional(_rawPointer)), + "sixth argument of createAsyncTask"); + auto fnType = requireObjectType(SILFunctionType, arguments[6], "result of createAsyncTask"); bool haveSending = F.getASTContext().LangOpts.hasFeature(Feature::SendingArgsAndResults); diff --git a/lib/SILGen/SILGenBuiltin.cpp b/lib/SILGen/SILGenBuiltin.cpp index dac0e2fab314a..5e8ee29335033 100644 --- a/lib/SILGen/SILGenBuiltin.cpp +++ b/lib/SILGen/SILGenBuiltin.cpp @@ -1636,6 +1636,14 @@ static ManagedValue emitCreateAsyncTask(SILGenFunction &SGF, SILLocation loc, } }(); + ManagedValue taskName = [&] { + if (options & CreateTaskOptions::OptionalEverything) { + return nextArg().getAsSingleValue(SGF); + } else { + return emitOptionalNone(ctx.TheRawPointerType); + } + }(); + auto functionValue = [&] { // No reabstraction required. if (options & CreateTaskOptions::Discarding) { @@ -1693,6 +1701,7 @@ static ManagedValue emitCreateAsyncTask(SILGenFunction &SGF, SILLocation loc, taskGroup.getUnmanagedValue(), taskExecutorDeprecated.getUnmanagedValue(), taskExecutorConsuming.forward(SGF), + taskName.forward(SGF), functionValue.forward(SGF) }; diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 5c7e1219d78b5..1b80ffd9e1d2c 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -314,6 +314,18 @@ JobPriority swift::swift_task_getCurrentThreadPriority() { #endif } +const char *swift_task_getTaskName(AsyncTask *task) { + if (!task) { + return nullptr; + } + return task->getTaskName(); +} + +const char *swift::swift_task_getCurrentTaskName() { + auto task = swift_task_getCurrent(); + return swift_task_getTaskName(task); +} + // Implemented in Swift to avoid some annoying hard-coding about // SerialExecutor's protocol witness table. We could inline this // with effort, though. diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index efd730c84f5bc..537af7382b908 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -721,6 +721,7 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags, // Collect the options we know about. SerialExecutorRef serialExecutor = SerialExecutorRef::generic(); TaskExecutorRef taskExecutor = TaskExecutorRef::undefined(); + const char* taskName = nullptr; bool taskExecutorIsOwned = false; TaskGroup *group = nullptr; AsyncLet *asyncLet = nullptr; @@ -751,6 +752,12 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags, #endif break; + case TaskOptionRecordKind::InitialTaskName: + taskName = cast(option) + ->getTaskName(); + jobFlags.task_setHasInitialTaskName(true); + break; + case TaskOptionRecordKind::TaskGroup: group = cast(option)->getGroup(); assert(group && "Missing group"); @@ -1155,17 +1162,24 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags, asyncLet_addImpl(task, asyncLet, !hasAsyncLetResultBuffer); } - // Task executor preference - // If the task does not have a specific executor set already via create - // options, and there is a task executor preference set in the parent, we - // inherit it by deep-copying the preference record. if - // (shouldPushTaskExecutorPreferenceRecord || taskExecutor.isDefined()) { - if (jobFlags.task_hasInitialTaskExecutorPreference()) { - // Implementation note: we must do this AFTER `swift_taskGroup_attachChild` - // because the group takes a fast-path when attaching the child record. - assert(jobFlags.task_hasInitialTaskExecutorPreference()); - task->pushInitialTaskExecutorPreference( - taskExecutor, /*owned=*/taskExecutorIsOwned); + // ==== Initial Task records + { + // Task executor preference + // If the task does not have a specific executor set already via create + // options, and there is a task executor preference set in the parent, we + // inherit it by deep-copying the preference record. if + // (shouldPushTaskExecutorPreferenceRecord || taskExecutor.isDefined()) { + if (jobFlags.task_hasInitialTaskExecutorPreference()) { + // Implementation note: we must do this AFTER `swift_taskGroup_attachChild` + // because the group takes a fast-path when attaching the child record. + task->pushInitialTaskExecutorPreference(taskExecutor, + /*owned=*/taskExecutorIsOwned); + } + + // Task name + if (jobFlags.task_hasInitialTaskName()) { + task->pushInitialTaskName(taskName); + } } // If we're supposed to enqueue the task, do so now. diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index e5e93ff70d09c..025c4bfe218ac 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -677,8 +677,7 @@ extension Task where Failure == Never { Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor let (task, _) = Builtin.createTask(flags: flags, - initialSerialExecutor: - builtinSerialExecutor, + initialSerialExecutor: builtinSerialExecutor, operation: operation) self._task = task @@ -686,6 +685,137 @@ extension Task where Failure == Never { #endif } +@available(SwiftStdlib 6.2, *) +extension Task where Failure == Never { +#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + @discardableResult + @_alwaysEmitIntoClient + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") + public init( + name: String? = nil, + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async -> Success + ) { + fatalError("Unavailable in task-to-thread concurrency model.") + } +#elseif $Embedded + @discardableResult + @_alwaysEmitIntoClient + @available(SwiftStdlib 6.2, *) + public init( + name: String? = nil, + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: sending @escaping () async -> Success + ) { + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: true, + inheritContext: true, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task. + let (task, _) = Builtin.createAsyncTask(flags, operation) + + self._task = task + } +#else + /// Runs the given nonthrowing operation asynchronously + /// as part of a new top-level task on behalf of the current actor. + /// + /// Use this function when creating asynchronous work + /// that operates on behalf of the synchronous function that calls it. + /// Like `Task.detached(priority:operation:)`, + /// this function creates a separate, top-level task. + /// Unlike `Task.detached(priority:operation:)`, + /// the task created by `Task.init(priority:operation:)` + /// inherits the priority and actor context of the caller, + /// so the operation is treated more like an asynchronous extension + /// to the synchronous operation. + /// + /// You need to keep a reference to the task + /// if you want to cancel it by calling the `Task.cancel()` method. + /// Discarding your reference to a detached task + /// doesn't implicitly cancel that task, + /// it only makes it impossible for you to explicitly cancel the task. + /// + /// - Parameters: + /// - name: The high-level name given for this task + /// - priority: The priority of the task. + /// Pass `nil` to use the priority from `Task.currentPriority`. + /// - operation: The operation to perform. + @discardableResult + @_alwaysEmitIntoClient + @available(SwiftStdlib 6.2, *) + public init( + name: String? = nil, + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async -> Success + ) { + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: true, + inheritContext: true, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task. + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + let task: Builtin.NativeObject + #if $BuiltinCreateAsyncTaskName + if var name { + task = + name.withUTF8 { nameBytes in + Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskName: nameBytes.baseAddress?._rawValue, + operation: operation).0 + } + } else { + task = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + operation: operation).0 + } + #else + task = Builtin.createTask( + flags: flags, + // unsupported names, so we drop it. + initialSerialExecutor: builtinSerialExecutor, + operation: operation).0 + #endif + + self._task = task + } +#endif +} + +@available(SwiftStdlib 6.2, *) +extension Task where Success == Never, Failure == Error { + + /// Returns the human-readable name of the current task, + /// if it was set during the tasks' creation. + /// + /// Tasks can be named during their creation, which can be helpful to identify + /// unique tasks which may be created at same source locations, for example: + /// + /// func process(items: [Int]) async { + /// await withTaskGroup { group in + /// for item in items { + /// group.addTask(name: "process-\(item)") { + /// await process(item) + /// } + /// } + /// } + /// } + @available(SwiftStdlib 6.2, *) + public static var name: String? { + return _getCurrentTaskNameString() + } +} + @available(SwiftStdlib 5.1, *) extension Task where Failure == Error { #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @@ -1206,6 +1336,20 @@ func _reportUnexpectedExecutor(_ _filenameStart: Builtin.RawPointer, @_silgen_name("swift_task_getCurrentThreadPriority") func _getCurrentThreadPriority() -> Int +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_getCurrentTaskName") +internal func _getCurrentTaskName() -> UnsafePointer? + +@available(SwiftStdlib 6.2, *) +internal func _getCurrentTaskNameString() -> String? { + if let stringPtr = _getCurrentTaskName() { + String(cString: stringPtr) + } else { + nil + } +} + + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.8, *) @usableFromInline diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index 361af35590ca3..46b306fd5c895 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -790,23 +790,31 @@ struct AsyncTask::PrivateStorage { /// Called on the thread that was previously executing the task that we are /// now trying to complete. void complete(AsyncTask *task) { - // If during task creation we created a task preference record; - // we must destroy it here; The record is task-local allocated, - // so we must do so specifically here, before the task-local storage - // elements are destroyed; in order to respect stack-discipline of - // the task-local allocator. - if (task->hasInitialTaskExecutorPreferenceRecord()) { - task->dropInitialTaskExecutorPreferenceRecord(); + // If during task creation we created a any Initial* records, destroy them. + // + // Initial records are task-local allocated, so we must do so specifically + // here, before the task-local storage elements are destroyed; in order to + // respect stack-discipline of the task-local allocator. + { + + if (task->hasInitialTaskExecutorPreferenceRecord()) { + task->dropInitialTaskExecutorPreferenceRecord(); + } + + if (task->hasInitialTaskNameRecord()) { + task->dropInitialTaskNameRecord(); + } } // Drain unlock the task and remove any overrides on thread as a // result of the task auto oldStatus = task->_private()._status().load(std::memory_order_relaxed); while (true) { - // Task is completing, it shouldn't have any records and therefore - // cannot be status record locked. - assert(oldStatus.getInnermostRecord() == NULL); - assert(!oldStatus.isStatusRecordLocked()); + // Task is completing + assert(oldStatus.getInnermostRecord() == NULL && + "Status records should have been removed by this time!"); + assert(!oldStatus.isStatusRecordLocked() && + "Task is completing, cannot be locked anymore!"); assert(oldStatus.isRunning()); diff --git a/stdlib/public/Concurrency/TaskStatus.cpp b/stdlib/public/Concurrency/TaskStatus.cpp index a38e3c9331213..f2288a52d8105 100644 --- a/stdlib/public/Concurrency/TaskStatus.cpp +++ b/stdlib/public/Concurrency/TaskStatus.cpp @@ -741,7 +741,7 @@ void AsyncTask::dropInitialTaskExecutorPreferenceRecord() { SWIFT_TASK_DEBUG_LOG("[InitialTaskExecutorPreference] Drop initial task " "preference record from task:%p", this); - assert(this->hasInitialTaskExecutorPreferenceRecord()); + assert(hasInitialTaskExecutorPreferenceRecord()); HeapObject *executorIdentityToRelease = nullptr; withStatusRecordLock(this, [&](ActiveTaskStatus status) { @@ -781,6 +781,89 @@ void AsyncTask::dropInitialTaskExecutorPreferenceRecord() { swift_unknownObjectRelease(executorIdentityToRelease); } +/******************************************************************************/ +/************************** TASK NAMING ***************************************/ +/******************************************************************************/ + +void AsyncTask::pushInitialTaskName(const char* _taskName) { + assert(_taskName && "Task name must not be null!"); + assert(hasInitialTaskNameRecord() && "Attempted pushing name but task has no initial task name flag!"); + + void *allocation = _swift_task_alloc_specific( + this, sizeof(class TaskNameStatusRecord)); + + // TODO: Copy the string maybe into the same allocation at an offset or retain the swift string? + char* taskNameCopy = reinterpret_cast( + _swift_task_alloc_specific(this, strlen(_taskName) + 1/*null terminator*/)); + (void) strcpy(/*dst=*/taskNameCopy, /*src=*/_taskName); + + auto record = + ::new (allocation) TaskNameStatusRecord(taskNameCopy); + SWIFT_TASK_DEBUG_LOG("[TaskName] Create initial task name record %p " + "for task:%p, name:%s", record, this, taskNameCopy); + + addStatusRecord(this, record, + [&](ActiveTaskStatus oldStatus, ActiveTaskStatus &newStatus) { + return true; // always add the record + }); +} + +void AsyncTask::dropInitialTaskNameRecord() { + if (!hasInitialTaskNameRecord()) { + return; + } + + SWIFT_TASK_DEBUG_LOG("[TaskName] Drop initial task name record for task:%p", this); + withStatusRecordLock(this, [&](ActiveTaskStatus status) { + for (auto cur : status.records()) { + if (cur->getKind() == TaskStatusRecordKind::TaskName) { + auto record = cast(cur); + + SWIFT_TASK_DEBUG_LOG("[TaskName] Drop initial task name record %p " + "for task:%p", record, this); + + removeStatusRecordLocked(status, record); + // Since we first allocated the record, and then the string copy + char *name = const_cast(record->getName()); + _swift_task_dealloc_specific(this, name); + _swift_task_dealloc_specific(this, record); + return; + } + } + + // This drop mirrors the push "initial" name during task creation; + // so it must always reliably always have a preference to drop. + assert(false && "dropInitialTaskNameRecord must be guaranteed to drop " + "the initial task name record if it was present"); + }); +} + +const char* +AsyncTask::getTaskName() { + // We first check the executor preference status flag, in order to avoid + // having to scan through the records of the task checking if there was + // such record. + // + // This is an optimization in order to make the enqueue/run + // path of a task avoid excessive work if a task had many records. + if (!hasInitialTaskNameRecord()) { + return nullptr; + } + + const char *data = nullptr; + withStatusRecordLock(this, [&](ActiveTaskStatus status) { + for (auto record : status.records()) { + if (record->getKind() == TaskStatusRecordKind::TaskName) { + auto nameRecord = cast(record); + data = nameRecord->getName(); + return; + } + } + }); + + return data; +} + /**************************************************************************/ /************************** CHILD TASK MANAGEMENT *************************/ /**************************************************************************/ @@ -906,6 +989,10 @@ static void performCancellationAction(TaskStatusRecord *record) { // Cancellation has no impact on executor preference. case TaskStatusRecordKind::TaskExecutorPreference: break; + + // Cancellation has no impact on task names. + case TaskStatusRecordKind::TaskName: + break; } // Other cases can fall through here and be ignored. @@ -974,14 +1061,6 @@ static void performEscalationAction(TaskStatusRecord *record, return; } - // Cancellation notifications can be ignore. - case TaskStatusRecordKind::CancellationNotification: - return; - - /// Executor preference we can ignore. - case TaskStatusRecordKind::TaskExecutorPreference: - return; - // Escalation notifications need to be called. case TaskStatusRecordKind::EscalationNotification: { auto notification = @@ -1002,6 +1081,15 @@ static void performEscalationAction(TaskStatusRecord *record, // anything to do anyway. case TaskStatusRecordKind::Private_RecordLock: return; + // Cancellation notifications can be ignore. + case TaskStatusRecordKind::CancellationNotification: + return; + /// Executor preference we can ignore. + case TaskStatusRecordKind::TaskExecutorPreference: + return; + /// Task names don't matter to priority escalation. + case TaskStatusRecordKind::TaskName: + return; } // Other cases can fall through here and be ignored. diff --git a/stdlib/public/core/OverloadMacros.swift b/stdlib/public/core/OverloadMacros.swift new file mode 100644 index 0000000000000..59db085c87a63 --- /dev/null +++ b/stdlib/public/core/OverloadMacros.swift @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2017 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 $Macros && hasAttribute(attached) + + +/* + +@_CompatibilityOverload(added: debugName, in: .swift_9999) +public init( + debugName: String? = nil, + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async -> Success +) +*/ +@freestanding(declaration) +internal macro _CompatibilityOverload( + added: String, + in: +) = Builtin.ErrorMacro + +internal enum CompatOverload: String { + case swift_5_1 = "@available(SwiftStdlib 5.1, *)" + case swift_5_2 = "@available(SwiftStdlib 5.2, *)" + case swift_5_3 = "@available(SwiftStdlib 5.3, *)" + case swift_5_4 = "@available(SwiftStdlib 5.4, *)" + case swift_5_5 = "@available(SwiftStdlib 5.5, *)" + case swift_5_6 = "@available(SwiftStdlib 5.6, *)" + case swift_5_7 = "@available(SwiftStdlib 5.7, *)" + case swift_5_8 = "@available(SwiftStdlib 5.8, *)" + case swift_5_9 = "@available(SwiftStdlib 5.9, *)" + case swift_5_10 = "@available(SwiftStdlib 5.10, *)" + case swift_6_0 = "@available(SwiftStdlib 6.0, *)" + case swift_9999 = "@available(SwiftStdlib 9999, *)" +} + +#endif // $Macros && hasAttribute(attached) diff --git a/stdlib/toolchain/Compatibility56/include/Concurrency/TaskPrivate.h b/stdlib/toolchain/Compatibility56/include/Concurrency/TaskPrivate.h index ed453bfb7d6f4..186df38da7ac3 100644 --- a/stdlib/toolchain/Compatibility56/include/Concurrency/TaskPrivate.h +++ b/stdlib/toolchain/Compatibility56/include/Concurrency/TaskPrivate.h @@ -29,7 +29,7 @@ namespace swift { -#if 0 +#if 1 using ThreadID = decltype(pthread_self()); inline ThreadID _swift_get_thread_id() { diff --git a/test/Concurrency/Runtime/async_task_naming.swift b/test/Concurrency/Runtime/async_task_naming.swift new file mode 100644 index 0000000000000..25394272d1e31 --- /dev/null +++ b/test/Concurrency/Runtime/async_task_naming.swift @@ -0,0 +1,34 @@ +// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency + +// REQUIRES: concurrency_runtime +// UNSUPPORTED: back_deployment_runtime + +func test() async { + // CHECK: Task.name = NONE + print("Task.name = \(Task.name ?? "NONE")") + + _ = await Task(name: "Caplin the Task") { + // CHECK: Task.name = Caplin the Task + print("Task.name = \(Task.name ?? "NONE")") + return 12 + }.value + +// _ = await Task.detached(name: "Caplin the Detached Task") { +// // CHECK: Task.name = Caplin the Detached Task +// print("Task.name = \(Task.name ?? "NONE")") +// return 12 +// }.value + +// _ = await withTaskGroup(of: Int.self) { g in +// g.addTask(name: "Caplin the TaskGroup Task") { +// // CHECK: Task.name = Caplin the TaskGroup Task +// print("Task.name = \(Task.name ?? "NONE")") +// return 12 +// } +// } +} + +await test() diff --git a/test/Concurrency/async_task_locals_basic_warnings.swift b/test/Concurrency/async_task_locals_basic_warnings.swift index d82f3a5b6c62f..7e751d038629d 100644 --- a/test/Concurrency/async_task_locals_basic_warnings.swift +++ b/test/Concurrency/async_task_locals_basic_warnings.swift @@ -3,6 +3,7 @@ // RUN: %target-swift-frontend -I %t -plugin-path %swift-plugin-dir -target %target-swift-5.1-abi-triple -strict-concurrency=complete -parse-as-library %s -emit-sil -o /dev/null -verify // RUN: %target-swift-frontend -I %t -plugin-path %swift-plugin-dir -target %target-swift-5.1-abi-triple -strict-concurrency=complete -parse-as-library %s -emit-sil -o /dev/null -verify -enable-upcoming-feature RegionBasedIsolation +// RUN: %target-swift-frontend -I %t -plugin-path %swift-plugin-dir -disable-availability-checking -swift-version 6 -parse-as-library %s -emit-sil -o /dev/null -verify // REQUIRES: concurrency // REQUIRES: swift_feature_RegionBasedIsolation From 0238f06b050ab71de33d02b4a8edaeeab589a24a Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 18 Feb 2025 10:19:36 +0900 Subject: [PATCH 2/9] [Tracing] Include task name, isDiscarding, hasExPreference in tracing --- stdlib/public/Concurrency/Task.cpp | 6 ++++-- stdlib/public/Concurrency/TracingSignpost.h | 10 +++++++--- stdlib/public/Concurrency/TracingStubs.h | 7 ++++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index 537af7382b908..66f0e05fdb288 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -1131,12 +1131,14 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags, } // FIXME: add discarding flag - // FIXME: add task executor concurrency::trace::task_create( task, parent, group, asyncLet, static_cast(task->Flags.getPriority()), task->Flags.task_isChildTask(), task->Flags.task_isFuture(), - task->Flags.task_isGroupChildTask(), task->Flags.task_isAsyncLetTask()); + task->Flags.task_isGroupChildTask(), task->Flags.task_isAsyncLetTask(), + taskCreateFlags.isDiscardingTask(), + task->Flags.task_hasInitialTaskExecutorPreference(), + taskName); // Attach to the group, if needed. if (group) { diff --git a/stdlib/public/Concurrency/TracingSignpost.h b/stdlib/public/Concurrency/TracingSignpost.h index af53cb9fa9225..09ac64b69f86d 100644 --- a/stdlib/public/Concurrency/TracingSignpost.h +++ b/stdlib/public/Concurrency/TracingSignpost.h @@ -182,7 +182,9 @@ inline void actor_note_job_queue(HeapObject *actor, Job *first, inline void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, AsyncLet *asyncLet, uint8_t jobPriority, bool isChildTask, bool isFuture, bool isGroupChildTask, - bool isAsyncLetTask) { + bool isAsyncLetTask, bool isDiscardingTask, + bool hasInitialTaskExecutorPreference, + const char* taskName) { ENSURE_LOGS(); auto id = os_signpost_id_make_with_pointer(TaskLog, task); auto parentID = parent ? parent->getTaskId() : 0; @@ -191,10 +193,12 @@ inline void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, "task=%" PRIx64 " resumefn=%p jobPriority=%u isChildTask=%{bool}d, isFuture=%{bool}d " "isGroupChildTask=%{bool}d isAsyncLetTask=%{bool}d parent=%" PRIx64 - " group=%p asyncLet=%p", + " group=%p asyncLet=%p " + "isDiscardingTask=%{bool}d hasInitialTaskExecutorPreference=%{bool}d " + "taskName=%{public}s", task->getTaskId(), task->getResumeFunctionForLogging(true), jobPriority, isChildTask, isFuture, isGroupChildTask, isAsyncLetTask, parentID, group, - asyncLet); + asyncLet, isDiscardingTask, hasInitialTaskExecutorPreference, taskName); } inline void task_destroy(AsyncTask *task) { diff --git a/stdlib/public/Concurrency/TracingStubs.h b/stdlib/public/Concurrency/TracingStubs.h index 5464565937935..179434e926be7 100644 --- a/stdlib/public/Concurrency/TracingStubs.h +++ b/stdlib/public/Concurrency/TracingStubs.h @@ -44,12 +44,13 @@ inline void actor_note_job_queue(HeapObject *actor, Job *first, inline void task_create(AsyncTask *task, AsyncTask *parent, TaskGroup *group, AsyncLet *asyncLet, uint8_t jobPriority, bool isChildTask, bool isFuture, bool isGroupChildTask, - bool isAsyncLetTask) {} + bool isAsyncLetTask, bool isDiscardingTask, + bool hasInitialTaskExecutorPreference, + const char* taskName) {} inline void task_destroy(AsyncTask *task) {} -inline void task_wait(AsyncTask *task, AsyncTask *waitingOn, uintptr_t status) { -} +inline void task_wait(AsyncTask *task, AsyncTask *waitingOn, uintptr_t status) {} inline void task_resume(AsyncTask *task) {} From 1ae758c7829a3a9d05c86e04a5cff9a86e5b2db3 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 18 Feb 2025 11:05:54 +0900 Subject: [PATCH 3/9] [Concurrency] TaskGroup with task names --- stdlib/public/Concurrency/Task.swift | 20 +++---- stdlib/public/Concurrency/TaskGroup.swift | 60 +++++++++++++++++++ .../Runtime/async_task_naming.swift | 14 ++++- 3 files changed, 80 insertions(+), 14 deletions(-) diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 025c4bfe218ac..396587aa195b2 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -704,6 +704,7 @@ extension Task where Failure == Never { @available(SwiftStdlib 6.2, *) public init( name: String? = nil, + // TaskExecutor is unavailable in embedded priority: TaskPriority? = nil, @_inheritActorContext @_implicitSelfCapture operation: sending @escaping () async -> Success ) { @@ -762,7 +763,8 @@ extension Task where Failure == Never { // Create the asynchronous task. let builtinSerialExecutor = Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - let task: Builtin.NativeObject + + var task: Builtin.NativeObject? #if $BuiltinCreateAsyncTaskName if var name { task = @@ -773,21 +775,17 @@ extension Task where Failure == Never { taskName: nameBytes.baseAddress?._rawValue, operation: operation).0 } - } else { - task = Builtin.createTask( - flags: flags, - initialSerialExecutor: builtinSerialExecutor, - operation: operation).0 } - #else - task = Builtin.createTask( + #endif + if task == nil { + // either no task name was set, or names are unsupported + task = Builtin.createTask( flags: flags, - // unsupported names, so we drop it. initialSerialExecutor: builtinSerialExecutor, operation: operation).0 - #endif + } - self._task = task + self._task = task! } #endif } diff --git a/stdlib/public/Concurrency/TaskGroup.swift b/stdlib/public/Concurrency/TaskGroup.swift index a6fedaa3e2aae..3b29089074285 100644 --- a/stdlib/public/Concurrency/TaskGroup.swift +++ b/stdlib/public/Concurrency/TaskGroup.swift @@ -362,12 +362,72 @@ public struct TaskGroup { // Create the task in this group. let builtinSerialExecutor = Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + _ = Builtin.createTask(flags: flags, initialSerialExecutor: builtinSerialExecutor, taskGroup: _group, operation: operation) } + /// Adds a child task to the group. + /// + /// - Parameters: + /// - name: Human readable name of this task. + /// - priority: The priority of the operation task. + /// Omit this parameter or pass `.unspecified` + /// to set the child task's priority to the priority of the group. + /// - operation: The operation to execute as part of the task group. + @_alwaysEmitIntoClient + public mutating func addTask( + name: String? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> ChildTaskResult + ) { + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + let enqueueJob = false + #else + let enqueueJob = true + #endif + + let flags = taskCreateFlags( + priority: priority, isChildTask: true, copyTaskLocals: false, + inheritContext: false, enqueueJob: enqueueJob, + addPendingGroupTaskUnconditionally: true, + isDiscardingTask: false + ) + + // Create the task in this group. + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + var task: Builtin.NativeObject? + #if $BuiltinCreateAsyncTaskName + if var name { + task = + name.withUTF8 { nameBytes in + Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + taskName: nameBytes.baseAddress?._rawValue, + operation: operation).0 + } + } + #endif + + if task == nil { + // either no task name was set, or names are unsupported + task = Builtin.createTask( + flags: flags, + // unsupported names, so we drop it. + initialSerialExecutor: builtinSerialExecutor, + operation: operation).0 + } + + // task was enqueued to the group, no need to store the 'task' ref itself + assert(task != nil, "Expected task to be created!") + } + /// Adds a child task to the group, unless the group has been canceled. /// /// - Parameters: diff --git a/test/Concurrency/Runtime/async_task_naming.swift b/test/Concurrency/Runtime/async_task_naming.swift index 25394272d1e31..62dc190d70a94 100644 --- a/test/Concurrency/Runtime/async_task_naming.swift +++ b/test/Concurrency/Runtime/async_task_naming.swift @@ -22,9 +22,17 @@ func test() async { // return 12 // }.value -// _ = await withTaskGroup(of: Int.self) { g in -// g.addTask(name: "Caplin the TaskGroup Task") { -// // CHECK: Task.name = Caplin the TaskGroup Task + _ = await withTaskGroup(of: Int.self) { g in + g.addTask(name: "Caplin the TaskGroup Task") { + // CHECK: Task.name = Caplin the TaskGroup Task + print("Task.name = \(Task.name ?? "NONE")") + return 12 + } + } + +// _ = await withThrowingTaskGroup(of: Int.self) { g in +// g.addTask(name: "Caplin the ThrowingTaskGroup Task") { +// // CHECK: Task.name = Caplin the ThrowingTaskGroup Task // print("Task.name = \(Task.name ?? "NONE")") // return 12 // } From 171954ae8077aa210726ff2321c9dc5cb1a90c4a Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 18 Feb 2025 11:05:54 +0900 Subject: [PATCH 4/9] [Concurrency] TaskGroup.addTaskUnlessCancelled with names --- stdlib/public/Concurrency/TaskGroup.swift | 107 +++++++++++++----- .../Runtime/async_task_naming.swift | 5 + 2 files changed, 83 insertions(+), 29 deletions(-) diff --git a/stdlib/public/Concurrency/TaskGroup.swift b/stdlib/public/Concurrency/TaskGroup.swift index 3b29089074285..71fda69232d36 100644 --- a/stdlib/public/Concurrency/TaskGroup.swift +++ b/stdlib/public/Concurrency/TaskGroup.swift @@ -14,6 +14,16 @@ import Swift // ==== TaskGroup -------------------------------------------------------------- +// In the task-to-thread model we don't enqueue tasks created using addTask +// to the global pool, but will run them inline instead. +#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +@usableFromInline +let enqueueJobOnCreate = false +#else +@usableFromInline +let enqueueJobOnCreate = true +#endif + /// Starts a new scope that can contain a dynamic number of child tasks. /// /// A group waits for all of its child tasks @@ -159,9 +169,9 @@ public func _unsafeInheritExecutor_withTaskGroup( /// by calling the `cancelAll()` method on the task group, /// or by canceling the task in which the group is running. /// -/// If you call `addTask(priority:operation:)` to create a new task in a canceled group, +/// If you call `addTask(name:priority:operation:)` to create a new task in a canceled group, /// that task is immediately canceled after creation. -/// Alternatively, you can call `addTaskUnlessCancelled(priority:operation:)`, +/// Alternatively, you can call `addTaskUnlessCancelled(name:priority:operation:)`, /// which doesn't create the task if the group has already been canceled. /// Choosing between these two functions /// lets you control how to react to cancellation within a group: @@ -307,8 +317,8 @@ public func _unsafeInheritExecutor_withThrowingTaskGroup { priority: TaskPriority? = nil, operation: sending @escaping @isolated(any) () async -> ChildTaskResult ) { -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY let flags = taskCreateFlags( priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: false, + inheritContext: false, enqueueJob: enqueueJobOnCreate, addPendingGroupTaskUnconditionally: true, isDiscardingTask: false ) -#else - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: false) -#endif // Create the task in this group. let builtinSerialExecutor = @@ -383,15 +385,9 @@ public struct TaskGroup { priority: TaskPriority? = nil, operation: sending @escaping @isolated(any) () async -> ChildTaskResult ) { - #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - let enqueueJob = false - #else - let enqueueJob = true - #endif - let flags = taskCreateFlags( priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: enqueueJob, + inheritContext: false, enqueueJob: enqueueJobOnCreate, addPendingGroupTaskUnconditionally: true, isDiscardingTask: false ) @@ -448,19 +444,11 @@ public struct TaskGroup { // the group is cancelled and is not accepting any new work return false } -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY let flags = taskCreateFlags( priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: false, + inheritContext: false, enqueueJob: enqueueJobOnCreate, addPendingGroupTaskUnconditionally: false, isDiscardingTask: false) -#else - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, - isDiscardingTask: false) -#endif // Create the task in this group. let builtinSerialExecutor = @@ -473,6 +461,67 @@ public struct TaskGroup { return true } + /// Adds a child task to the group, unless the group has been canceled. + /// + /// - Parameters: + /// - name: Human readable name of the task. + /// - priority: The priority of the operation task. + /// Omit this parameter or pass `.unspecified` + /// to set the child task's priority to the priority of the group. + /// - operation: The operation to execute as part of the task group. + /// - Returns: `true` if the child task was added to the group; + /// otherwise `false`. + @_alwaysEmitIntoClient + public mutating func addTaskUnlessCancelled( + name: String? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> ChildTaskResult + ) -> Bool { + let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) + + guard canAdd else { + // the group is cancelled and is not accepting any new work + return false + } + let flags = taskCreateFlags( + priority: priority, isChildTask: true, copyTaskLocals: false, + inheritContext: false, enqueueJob: enqueueJobOnCreate, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + var task: Builtin.NativeObject? + #if $BuiltinCreateAsyncTaskName + if var name { + task = + name.withUTF8 { nameBytes in + Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + taskName: nameBytes.baseAddress?._rawValue, + operation: operation).0 + } + } + #endif + + if task == nil { + // either no task name was set, or names are unsupported + task = Builtin.createTask( + flags: flags, + // unsupported names, so we drop it. + initialSerialExecutor: builtinSerialExecutor, + operation: operation).0 + } + + // task was enqueued to the group, no need to store the 'task' ref itself + assert(task != nil, "Expected task to be created!") + + return true + } + #else // if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.7, *) @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") diff --git a/test/Concurrency/Runtime/async_task_naming.swift b/test/Concurrency/Runtime/async_task_naming.swift index 62dc190d70a94..ad93f04f98883 100644 --- a/test/Concurrency/Runtime/async_task_naming.swift +++ b/test/Concurrency/Runtime/async_task_naming.swift @@ -28,6 +28,11 @@ func test() async { print("Task.name = \(Task.name ?? "NONE")") return 12 } + g.addTaskUnlessCancelled(name: "Caplin the TaskGroup Task (unless cancelled)") { + // CHECK: Task.name = Caplin the TaskGroup Task (unless cancelled) + print("Task.name = \(Task.name ?? "NONE")") + return 12 + } } // _ = await withThrowingTaskGroup(of: Int.self) { g in From 45b639a3c15813205ceb3165b62aa745f9a4bebc Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 18 Feb 2025 12:25:48 +0900 Subject: [PATCH 5/9] [Concurrency] Unstructured, throwing and detached tasks with names --- stdlib/public/Concurrency/Task.swift | 315 ++++++++++++++++-- .../Runtime/async_task_naming.swift | 26 +- 2 files changed, 312 insertions(+), 29 deletions(-) diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 396587aa195b2..06ef5fa797a87 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -790,30 +790,6 @@ extension Task where Failure == Never { #endif } -@available(SwiftStdlib 6.2, *) -extension Task where Success == Never, Failure == Error { - - /// Returns the human-readable name of the current task, - /// if it was set during the tasks' creation. - /// - /// Tasks can be named during their creation, which can be helpful to identify - /// unique tasks which may be created at same source locations, for example: - /// - /// func process(items: [Int]) async { - /// await withTaskGroup { group in - /// for item in items { - /// group.addTask(name: "process-\(item)") { - /// await process(item) - /// } - /// } - /// } - /// } - @available(SwiftStdlib 6.2, *) - public static var name: String? { - return _getCurrentTaskNameString() - } -} - @available(SwiftStdlib 5.1, *) extension Task where Failure == Error { #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @@ -877,6 +853,112 @@ extension Task where Failure == Error { #endif } + +@available(SwiftStdlib 6.2, *) +extension Task where Failure == Error { + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + @discardableResult + @_alwaysEmitIntoClient + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") + public init( + name: String? = nil, + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async throws -> Success +) { + fatalError("Unavailable in task-to-thread concurrency model.") +} + #elseif $Embedded + @discardableResult + @_alwaysEmitIntoClient + @available(SwiftStdlib 6.2, *) + public init( + name: String? = nil, + // TaskExecutor is unavailable in embedded + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: sending @escaping () async throws -> Success +) { + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: true, + inheritContext: true, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task. + let (task, _) = Builtin.createAsyncTask(flags, operation) + +self._task = task +} + #else + /// Runs the given nonthrowing operation asynchronously + /// as part of a new top-level task on behalf of the current actor. + /// + /// Use this function when creating asynchronous work + /// that operates on behalf of the synchronous function that calls it. + /// Like `Task.detached(priority:operation:)`, + /// this function creates a separate, top-level task. + /// Unlike `Task.detached(priority:operation:)`, + /// the task created by `Task.init(priority:operation:)` + /// inherits the priority and actor context of the caller, + /// so the operation is treated more like an asynchronous extension + /// to the synchronous operation. + /// + /// You need to keep a reference to the task + /// if you want to cancel it by calling the `Task.cancel()` method. + /// Discarding your reference to a detached task + /// doesn't implicitly cancel that task, + /// it only makes it impossible for you to explicitly cancel the task. + /// + /// - Parameters: + /// - name: The high-level name given for this task + /// - priority: The priority of the task. + /// Pass `nil` to use the priority from `Task.currentPriority`. + /// - operation: The operation to perform. + @discardableResult + @_alwaysEmitIntoClient + @available(SwiftStdlib 6.2, *) + public init( + name: String? = nil, + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async throws -> Success +) { + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: true, + inheritContext: true, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task. + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + var task: Builtin.NativeObject? + #if $BuiltinCreateAsyncTaskName +if var name { + task = + name.withUTF8 { nameBytes in + Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskName: nameBytes.baseAddress?._rawValue, + operation: operation).0 + } +} + #endif +if task == nil { + // either no task name was set, or names are unsupported + task = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + operation: operation).0 +} + +self._task = task! +} + #endif +} + // ==== Detached Tasks --------------------------------------------------------- @available(SwiftStdlib 5.1, *) @@ -939,6 +1021,85 @@ extension Task where Failure == Never { #endif } +@available(SwiftStdlib 6.2, *) +extension Task where Failure == Never { +#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + @discardableResult + @_alwaysEmitIntoClient + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") + public static func detached( + name: String? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> Success + ) -> Task { + fatalError("Unavailable in task-to-thread concurrency model") + } +#else + /// Runs the given nonthrowing operation asynchronously + /// as part of a new top-level task. + /// + /// Don't use a detached task if it's possible + /// to model the operation using structured concurrency features like child tasks. + /// Child tasks inherit the parent task's priority and task-local storage, + /// and canceling a parent task automatically cancels all of its child tasks. + /// You need to handle these considerations manually with a detached task. + /// + /// You need to keep a reference to the detached task + /// if you want to cancel it by calling the `Task.cancel()` method. + /// Discarding your reference to a detached task + /// doesn't implicitly cancel that task, + /// it only makes it impossible for you to explicitly cancel the task. + /// + /// - Parameters: + /// - name: Human readable name of the task. + /// - priority: The priority of the task. + /// - operation: The operation to perform. + /// + /// - Returns: A reference to the task. + @discardableResult + @_alwaysEmitIntoClient + public static func detached( + name: String? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> Success + ) -> Task { + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task. + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + var task: Builtin.NativeObject? + #if $BuiltinCreateAsyncTaskName + if var name { + task = + name.withUTF8 { nameBytes in + Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskName: nameBytes.baseAddress?._rawValue, + operation: operation).0 + } + } + #endif + if task == nil { + // either no task name was set, or names are unsupported + task = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + operation: operation).0 + } + + return Task(task!) + } +#endif +} + @available(SwiftStdlib 5.1, *) extension Task where Failure == Error { #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @@ -1001,6 +1162,112 @@ extension Task where Failure == Error { #endif } +@available(SwiftStdlib 6.2, *) +extension Task where Failure == Error { +#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + @discardableResult + @_alwaysEmitIntoClient + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") + public static func detached( + name: String? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async throws -> Success + ) -> Task { + fatalError("Unavailable in task-to-thread concurrency model") + } +#else + /// Runs the given throwing operation asynchronously + /// as part of a new top-level task. + /// + /// If the operation throws an error, this method propagates that error. + /// + /// Don't use a detached task if it's possible + /// to model the operation using structured concurrency features like child tasks. + /// Child tasks inherit the parent task's priority and task-local storage, + /// and canceling a parent task automatically cancels all of its child tasks. + /// You need to handle these considerations manually with a detached task. + /// + /// You need to keep a reference to the detached task + /// if you want to cancel it by calling the `Task.cancel()` method. + /// Discarding your reference to a detached task + /// doesn't implicitly cancel that task, + /// it only makes it impossible for you to explicitly cancel the task. + /// + /// - Parameters: + /// - priority: The priority of the task. + /// - operation: The operation to perform. + /// + /// - Returns: A reference to the task. + @discardableResult + @_alwaysEmitIntoClient + public static func detached( + name: String? = nil, + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async throws -> Success + ) -> Task { + // Set up the job flags for a new task. + let flags = taskCreateFlags( + priority: priority, isChildTask: false, copyTaskLocals: false, + inheritContext: false, enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false) + + // Create the asynchronous task future. + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + var task: Builtin.NativeObject? + #if $BuiltinCreateAsyncTaskName + if var name { + task = + name.withUTF8 { nameBytes in + Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskName: nameBytes.baseAddress?._rawValue, + operation: operation).0 + } + } + #endif + if task == nil { + // either no task name was set, or names are unsupported + task = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + operation: operation).0 + } + + return Task(task!) + } +#endif +} + +// ==== Task Name -------------------------------------------------------------- + +@available(SwiftStdlib 6.2, *) +extension Task where Success == Never, Failure == Never { + + /// Returns the human-readable name of the current task, + /// if it was set during the tasks' creation. + /// + /// Tasks can be named during their creation, which can be helpful to identify + /// unique tasks which may be created at same source locations, for example: + /// + /// func process(items: [Int]) async { + /// await withTaskGroup { group in + /// for item in items { + /// group.addTask(name: "process-\(item)") { + /// await process(item) + /// } + /// } + /// } + /// } + @available(SwiftStdlib 6.2, *) + public static var name: String? { + return _getCurrentTaskNameString() + } +} + // ==== Voluntary Suspension ----------------------------------------------------- @available(SwiftStdlib 5.1, *) diff --git a/test/Concurrency/Runtime/async_task_naming.swift b/test/Concurrency/Runtime/async_task_naming.swift index ad93f04f98883..bb3aac5114b65 100644 --- a/test/Concurrency/Runtime/async_task_naming.swift +++ b/test/Concurrency/Runtime/async_task_naming.swift @@ -6,6 +6,8 @@ // REQUIRES: concurrency_runtime // UNSUPPORTED: back_deployment_runtime +func pretendToThrow() throws {} + func test() async { // CHECK: Task.name = NONE print("Task.name = \(Task.name ?? "NONE")") @@ -16,11 +18,25 @@ func test() async { return 12 }.value -// _ = await Task.detached(name: "Caplin the Detached Task") { -// // CHECK: Task.name = Caplin the Detached Task -// print("Task.name = \(Task.name ?? "NONE")") -// return 12 -// }.value + _ = try? await Task(name: "Caplin the Throwing Task") { + // CHECK: Task.name = Caplin the Throwing Task + print("Task.name = \(Task.name ?? "NONE")") + try pretendToThrow() + return 12 + }.value + + _ = await Task.detached(name: "Caplin the Detached Task") { + // CHECK: Task.name = Caplin the Detached Task + print("Task.name = \(Task.name ?? "NONE")") + return 12 + }.value + + _ = try? await Task.detached(name: "Caplin the Detached Throwing Task") { + // CHECK: Task.name = Caplin the Detached Task + print("Task.name = \(Task.name ?? "NONE")") + try pretendToThrow() + return 12 + }.value _ = await withTaskGroup(of: Int.self) { g in g.addTask(name: "Caplin the TaskGroup Task") { From efbbc2c4453f74f45ee598967a8dc51708afc8f5 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 19 Feb 2025 12:17:29 +0900 Subject: [PATCH 6/9] [Concurrency] introduce gyb file to generate addTask implementations --- Runtimes/Core/Concurrency/CMakeLists.txt | 7 +- lib/AST/Builtins.cpp | 46 -- lib/IRGen/GenConcurrency.cpp | 7 - stdlib/public/Concurrency/CMakeLists.txt | 8 +- .../Concurrency/DiscardingTaskGroup.swift | 203 -------- .../SourceCompatibilityShims.swift | 215 +------- .../Concurrency/Task+TaskExecutor.swift | 2 +- stdlib/public/Concurrency/Task.cpp | 5 +- .../Concurrency/TaskGroup+Embedded.swift | 277 +++++++++++ .../Concurrency/TaskGroup+TaskExecutor.swift | 468 ------------------ .../Concurrency/TaskGroup+addTask.swift.gyb | 311 ++++++++++++ stdlib/public/Concurrency/TaskGroup.swift | 417 +--------------- stdlib/public/Concurrency/TaskPrivate.h | 8 +- stdlib/public/core/OverloadMacros.swift | 46 -- .../include/Concurrency/TaskPrivate.h | 2 +- .../Runtime/async_task_naming.swift | 76 ++- test/Concurrency/async_main.swift | 3 +- test/IRGen/async/builtins.sil | 21 +- test/SILGen/async_builtins.swift | 53 +- test/abi/macOS/arm64/concurrency.swift | 5 + test/abi/macOS/x86_64/concurrency.swift | 5 + ... => concurrency-discardingtaskgroup.swift} | 0 ...ndencies-concurrency-custom-executor.swift | 1 + ...dencies-concurrency-custom-executor2.swift | 1 + test/embedded/dependencies-concurrency.swift | 1 + test/embedded/dependencies-concurrency2.swift | 1 + 26 files changed, 768 insertions(+), 1421 deletions(-) create mode 100644 stdlib/public/Concurrency/TaskGroup+Embedded.swift delete mode 100644 stdlib/public/Concurrency/TaskGroup+TaskExecutor.swift create mode 100644 stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb delete mode 100644 stdlib/public/core/OverloadMacros.swift rename test/embedded/{concurrency-taskgroup2.swift => concurrency-discardingtaskgroup.swift} (100%) diff --git a/Runtimes/Core/Concurrency/CMakeLists.txt b/Runtimes/Core/Concurrency/CMakeLists.txt index ab7af17f61f4d..5ae60448943f4 100644 --- a/Runtimes/Core/Concurrency/CMakeLists.txt +++ b/Runtimes/Core/Concurrency/CMakeLists.txt @@ -1,5 +1,7 @@ add_subdirectory(InternalShims) +gyb_expand(TaskGroup+addTask.swift.gyb TaskGroup+addTask.swift) + add_library(swift_Concurrency Actor.cpp AsyncLet.cpp @@ -77,10 +79,11 @@ add_library(swift_Concurrency Task+TaskExecutor.swift TaskCancellation.swift TaskGroup.swift - TaskGroup+TaskExecutor.swift TaskLocal.swift TaskSleep.swift - TaskSleepDuration.swift) + TaskSleepDuration.swift + "${CMAKE_CURRENT_BINARY_DIR}/TaskGroup+addTask.swift") + include(${SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR}.cmake) target_compile_definitions(swift_Concurrency PRIVATE $<$:-DSWIFT_TARGET_LIBRARY_NAME=swift_Concurrency> diff --git a/lib/AST/Builtins.cpp b/lib/AST/Builtins.cpp index 05c05ac7215ae..53712e47a9b74 100644 --- a/lib/AST/Builtins.cpp +++ b/lib/AST/Builtins.cpp @@ -219,19 +219,6 @@ _generics(ParamS... params) { return {{params...}}; } -///// A conditional synthesizer which generates a generic parameter list. -///// If the 'condition' is false, no generic parameters are created. -//template -//struct ConditionalGenericParamListSynthesizer { -// bool condition; -// VariadicSynthesizerStorage Params; -//}; -//template -//constexpr ConditionalGenericParamListSynthesizer -//_ifGenerics(bool condition, ParamS... params) { -// return {condition, {params...}}; -//} - struct CountGenericParameters { unsigned &Count; @@ -305,21 +292,6 @@ static GenericParamList *synthesizeGenericParamList(SynthesisContext &SC, return paramList; } -//template -//static GenericParamList *synthesizeGenericParamList( -// SynthesisContext &SC, -// const ConditionalGenericParamListSynthesizer ¶ms) { -// if (params.condition) { -// unsigned count = 0; -// params.Params.visit(CountGenericParameters{count}); -// auto paramList = getGenericParams(SC.Context, count); -// SC.GenericParams = paramList; -// return paramList; -// } else { -// return GenericParamList::create(SC.Context, SourceLoc(), {}, SourceLoc()); -// } -//} - namespace { struct CollectGenericParams { SynthesisContext &SC; @@ -369,24 +341,6 @@ synthesizeGenericSignature(SynthesisContext &SC, /*allowInverses=*/false); } -//template -//static GenericSignature -//synthesizeGenericSignature(SynthesisContext &SC, -// const ConditionalGenericParamListSynthesizer &list) { -// CollectGenericParams collector(SC); -// if (list.condition) { -// list.Params.visit(collector); -// -// return buildGenericSignature(SC.Context, -// GenericSignature(), -// std::move(collector.GenericParamTypes), -// std::move(collector.AddedRequirements), -// /*allowInverses=*/false); -// } else { -// return GenericSignature(); -// } -//} - /// Build a builtin function declaration. /// /// This is a "legacy" interface; you should probably use one of diff --git a/lib/IRGen/GenConcurrency.cpp b/lib/IRGen/GenConcurrency.cpp index 716d56098cfcd..689688abba285 100644 --- a/lib/IRGen/GenConcurrency.cpp +++ b/lib/IRGen/GenConcurrency.cpp @@ -857,13 +857,6 @@ irgen::emitTaskCreate(IRGenFunction &IGF, llvm::Value *flags, } // Add an option record for the initial task name, if present. - // - // (lldb) e taskName.value.Values[0] - // (llvm::Value *) $11 = 0x000060000220d1a0 - // (lldb) e taskName.value.Values[0]->dump() - // %16 = extractvalue { i64, i64 } %15, 0 - // (lldb) e taskName.value.Values[1]->dump() - // %17 = extractvalue { i64, i64 } %15, 1 taskOptions = maybeAddTaskNameOptionRecord(IGF, taskOptions, taskName); // In embedded Swift, create and pass result type info. diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index a38f4230192ad..bc45f951e9be9 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -144,7 +144,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES Task+TaskExecutor.swift TaskCancellation.swift TaskGroup.swift - TaskGroup+TaskExecutor.swift + TaskGroup+Embedded.swift DiscardingTaskGroup.swift TaskLocal.swift TaskSleep.swift @@ -181,6 +181,9 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I ${SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES} + GYB_SOURCES + TaskGroup+addTask.swift.gyb + SWIFT_MODULE_DEPENDS_ANDROID Android SWIFT_MODULE_DEPENDS_LINUX Glibc SWIFT_MODULE_DEPENDS_LINUX_STATIC Musl @@ -274,6 +277,9 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC ${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES} + # FIXME: Need to include the TaskGroup+addTask.swift.gyb here, + # but there's trouble with that: Unknown architecture: x86_64-apple-macos + SWIFT_COMPILE_FLAGS ${extra_swift_compile_flags} -enable-experimental-feature Embedded -parse-stdlib -DSWIFT_CONCURRENCY_EMBEDDED diff --git a/stdlib/public/Concurrency/DiscardingTaskGroup.swift b/stdlib/public/Concurrency/DiscardingTaskGroup.swift index 940757b014fda..1de2e5311abcf 100644 --- a/stdlib/public/Concurrency/DiscardingTaskGroup.swift +++ b/stdlib/public/Concurrency/DiscardingTaskGroup.swift @@ -186,152 +186,6 @@ public struct DiscardingTaskGroup { let _: Void? = try await _taskGroupWaitAll(group: _group, bodyError: nil) } - /// Adds a child task to the group. - /// - /// - Parameters: - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") - #endif - public mutating func addTask( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> Void - ) { -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: false, - addPendingGroupTaskUnconditionally: true, isDiscardingTask: true - ) -#else - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, isDiscardingTask: true - ) -#endif - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - _ = Builtin.createDiscardingTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - } - - /// Adds a child task to the group, unless the group has been canceled. - /// - /// - Parameters: - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") - #endif - public mutating func addTaskUnlessCancelled( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> Void - ) -> Bool { - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: false, - addPendingGroupTaskUnconditionally: false, isDiscardingTask: true - ) -#else - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, isDiscardingTask: true - ) -#endif - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - _ = Builtin.createDiscardingTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - - return true - } - - @_alwaysEmitIntoClient - public mutating func addTask( - operation: sending @escaping @isolated(any) () async -> Void - ) { - let flags = taskCreateFlags( - priority: nil, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, isDiscardingTask: true - ) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - _ = Builtin.createDiscardingTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - } - - /// Adds a child task to the group, unless the group has been canceled. - /// - /// - Parameters: - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") -#endif - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - operation: sending @escaping @isolated(any) () async -> Void - ) -> Bool { - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - - let flags = taskCreateFlags( - priority: nil, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, isDiscardingTask: true - ) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - _ = Builtin.createDiscardingTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - - return true - } - /// A Boolean value that indicates whether the group has any remaining tasks. /// /// At the start of the body of a `withDiscardingTaskGroup(of:returning:body:)` call, @@ -624,63 +478,6 @@ public struct ThrowingDiscardingTaskGroup { let _: Void? = try await _taskGroupWaitAll(group: _group, bodyError: bodyError) } -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") -#endif - @_alwaysEmitIntoClient - public mutating func addTask( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async throws -> Void - ) { - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, isDiscardingTask: true - ) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - _ = Builtin.createDiscardingTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - } - -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") -#endif - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async throws -> Void - ) -> Bool { - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, isDiscardingTask: true - ) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - _ = Builtin.createDiscardingTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - - return true - } - /// A Boolean value that indicates whether the group has any remaining tasks. /// /// At the start of the body of a `withThrowingDiscardingTaskGroup(returning:body:)` call, diff --git a/stdlib/public/Concurrency/SourceCompatibilityShims.swift b/stdlib/public/Concurrency/SourceCompatibilityShims.swift index aafa463b38b9d..a6caac782987a 100644 --- a/stdlib/public/Concurrency/SourceCompatibilityShims.swift +++ b/stdlib/public/Concurrency/SourceCompatibilityShims.swift @@ -274,38 +274,14 @@ extension Task where Success == Never, Failure == Never { } } -@available(SwiftStdlib 5.1, *) -extension Task { - @available(*, deprecated, message: "get() has been replaced by .value") - @_alwaysEmitIntoClient - public func get() async throws -> Success { - return try await value - } - - @available(*, deprecated, message: "getResult() has been replaced by .result") - @_alwaysEmitIntoClient - public func getResult() async -> Result { - return await result - } -} - -@available(SwiftStdlib 5.1, *) -extension Task where Failure == Never { - @available(*, deprecated, message: "get() has been replaced by .value") - @_alwaysEmitIntoClient - public func get() async -> Success { - return await value - } -} - -#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.1, *) extension TaskGroup { - @available(*, deprecated, renamed: "addTask(priority:operation:)") + @available(SwiftStdlib 5.1, *) + @available(*, deprecated, renamed: "addTaskUnlessCancelled(priority:operation:)") @_alwaysEmitIntoClient public mutating func add( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async -> ChildTaskResult + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult ) async -> Bool { return self.addTaskUnlessCancelled(priority: priority) { await operation() @@ -318,7 +294,7 @@ extension TaskGroup { priority: TaskPriority? = nil, operation: __owned @Sendable @escaping () async -> ChildTaskResult ) { - addTask(priority: priority, operation: operation) + self.addTask(priority: priority, operation: operation) } @available(*, deprecated, renamed: "addTaskUnlessCancelled(priority:operation:)") @@ -348,94 +324,7 @@ extension TaskGroup { addTaskUnlessCancelled(priority: priority, operation: operation) } } -#else -@available(SwiftStdlib 5.1, *) -extension TaskGroup { - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") - public mutating func add( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) async -> Bool { - fatalError("Unavailable in task-to-thread concurrency model") - } - @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") - @_alwaysEmitIntoClient - public mutating func add( - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) async -> Bool { - return self.addTaskUnlessCancelled { - await operation() - } - } - - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") - public mutating func spawn( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) { - fatalError("Unavailable in task-to-thread concurrency model") - } - - @available(*, deprecated, renamed: "addTask(operation:)") - @_alwaysEmitIntoClient - public mutating func spawn( - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) { - addTask(operation: operation) - } - - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") - public mutating func spawnUnlessCancelled( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) -> Bool { - fatalError("Unavailable in task-to-thread concurrency model") - } - - @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") - @_alwaysEmitIntoClient - public mutating func spawnUnlessCancelled( - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) -> Bool { - addTaskUnlessCancelled(operation: operation) - } - - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") - public mutating func async( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) { - fatalError("Unavailable in task-to-thread concurrency model") - } - - @available(*, deprecated, renamed: "addTask(operation:)") - @_alwaysEmitIntoClient - public mutating func async( - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) { - addTask(operation: operation) - } - - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") - public mutating func asyncUnlessCancelled( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) -> Bool { - fatalError("Unavailable in task-to-thread concurrency model") - } - - @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") - @_alwaysEmitIntoClient - public mutating func asyncUnlessCancelled( - operation: __owned @Sendable @escaping () async -> ChildTaskResult - ) -> Bool { - addTaskUnlessCancelled(operation: operation) - } -} -#endif - -#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.1, *) extension ThrowingTaskGroup { @available(*, deprecated, renamed: "addTask(priority:operation:)") @@ -485,92 +374,34 @@ extension ThrowingTaskGroup { addTaskUnlessCancelled(priority: priority, operation: operation) } } -#else -@available(SwiftStdlib 5.1, *) -extension ThrowingTaskGroup { - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") - public mutating func add( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) async -> Bool { - fatalError("Unavailable in task-to-thread concurrency model") - } - - @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") - @_alwaysEmitIntoClient - public mutating func add( - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) async -> Bool { - return self.addTaskUnlessCancelled { - try await operation() - } - } - - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") - public mutating func spawn( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) { - fatalError("Unavailable in task-to-thread concurrency model") - } - - @available(*, deprecated, renamed: "addTask(operation:)") - @_alwaysEmitIntoClient - public mutating func spawn( - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) { - addTask(operation: operation) - } - - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") - public mutating func spawnUnlessCancelled( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) -> Bool { - fatalError("Unavailable in task-to-thread concurrency model") - } - @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") +@available(SwiftStdlib 5.1, *) +extension Task { + @available(*, deprecated, message: "get() has been replaced by .value") @_alwaysEmitIntoClient - public mutating func spawnUnlessCancelled( - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) -> Bool { - addTaskUnlessCancelled(operation: operation) - } - - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") - public mutating func async( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) { - fatalError("Unavailable in task-to-thread concurrency model") + public func get() async throws -> Success { + return try await value } - @available(*, deprecated, renamed: "addTask(operation:)") + @available(*, deprecated, message: "getResult() has been replaced by .result") @_alwaysEmitIntoClient - public mutating func async( - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) { - addTask(operation: operation) - } - - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") - public mutating func asyncUnlessCancelled( - priority: TaskPriority? = nil, - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) -> Bool { - fatalError("Unavailable in task-to-thread concurrency model") + public func getResult() async -> Result { + return await result } +} - @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") +@available(SwiftStdlib 5.1, *) +extension Task where Failure == Never { + @available(*, deprecated, message: "get() has been replaced by .value") @_alwaysEmitIntoClient - public mutating func asyncUnlessCancelled( - operation: __owned @Sendable @escaping () async throws -> ChildTaskResult - ) -> Bool { - addTaskUnlessCancelled(operation: operation) + public func get() async -> Success { + return await value } } -#endif + +// NOTE: We had to move sources touching TaskGroup addTask APIs into TaskGroup+addTask.swift.gyb +// due to the build having trouble with using the generated source in the same module +// rdar://145171772 @available(SwiftStdlib 5.1, *) @available(*, deprecated, message: "please use UnsafeContinuation<..., Error>") diff --git a/stdlib/public/Concurrency/Task+TaskExecutor.swift b/stdlib/public/Concurrency/Task+TaskExecutor.swift index f10493e13c1fe..e52df4c3f5888 100644 --- a/stdlib/public/Concurrency/Task+TaskExecutor.swift +++ b/stdlib/public/Concurrency/Task+TaskExecutor.swift @@ -112,7 +112,7 @@ import Swift /// /// // child tasks execute on default executor (same as case 0): /// async let x = ... -/// await withTaskGroup(of: Int.self) { group in g.addTask { 7 } } +/// await withTaskGroup(of: Int.self) { group in group.addTask { 7 } } /// } /// } /// } diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index 66f0e05fdb288..32fbe9f6c9eba 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -208,6 +208,8 @@ TaskExecutorRef _task_taskExecutor_getTaskExecutorRef( TaskExecutorRef InitialTaskExecutorOwnedPreferenceTaskOptionRecord::getExecutorRefFromUnownedTaskExecutor() const { + if (!Identity) return TaskExecutorRef::undefined(); + TaskExecutorRef executorRef = _task_taskExecutor_getTaskExecutorRef( Identity, /*selfType=*/swift_getObjectType(Identity), @@ -737,8 +739,8 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags, case TaskOptionRecordKind::InitialTaskExecutorUnowned: taskExecutor = cast(option) ->getExecutorRef(); - jobFlags.task_setHasInitialTaskExecutorPreference(true); taskExecutorIsOwned = false; + jobFlags.task_setHasInitialTaskExecutorPreference(true); break; case TaskOptionRecordKind::InitialTaskExecutorOwned: @@ -1130,7 +1132,6 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags, task->_private().Local.initializeLinkParent(task, parent); } - // FIXME: add discarding flag concurrency::trace::task_create( task, parent, group, asyncLet, static_cast(task->Flags.getPriority()), diff --git a/stdlib/public/Concurrency/TaskGroup+Embedded.swift b/stdlib/public/Concurrency/TaskGroup+Embedded.swift new file mode 100644 index 0000000000000..9ead03d677005 --- /dev/null +++ b/stdlib/public/Concurrency/TaskGroup+Embedded.swift @@ -0,0 +1,277 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// FIXME: This is a workaround for trouble including gyb-generated sources in the embedded build. + +#if SWIFT_CONCURRENCY_EMBEDDED + +@available(SwiftStdlib 5.1, *) +extension TaskGroup { + + @available(SwiftStdlib 5.1, *) + @_alwaysEmitIntoClient + public mutating func addTask( + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> ChildTaskResult + ) { + let flags = taskCreateFlags( + priority: priority, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + enqueueJob: true, + addPendingGroupTaskUnconditionally: true, + isDiscardingTask: false + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + + _ = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + } + + @available(SwiftStdlib 5.1, *) + @_alwaysEmitIntoClient + public mutating func addTaskUnlessCancelled( + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> ChildTaskResult + ) -> Bool { + let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) + + guard canAdd else { + return false + } + + let flags = taskCreateFlags( + priority: priority, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: true + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + _ = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + + return true + } +} + +@available(SwiftStdlib 5.1, *) +extension ThrowingTaskGroup { + + @available(SwiftStdlib 5.1, *) + @_alwaysEmitIntoClient + public mutating func addTask( + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + ) { + let flags = taskCreateFlags( + priority: priority, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + enqueueJob: true, + addPendingGroupTaskUnconditionally: true, + isDiscardingTask: false + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + _ = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + } + + @available(SwiftStdlib 5.1, *) + @_alwaysEmitIntoClient + public mutating func addTaskUnlessCancelled( + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult + ) -> Bool { + let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) + + guard canAdd else { + return false + } + + let flags = taskCreateFlags( + priority: priority, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: true + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + _ = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + + return true + } +} + +@available(SwiftStdlib 5.9, *) +extension DiscardingTaskGroup { + + @available(SwiftStdlib 5.9, *) + @_alwaysEmitIntoClient + public mutating func addTask( + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> Void + ) { + let flags = taskCreateFlags( + priority: priority, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + enqueueJob: true, + addPendingGroupTaskUnconditionally: true, + isDiscardingTask: true + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + _ = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + } + + @available(SwiftStdlib 5.9, *) + @_alwaysEmitIntoClient + public mutating func addTaskUnlessCancelled( + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async -> Void + ) -> Bool { + let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) + + guard canAdd else { + return false + } + + let flags = taskCreateFlags( + priority: priority, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: true + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + _ = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + + return true + } +} + +@available(SwiftStdlib 5.9, *) +extension ThrowingDiscardingTaskGroup { + + @available(SwiftStdlib 5.9, *) + @_alwaysEmitIntoClient + public mutating func addTask( + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async throws -> Void + ) { + let flags = taskCreateFlags( + priority: priority, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + enqueueJob: true, + addPendingGroupTaskUnconditionally: true, + isDiscardingTask: true + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + + _ = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + } + + @available(SwiftStdlib 5.9, *) + @_alwaysEmitIntoClient + public mutating func addTaskUnlessCancelled( + priority: TaskPriority? = nil, + operation: sending @escaping @isolated(any) () async throws -> Void + ) -> Bool { + let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) + + guard canAdd else { + return false + } + + let flags = taskCreateFlags( + priority: priority, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + enqueueJob: true, + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: true + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + _ = Builtin.createTask( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + + return true + } +} + +#endif diff --git a/stdlib/public/Concurrency/TaskGroup+TaskExecutor.swift b/stdlib/public/Concurrency/TaskGroup+TaskExecutor.swift deleted file mode 100644 index 531b1ee303bb0..0000000000000 --- a/stdlib/public/Concurrency/TaskGroup+TaskExecutor.swift +++ /dev/null @@ -1,468 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 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 -// -//===----------------------------------------------------------------------===// - -import Swift - -// None of TaskExecutor APIs are available in task-to-thread concurrency model. -#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - -@_unavailableInEmbedded -@available(SwiftStdlib 6.0, *) -extension TaskGroup { - /// Adds a child task to the group and enqueue it on the specified executor. - /// - /// - Parameters: - /// - taskExecutor: The task executor that the child task should be started on and keep using. - /// Explicitly passing `nil` as the executor preference is equivalent to - /// calling the `addTask` method without a preference, and effectively - /// means to inherit the outer context's executor preference. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - executorPreference taskExecutor: (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) { - guard let taskExecutor else { - return self.addTask(priority: priority, operation: operation) - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: false) - - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - -#if $BuiltinCreateAsyncTaskOwnedTaskExecutor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutorConsuming: taskExecutor, - operation: operation) -#else - let executorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutor: executorBuiltin, - operation: operation) -#endif - } - - /// Adds a child task to the group and enqueue it on the specified executor, unless the group has been canceled. - /// - /// - Parameters: - /// - taskExecutor: The task executor that the child task should be started on and keep using. - /// If `nil` is passed explicitly, that parent task's executor preference (if any), - /// will be ignored. In order to inherit the parent task's executor preference - /// invoke `addTaskUnlessCancelled()` without passing a value to the `taskExecutor` parameter, - /// and it will be inherited automatically. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - executorPreference taskExecutor: (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) -> Bool { - guard let taskExecutor else { - return self.addTaskUnlessCancelled(priority: priority, operation: operation) - } - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, - isDiscardingTask: false) - - // Create the task in this group with an executor preference. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - -#if $BuiltinCreateAsyncTaskOwnedTaskExecutor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutorConsuming: taskExecutor, - operation: operation) -#else - let executorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutor: executorBuiltin, - operation: operation) -#endif - - return true - } -} - -// ==== ThrowingTaskGroup ------------------------------------------------------------------------------------------------------ - -@_unavailableInEmbedded -@available(SwiftStdlib 6.0, *) -extension ThrowingTaskGroup { - /// Adds a child task to the group and enqueue it on the specified executor. - /// - /// - Parameters: - /// - taskExecutor: The task executor that the child task should be started on and keep using. - /// If `nil` is passed explicitly, that parent task's executor preference (if any), - /// will be ignored. In order to inherit the parent task's executor preference - /// invoke `addTask()` without passing a value to the `taskExecutor` parameter, - /// and it will be inherited automatically. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - executorPreference taskExecutor: (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult - ) { - guard let taskExecutor else { - return self.addTask(priority: priority, operation: operation) - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: false) - - // Create the task in this group with an executor preference. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - -#if $BuiltinCreateAsyncTaskOwnedTaskExecutor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutorConsuming: taskExecutor, - operation: operation) -#else - let executorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutor: executorBuiltin, - operation: operation) -#endif - } - - /// Adds a child task to the group and enqueue it on the specified executor, unless the group has been canceled. - /// - /// - Parameters: - /// - taskExecutor: The task executor that the child task should be started on and keep using. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - executorPreference taskExecutor: (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult - ) -> Bool { - guard let taskExecutor else { - return self.addTaskUnlessCancelled(priority: priority, operation: operation) - } - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, - isDiscardingTask: false) - - // Create the task in this group with an executor preference. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - -#if $BuiltinCreateAsyncTaskOwnedTaskExecutor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutorConsuming: taskExecutor, - operation: operation) -#else - let executorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutor: executorBuiltin, - operation: operation) -#endif - - return true - } -} - -// ==== DiscardingTaskGroup ------------------------------------------------------------------------------------------------------ - -@_unavailableInEmbedded -@available(SwiftStdlib 6.0, *) -extension DiscardingTaskGroup { - /// Adds a child task to the group and enqueue it on the specified executor. - /// - /// - Parameters: - /// - taskExecutor: The task executor that the child task should be started on and keep using. - /// If `nil` is passed explicitly, that parent task's executor preference (if any), - /// will be ignored. In order to inherit the parent task's executor preference - /// invoke `addTask()` without passing a value to the `taskExecutor` parameter, - /// and it will be inherited automatically. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - executorPreference taskExecutor: (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> Void - ) { - guard let taskExecutor else { - return self.addTask(priority: priority, operation: operation) - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: true) - - // Create the task in this group with an executor preference. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - -#if $BuiltinCreateAsyncTaskOwnedTaskExecutor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutorConsuming: taskExecutor, - operation: operation) -#else - let executorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutor: executorBuiltin, - operation: operation) -#endif - } - - /// Adds a child task to the group and set it up with the passed in task executor preference, - /// unless the group has been canceled. - /// - /// - Parameters: - /// - taskExecutor: The task executor that the child task should be started on and keep using. - /// If `nil` is passed explicitly, that parent task's executor preference (if any), - /// will be ignored. In order to inherit the parent task's executor preference - /// invoke `addTask()` without passing a value to the `taskExecutor` parameter, - /// and it will be inherited automatically. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - executorPreference taskExecutor: (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> Void - ) -> Bool { - guard let taskExecutor else { - return self.addTaskUnlessCancelled(priority: priority, operation: operation) - } - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, isDiscardingTask: true - ) - - // Create the task in this group with an executor preference. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - -#if $BuiltinCreateAsyncTaskOwnedTaskExecutor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutorConsuming: taskExecutor, - operation: operation) -#else - let executorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutor: executorBuiltin, - operation: operation) -#endif - - return true - } -} - -// ==== ThrowingDiscardingTaskGroup ------------------------------------------------------------------------------------------------------ - -@_unavailableInEmbedded -@available(SwiftStdlib 6.0, *) -extension ThrowingDiscardingTaskGroup { - /// Adds a child task to the group and set it up with the passed in task executor preference. - /// - /// - Parameters: - /// - taskExecutor: The task executor that the child task should be started on and keep using. - /// If `nil` is passed explicitly, that parent task's executor preference (if any), - /// will be ignored. In order to inherit the parent task's executor preference - /// invoke `addTask()` without passing a value to the `taskExecutor` parameter, - /// and it will be inherited automatically. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - executorPreference taskExecutor: (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async throws -> Void - ) { - guard let taskExecutor else { - return self.addTask(priority: priority, operation: operation) - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: true) - - // Create the task in this group with an executor preference. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - -#if $BuiltinCreateAsyncTaskOwnedTaskExecutor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutorConsuming: taskExecutor, - operation: operation) -#else - let executorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutor: executorBuiltin, - operation: operation) -#endif - } - - /// Adds a child task to the group and set it up with the passed in task executor preference, - /// unless the group has been canceled. - /// - /// - Parameters: - /// - taskExecutor: The task executor that the child task should be started on and keep using. - /// If `nil` is passed explicitly, that parent task's executor preference (if any), - /// will be ignored. In order to inherit the parent task's executor preference - /// invoke `addTask()` without passing a value to the `taskExecutor` parameter, - /// and it will be inherited automatically. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - executorPreference taskExecutor: (any TaskExecutor)?, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async throws -> Void - ) -> Bool { - guard let taskExecutor else { - return self.addTaskUnlessCancelled(priority: priority, operation: operation) - } - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, isDiscardingTask: true - ) - - // Create the task in this group with an executor preference. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - -#if $BuiltinCreateAsyncTaskOwnedTaskExecutor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutorConsuming: taskExecutor, - operation: operation) -#else - let executorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - initialTaskExecutor: executorBuiltin, - operation: operation) -#endif - - return true - } -} - -#endif diff --git a/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb b/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb new file mode 100644 index 0000000000000..b4e8fe1e2848d --- /dev/null +++ b/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb @@ -0,0 +1,311 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 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 +// +//===----------------------------------------------------------------------===// + +import Swift + +// ==== addTask / addTaskUnlessCancelled --------------------------------------- +% # Generate: +% # - both method kinds (2) +% # - for every task group kind (4) +% # - every overload that we need to keep for ABI compatibility +% # - #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY guarded versions making the methods unavailable + +% for (IFDEF, TYPES, ALL_AVAILABILITY, ADD_TASK_METHOD_VARIANT, PARAMS) in [ +% # ----------------------------------------------------------------------- +% # === Added task name --------------------------------------------------- +% ( +% '', # no #if condition +% [ +% 'TaskGroup', +% 'ThrowingTaskGroup', +% 'DiscardingTaskGroup', +% 'ThrowingDiscardingTaskGroup' +% ], +% [ +% '@available(SwiftStdlib 6.2, *)', +% '@_unavailableInEmbedded', # since TaskExecutor is not available on embedded +% ], +% ['addTask', 'addTaskUnlessCancelled'], +% [ +% 'name: String? = nil', +% 'executorPreference taskExecutor: (any TaskExecutor)? = nil', +% 'priority: TaskPriority? = nil', +% # throws and ChildTaskResult will be adjusted per task group type +% 'operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult' +% ], +% ), +% # ----------------------------------------------------------------------- +% # === Added TaskExecutor +% ( +% '', # no #if condition +% [ +% 'TaskGroup', +% 'ThrowingTaskGroup', +% 'DiscardingTaskGroup', +% 'ThrowingDiscardingTaskGroup' +% ], +% [ +% '@available(SwiftStdlib 6.0, *)', +% '@_unavailableInEmbedded', # since TaskExecutor is not available on embedded +% ], +% ['addTask', 'addTaskUnlessCancelled'], +% [ +% 'executorPreference taskExecutor: (any TaskExecutor)?', +% 'priority: TaskPriority? = nil', +% # throws and ChildTaskResult will be adjusted per task group type +% 'operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult' +% ], +% ), +% ( +% 'SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY', +% [ +% 'TaskGroup', +% 'ThrowingTaskGroup', +% ], +% [ +% '@available(SwiftStdlib 5.7, *)', +% '@available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)")', +% ], +% ['addTask', 'addTaskUnlessCancelled'], +% [ +% 'priority: TaskPriority? = nil', +% # throws and ChildTaskResult will be adjusted per task group type +% 'operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult' +% ], +% ), +% ( +% 'SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY', +% [ +% 'TaskGroup', +% 'ThrowingTaskGroup', +% ], +% [ +% '// version for task to thread model, without priority parameter', +% ], +% ['addTask', 'addTaskUnlessCancelled'], +% [ +% # throws and ChildTaskResult will be adjusted per task group type +% 'operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult' +% ], +% ), +% # ----------------------------------------------------------------------- +% # === Baseline +% ( +% '!SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY', +% [ +% 'TaskGroup', +% 'ThrowingTaskGroup', +% ], +% [ +% '@available(SwiftStdlib 5.1, *)', +% ], +% ['addTask', 'addTaskUnlessCancelled'], +% [ +% 'priority: TaskPriority? = nil', +% # throws and ChildTaskResult will be adjusted per task group type +% 'operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult' +% ], +% ), +% ]: +% +% for TYPE in TYPES: +% IS_DISCARDING = 'Discarding' in TYPE +% IS_THROWING = 'Throwing' in TYPE +% +% def adjust_params_for_group_kind(params): +% res = [] +% for p in params: +% np = p +% if IS_DISCARDING: +% np = np.replace("-> ChildTaskResult", "-> Void") +% if not IS_THROWING: +% np = np.replace("throws", "") +% res.append(np) +% return res +% +% for METHOD_NAME in ADD_TASK_METHOD_VARIANT: +% +% IS_ADD_UNLESS_CANCELLED = METHOD_NAME == "addTaskUnlessCancelled" +% IS_IMPL_UNAVAILABLE = any('Unavailable in task-to-thread' in av for av in ALL_AVAILABILITY) +% IS_TASK_TO_THREAD_MODEL = 'SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY' == IFDEF +% +% HAS_TASK_PRIORITY = any('priority:' in param for param in PARAMS) +% HAS_TASK_NAME = any('name:' in param for param in PARAMS) +% HAS_TASK_EXECUTOR = any('taskExecutor:' in param for param in PARAMS) +% +% ARROW_RETURN_TYPE = "-> Bool " if IS_ADD_UNLESS_CANCELLED else "" +% +% if IS_DISCARDING: +% TASK_CREATE_FN = 'Builtin.createDiscardingTask' +% else: +% TASK_CREATE_FN = 'Builtin.createTask' +% end + +% if IFDEF: +#if ${IFDEF} +% end + +% if IS_DISCARDING: +@available(SwiftStdlib 5.9, *) +% else: +@available(SwiftStdlib 5.1, *) +% end +extension ${TYPE} { + + % if IS_ADD_UNLESS_CANCELLED: + /// Adds a child task to the group, unless the group has been canceled. + /// Returns a boolean value indicating if the task was successfully added to the group or not. + % else: + /// Adds a child task to the group. + % end + /// + % if IS_THROWING: + /// This method doesn't throw an error, even if the child task does. + /// Instead, the corresponding call to `ThrowingTaskGroup.next()` rethrows that error. + % end + /// + /// - Parameters: + % if HAS_TASK_NAME: + /// - name: Human readable name of this task. + % end + % if HAS_TASK_EXECUTOR: + /// - taskExecutor: + /// - taskExecutor: The task executor that the child task should be started on and keep using. + /// Explicitly passing `nil` as the executor preference is equivalent to + /// calling the `addTask` method without a preference, and effectively + /// means to inherit the outer context's executor preference. + /// You can also pass the ``globalConcurrentExecutor`` global executor explicitly. + % end + % if HAS_TASK_PRIORITY: + /// - priority: The priority of the operation task. + /// Omit this parameter or pass `nil` to inherit the task group's base priority. + % end + /// Omit this parameter or pass `.unspecified` + /// to set the child task's priority to the priority of the group. + /// - operation: The operation to execute as part of the task group. + % if IS_ADD_UNLESS_CANCELLED: + /// - Returns: `true` if the child task was added to the group; + /// otherwise `false`. + % end + ${"\n ".join(ALL_AVAILABILITY)} + @_alwaysEmitIntoClient + public mutating func ${METHOD_NAME}( + ${",\n ".join(adjust_params_for_group_kind(PARAMS))} + ) ${ARROW_RETURN_TYPE}{ + % if IS_IMPL_UNAVAILABLE: + fatalError("Unavailable in task-to-thread concurrency model") + % else: # !IS_IMPL_UNAVAILABLE + % if IS_ADD_UNLESS_CANCELLED: + let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) + + guard canAdd else { + // the group is cancelled and is not accepting any new work + return false + } + % end # IS_ADD_UNLESS_CANCELLED + + let flags = taskCreateFlags( + priority: ${'priority' if HAS_TASK_PRIORITY else 'nil'}, + isChildTask: true, + copyTaskLocals: false, + inheritContext: false, + % if IS_TASK_TO_THREAD_MODEL: + // task-to-thread model; don't enqueue as we'll run inline + enqueueJob: false, + % else: + enqueueJob: true, + % end + % if IS_ADD_UNLESS_CANCELLED: + % # In this case, we already added the pending task count before we create the task + % # so we must not add to the pending counter again. + addPendingGroupTaskUnconditionally: false, + % else: + addPendingGroupTaskUnconditionally: true, + % end + isDiscardingTask: ${str(IS_DISCARDING).lower()} + ) + + let builtinSerialExecutor = + Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor + + var task: Builtin.NativeObject? + + % # These #if conditionals are in specific order because if we have the "latest one", + % # we definitely have the prior ones, as we always keep adding parameters. + % if HAS_TASK_NAME: + #if $BuiltinCreateAsyncTaskName + if var name { + task = + name.withUTF8 { nameBytes in + ${TASK_CREATE_FN}( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + initialTaskExecutorConsuming: taskExecutor, + taskName: nameBytes.baseAddress?._rawValue, + operation: operation).0 + } + } + #endif // $BuiltinCreateAsyncTaskName + % end # HAS_TASK_NAME + + // Task name was not set, or task name createTask is unavailable + % if HAS_TASK_EXECUTOR: + if task == nil, let taskExecutor { + #if $BuiltinCreateAsyncTaskOwnedTaskExecutor + task = ${TASK_CREATE_FN}( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + initialTaskExecutorConsuming: taskExecutor, + operation: operation).0 + #else + // legacy branch for the non-consuming task executor + let executorBuiltin: Builtin.Executor = + taskExecutor.asUnownedTaskExecutor().executor + + task = ${TASK_CREATE_FN}( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + initialTaskExecutor: executorBuiltin, + operation: operation).0 + #endif + } + % end + + % # The baseline fallback, if no other create calls could be used + if task == nil { + task = ${TASK_CREATE_FN}( + flags: flags, + initialSerialExecutor: builtinSerialExecutor, + taskGroup: _group, + operation: operation).0 + } + + // Assert that we did create the task, but there's no need to store it, + // as it was added to the group itself. + assert(task != nil, "Expected task to be created!") + % if IS_ADD_UNLESS_CANCELLED: + return true // task successfully enqueued + % end + % end # !IS_IMPL_UNAVAILABLE + } + +} +% if IFDEF: +#endif +% end + +% end +% end +% end diff --git a/stdlib/public/Concurrency/TaskGroup.swift b/stdlib/public/Concurrency/TaskGroup.swift index 71fda69232d36..bcf1b1a1fd654 100644 --- a/stdlib/public/Concurrency/TaskGroup.swift +++ b/stdlib/public/Concurrency/TaskGroup.swift @@ -14,16 +14,6 @@ import Swift // ==== TaskGroup -------------------------------------------------------------- -// In the task-to-thread model we don't enqueue tasks created using addTask -// to the global pool, but will run them inline instead. -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY -@usableFromInline -let enqueueJobOnCreate = false -#else -@usableFromInline -let enqueueJobOnCreate = true -#endif - /// Starts a new scope that can contain a dynamic number of child tasks. /// /// A group waits for all of its child tasks @@ -56,9 +46,9 @@ let enqueueJobOnCreate = true /// by calling the `cancelAll()` method on the task group, /// or by canceling the task in which the group is running. /// -/// If you call `addTask(priority:operation:)` to create a new task in a canceled group, +/// If you call `addTask(name:priority:operation:)` to create a new task in a canceled group, /// that task is immediately canceled after creation. -/// Alternatively, you can call `addTaskUnlessCancelled(priority:operation:)`, +/// Alternatively, you can call `addTaskUnlessCancelled(name:priority:operation:)`, /// which doesn't create the task if the group has already been canceled. /// Choosing between these two functions /// lets you control how to react to cancellation within a group: @@ -341,264 +331,6 @@ public struct TaskGroup { self._group = group } -#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - /// Adds a child task to the group. - /// - /// - Parameters: - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) { - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: enqueueJobOnCreate, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: false - ) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - } - - /// Adds a child task to the group. - /// - /// - Parameters: - /// - name: Human readable name of this task. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - name: String? = nil, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) { - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: enqueueJobOnCreate, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: false - ) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - var task: Builtin.NativeObject? - #if $BuiltinCreateAsyncTaskName - if var name { - task = - name.withUTF8 { nameBytes in - Builtin.createTask( - flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - taskName: nameBytes.baseAddress?._rawValue, - operation: operation).0 - } - } - #endif - - if task == nil { - // either no task name was set, or names are unsupported - task = Builtin.createTask( - flags: flags, - // unsupported names, so we drop it. - initialSerialExecutor: builtinSerialExecutor, - operation: operation).0 - } - - // task was enqueued to the group, no need to store the 'task' ref itself - assert(task != nil, "Expected task to be created!") - } - - /// Adds a child task to the group, unless the group has been canceled. - /// - /// - Parameters: - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) -> Bool { - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: enqueueJobOnCreate, - addPendingGroupTaskUnconditionally: false, - isDiscardingTask: false) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - - return true - } - - /// Adds a child task to the group, unless the group has been canceled. - /// - /// - Parameters: - /// - name: Human readable name of the task. - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - name: String? = nil, - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) -> Bool { - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: enqueueJobOnCreate, - addPendingGroupTaskUnconditionally: false, - isDiscardingTask: false) - - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - - var task: Builtin.NativeObject? - #if $BuiltinCreateAsyncTaskName - if var name { - task = - name.withUTF8 { nameBytes in - Builtin.createTask( - flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - taskName: nameBytes.baseAddress?._rawValue, - operation: operation).0 - } - } - #endif - - if task == nil { - // either no task name was set, or names are unsupported - task = Builtin.createTask( - flags: flags, - // unsupported names, so we drop it. - initialSerialExecutor: builtinSerialExecutor, - operation: operation).0 - } - - // task was enqueued to the group, no need to store the 'task' ref itself - assert(task != nil, "Expected task to be created!") - - return true - } - -#else // if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - @available(SwiftStdlib 5.7, *) - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") - public mutating func addTask( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) { - fatalError("Unavailable in task-to-thread concurrency model") - } - - /// Adds a child task to the group. - /// - /// - Parameters: - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) { - let flags = taskCreateFlags( - priority: nil, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: false) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - } - - @available(SwiftStdlib 5.7, *) - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") - public mutating func addTaskUnlessCancelled( - priority: TaskPriority? = nil, - operation: sending @escaping () async -> ChildTaskResult - ) -> Bool { - fatalError("Unavailable in task-to-thread concurrency model") - } - - /// Adds a child task to the group, unless the group has been canceled. - /// - /// - Parameters: - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - operation: sending @escaping @isolated(any) () async -> ChildTaskResult - ) -> Bool { - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - - let flags = taskCreateFlags( - priority: nil, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, - isDiscardingTask: false) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - - return true - } -#endif - /// Wait for the next child task to complete, /// and return the value it returned. /// @@ -886,151 +618,6 @@ public struct ThrowingTaskGroup { } } -#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - /// Adds a child task to the group. - /// - /// This method doesn't throw an error, even if the child task does. - /// Instead, the corresponding call to `ThrowingTaskGroup.next()` rethrows that error. - /// - /// - Parameters: - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult - ) { - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: false - ) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - } - - /// Adds a child task to the group, unless the group has been canceled. - /// - /// This method doesn't throw an error, even if the child task does. - /// Instead, the corresponding call to `ThrowingTaskGroup.next()` rethrows that error. - /// - /// - Parameters: - /// - priority: The priority of the operation task. - /// Omit this parameter or pass `.unspecified` - /// to set the child task's priority to the priority of the group. - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - priority: TaskPriority? = nil, - operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult - ) -> Bool { - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - - let flags = taskCreateFlags( - priority: priority, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, - isDiscardingTask: false) - - // Create the task in this group. - let builtinSerialExecutor = - Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - _ = Builtin.createTask(flags: flags, - initialSerialExecutor: builtinSerialExecutor, - taskGroup: _group, - operation: operation) - - return true - } -#else - @available(SwiftStdlib 5.7, *) - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") - public mutating func addTask( - priority: TaskPriority? = nil, - operation: sending @escaping () async throws -> ChildTaskResult - ) { - fatalError("Unavailable in task-to-thread concurrency model") - } - - /// Adds a child task to the group. - /// - /// This method doesn't throw an error, even if the child task does. - /// Instead, the corresponding call to `ThrowingTaskGroup.next()` rethrows that error. - /// - /// - Parameters: - /// - operation: The operation to execute as part of the task group. - @_alwaysEmitIntoClient - public mutating func addTask( - operation: sending @escaping () async throws -> ChildTaskResult - ) { - let flags = taskCreateFlags( - priority: nil, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: true, - isDiscardingTask: false) - - // Create the task in this group. - _ = Builtin.createAsyncTaskInGroup(flags, _group, operation) - } - - @available(SwiftStdlib 5.7, *) - @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") - public mutating func addTaskUnlessCancelled( - priority: TaskPriority? = nil, - operation: sending @escaping () async throws -> ChildTaskResult - ) -> Bool { - fatalError("Unavailable in task-to-thread concurrency model") - } - - /// Adds a child task to the group, unless the group has been canceled. - /// - /// This method doesn't throw an error, even if the child task does. - /// Instead, the corresponding call to `ThrowingTaskGroup.next()` rethrows that error. - /// - /// - Parameters: - /// - operation: The operation to execute as part of the task group. - /// - Returns: `true` if the child task was added to the group; - /// otherwise `false`. - @_alwaysEmitIntoClient - public mutating func addTaskUnlessCancelled( - operation: sending @escaping () async throws -> ChildTaskResult - ) -> Bool { - let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false) - - guard canAdd else { - // the group is cancelled and is not accepting any new work - return false - } - - let flags = taskCreateFlags( - priority: nil, isChildTask: true, copyTaskLocals: false, - inheritContext: false, enqueueJob: true, - addPendingGroupTaskUnconditionally: false, - isDiscardingTask: false) - - // Create the task in this group. - _ = Builtin.createAsyncTaskInGroup(flags, _group, operation) - - return true - } -#endif - /// Wait for the next child task to complete, /// and return the value it returned or rethrow the error it threw. /// diff --git a/stdlib/public/Concurrency/TaskPrivate.h b/stdlib/public/Concurrency/TaskPrivate.h index 46b306fd5c895..afec14705b8c4 100644 --- a/stdlib/public/Concurrency/TaskPrivate.h +++ b/stdlib/public/Concurrency/TaskPrivate.h @@ -796,14 +796,12 @@ struct AsyncTask::PrivateStorage { // here, before the task-local storage elements are destroyed; in order to // respect stack-discipline of the task-local allocator. { - - if (task->hasInitialTaskExecutorPreferenceRecord()) { - task->dropInitialTaskExecutorPreferenceRecord(); - } - if (task->hasInitialTaskNameRecord()) { task->dropInitialTaskNameRecord(); } + if (task->hasInitialTaskExecutorPreferenceRecord()) { + task->dropInitialTaskExecutorPreferenceRecord(); + } } // Drain unlock the task and remove any overrides on thread as a diff --git a/stdlib/public/core/OverloadMacros.swift b/stdlib/public/core/OverloadMacros.swift deleted file mode 100644 index 59db085c87a63..0000000000000 --- a/stdlib/public/core/OverloadMacros.swift +++ /dev/null @@ -1,46 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2014 - 2017 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 $Macros && hasAttribute(attached) - - -/* - -@_CompatibilityOverload(added: debugName, in: .swift_9999) -public init( - debugName: String? = nil, - priority: TaskPriority? = nil, - @_inheritActorContext @_implicitSelfCapture operation: sending @escaping @isolated(any) () async -> Success -) -*/ -@freestanding(declaration) -internal macro _CompatibilityOverload( - added: String, - in: -) = Builtin.ErrorMacro - -internal enum CompatOverload: String { - case swift_5_1 = "@available(SwiftStdlib 5.1, *)" - case swift_5_2 = "@available(SwiftStdlib 5.2, *)" - case swift_5_3 = "@available(SwiftStdlib 5.3, *)" - case swift_5_4 = "@available(SwiftStdlib 5.4, *)" - case swift_5_5 = "@available(SwiftStdlib 5.5, *)" - case swift_5_6 = "@available(SwiftStdlib 5.6, *)" - case swift_5_7 = "@available(SwiftStdlib 5.7, *)" - case swift_5_8 = "@available(SwiftStdlib 5.8, *)" - case swift_5_9 = "@available(SwiftStdlib 5.9, *)" - case swift_5_10 = "@available(SwiftStdlib 5.10, *)" - case swift_6_0 = "@available(SwiftStdlib 6.0, *)" - case swift_9999 = "@available(SwiftStdlib 9999, *)" -} - -#endif // $Macros && hasAttribute(attached) diff --git a/stdlib/toolchain/Compatibility56/include/Concurrency/TaskPrivate.h b/stdlib/toolchain/Compatibility56/include/Concurrency/TaskPrivate.h index 186df38da7ac3..ed453bfb7d6f4 100644 --- a/stdlib/toolchain/Compatibility56/include/Concurrency/TaskPrivate.h +++ b/stdlib/toolchain/Compatibility56/include/Concurrency/TaskPrivate.h @@ -29,7 +29,7 @@ namespace swift { -#if 1 +#if 0 using ThreadID = decltype(pthread_self()); inline ThreadID _swift_get_thread_id() { diff --git a/test/Concurrency/Runtime/async_task_naming.swift b/test/Concurrency/Runtime/async_task_naming.swift index bb3aac5114b65..7b597a1271dd6 100644 --- a/test/Concurrency/Runtime/async_task_naming.swift +++ b/test/Concurrency/Runtime/async_task_naming.swift @@ -9,8 +9,8 @@ func pretendToThrow() throws {} func test() async { - // CHECK: Task.name = NONE - print("Task.name = \(Task.name ?? "NONE")") + // CHECK: Task.name = NONE OK + print("Task.name = \(Task.name ?? "NONE OK")") _ = await Task(name: "Caplin the Task") { // CHECK: Task.name = Caplin the Task @@ -22,6 +22,10 @@ func test() async { // CHECK: Task.name = Caplin the Throwing Task print("Task.name = \(Task.name ?? "NONE")") try pretendToThrow() + await Task { + // CHECK: Does not inherit Task.name = NONE OK + print("Does not inherit Task.name = \(Task.name ?? "NONE OK")") + }.value return 12 }.value @@ -32,32 +36,80 @@ func test() async { }.value _ = try? await Task.detached(name: "Caplin the Detached Throwing Task") { - // CHECK: Task.name = Caplin the Detached Task + // CHECK: Task.name = Caplin the Detached Throwing Task print("Task.name = \(Task.name ?? "NONE")") try pretendToThrow() return 12 }.value _ = await withTaskGroup(of: Int.self) { g in - g.addTask(name: "Caplin the TaskGroup Task") { + g.addTask( + name: "Caplin the TaskGroup Task", + executorPreference: nil) { // CHECK: Task.name = Caplin the TaskGroup Task print("Task.name = \(Task.name ?? "NONE")") return 12 } - g.addTaskUnlessCancelled(name: "Caplin the TaskGroup Task (unless cancelled)") { + _ = await g.next() + _ = g.addTaskUnlessCancelled( + name: "Caplin the TaskGroup Task (unless cancelled)", + executorPreference: nil) { // CHECK: Task.name = Caplin the TaskGroup Task (unless cancelled) print("Task.name = \(Task.name ?? "NONE")") return 12 } } -// _ = await withThrowingTaskGroup(of: Int.self) { g in -// g.addTask(name: "Caplin the ThrowingTaskGroup Task") { -// // CHECK: Task.name = Caplin the ThrowingTaskGroup Task -// print("Task.name = \(Task.name ?? "NONE")") -// return 12 -// } -// } + _ = await withThrowingTaskGroup(of: Int.self) { g in + g.addTask( + name: "Caplin the ThrowingTaskGroup Task", + executorPreference: nil) { + // CHECK: Task.name = Caplin the ThrowingTaskGroup Task + print("Task.name = \(Task.name ?? "NONE")") + return 12 + } + _ = try? await g.next() + _ = g.addTaskUnlessCancelled( + name: "Caplin the ThrowingTaskGroup Task (unless cancelled)", + executorPreference: nil) { + // CHECK: Task.name = Caplin the ThrowingTaskGroup Task (unless cancelled) + print("Task.name = \(Task.name ?? "NONE")") + return 12 + } + } + + _ = await withDiscardingTaskGroup { g in + g.addTask( + name: "Caplin the DiscardingTaskGroup Task", + executorPreference: nil) { + // CHECK: Task.name = Caplin the DiscardingTaskGroup Task + print("Task.name = \(Task.name ?? "NONE")") + } + } + _ = await withDiscardingTaskGroup { g in + _ = g.addTaskUnlessCancelled( + name: "Caplin the DiscardingTaskGroup Task (unless cancelled)", + executorPreference: nil) { + // CHECK: Task.name = Caplin the DiscardingTaskGroup Task (unless cancelled) + print("Task.name = \(Task.name ?? "NONE")") + } + } + _ = try? await withThrowingDiscardingTaskGroup { g in + g.addTask( + name: "Caplin the ThrowingDiscardingTaskGroup Task", + executorPreference: nil) { + // CHECK: Task.name = Caplin the ThrowingDiscardingTaskGroup Task + print("Task.name = \(Task.name ?? "NONE")") + } + } + _ = try? await withThrowingDiscardingTaskGroup { g in + _ = g.addTaskUnlessCancelled( + name: "Caplin the ThrowingDiscardingTaskGroup Task (unless cancelled)", + executorPreference: nil) { + // CHECK: Task.name = Caplin the ThrowingDiscardingTaskGroup Task (unless cancelled) + print("Task.name = \(Task.name ?? "NONE")") + } + } } await test() diff --git a/test/Concurrency/async_main.swift b/test/Concurrency/async_main.swift index 358c4b0a3770f..9fd02723d1f2f 100644 --- a/test/Concurrency/async_main.swift +++ b/test/Concurrency/async_main.swift @@ -71,11 +71,12 @@ func asyncFunc() async { // CHECK-SIL-NEXT: [[GROUP:%.*]] = enum $Optional, #Optional.none // CHECK-SIL-NEXT: [[TASK_EXECUTOR_UNOWNED:%.*]] = enum $Optional, #Optional.none // CHECK-SIL-NEXT: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional, #Optional.none +// CHECK-SIL-NEXT: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK-SIL-NEXT: // function_ref thunk for @escaping @convention(thin) @async () -> () // CHECK-SIL-NEXT: [[THUNK_FN:%.*]] = function_ref @$sIetH_yts5Error_pIegHrzo_TR : $@convention(thin) @async (@convention(thin) @async () -> ()) -> (@out (), @error any Error) // CHECK-SIL-NEXT: [[THUNK:%.*]] = partial_apply [callee_guaranteed] [[THUNK_FN]]([[ASYNC_MAIN_FN]]) : $@convention(thin) @async (@convention(thin) @async () -> ()) -> (@out (), @error any Error) // CHECK-SIL-NEXT: [[CONVERTED_THUNK:%.*]] = convert_function [[THUNK]] : $@async @callee_guaranteed () -> (@out (), @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <()> -// CHECK-SIL-NEXT: [[TASK_RESULT:%.*]] = builtin "createAsyncTask"<()>([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR_UNOWNED]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional, [[CONVERTED_THUNK]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <()>) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-SIL-NEXT: [[TASK_RESULT:%.*]] = builtin "createAsyncTask"<()>([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR_UNOWNED]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional, [[TASK_NAME]] : $Optional, [[CONVERTED_THUNK]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for <()>) : $(Builtin.NativeObject, Builtin.RawPointer) // CHECK-SIL-NEXT: [[TASK:%.*]] = tuple_extract [[TASK_RESULT]] : $(Builtin.NativeObject, Builtin.RawPointer), 0 // CHECK-SIL-NEXT: // function_ref swift_job_run // CHECK-SIL-NEXT: [[RUN_FN:%.*]] = function_ref @swift_job_run : $@convention(thin) (UnownedJob, UnownedSerialExecutor) -> () diff --git a/test/IRGen/async/builtins.sil b/test/IRGen/async/builtins.sil index ba47613d8f1e0..9ad7f105d3919 100644 --- a/test/IRGen/async/builtins.sil +++ b/test/IRGen/async/builtins.sil @@ -39,7 +39,8 @@ bb0(%flags : $Int, %taskFunction: @owned $@async @callee_guaranteed @substituted %optTaskGroup = enum $Optional, #Optional.none %optTaskExecutor = enum $Optional, #Optional.none %optTaskExecutorOwned = enum $Optional, #Optional.none - %20 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + %taskName = enum $Optional, #Optional.none + %20 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskName : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) destroy_value %20 : $(Builtin.NativeObject, Builtin.RawPointer) %21 = tuple () return %21 : $() @@ -65,7 +66,8 @@ bb0(%flags : $Int, %serialExecutor : $Builtin.Executor, %taskFunction: @owned $@ %optTaskGroup = enum $Optional, #Optional.none %optTaskExecutor = enum $Optional, #Optional.none %optTaskExecutorOwned = enum $Optional, #Optional.none - %20 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + %taskName = enum $Optional, #Optional.none + %20 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskName : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) destroy_value %20 : $(Builtin.NativeObject, Builtin.RawPointer) %21 = tuple () return %21 : $() @@ -88,7 +90,8 @@ bb0(%taskGroup : $Builtin.RawPointer, %taskFunction : $@async @callee_guaranteed %optTaskGroup = enum $Optional, #Optional.some!enumelt, %taskGroup : $Builtin.RawPointer %optTaskExecutor = enum $Optional, #Optional.none %optTaskExecutorOwned = enum $Optional, #Optional.none - %9 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + %taskName = enum $Optional, #Optional.none + %9 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskName : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) %10 = tuple_extract %9 : $(Builtin.NativeObject, Builtin.RawPointer), 0 strong_release %10 : $Builtin.NativeObject %12 = tuple () @@ -118,7 +121,8 @@ bb0(%flags : $Int, %optTaskGroup : $Optional, %taskFunction: %optSerialExecutor = enum $Optional, #Optional.none %optTaskExecutor = enum $Optional, #Optional.none %optTaskExecutorOwned = enum $Optional, #Optional.none - %20 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + %taskName = enum $Optional, #Optional.none + %20 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskName : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) destroy_value %20 : $(Builtin.NativeObject, Builtin.RawPointer) %result = tuple () return %result : $() @@ -131,9 +135,10 @@ bb0(%taskGroup : $Builtin.RawPointer, %taskFunction : $@async @callee_guaranteed %optTaskGroup = enum $Optional, #Optional.some!enumelt, %taskGroup : $Builtin.RawPointer %optTaskExecutor = enum $Optional, #Optional.none %optTaskExecutorOwned = enum $Optional, #Optional.none + %taskName = enum $Optional, #Optional.none // CHECK-NOT: br i1 // CHECK: call swift{{(tail)?}}cc %swift.async_task_and_context @swift_task_create( - %9 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskFunction : $@async @callee_guaranteed () -> @error Error) : $(Builtin.NativeObject, Builtin.RawPointer) + %9 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskName : $Optional, %taskFunction : $@async @callee_guaranteed () -> @error Error) : $(Builtin.NativeObject, Builtin.RawPointer) %10 = tuple_extract %9 : $(Builtin.NativeObject, Builtin.RawPointer), 0 strong_release %10 : $Builtin.NativeObject %12 = tuple () @@ -147,6 +152,7 @@ bb0(%taskGroup : $Builtin.RawPointer, %taskExecutor : $Builtin.Executor, %taskFu %optTaskGroup = enum $Optional, #Optional.some!enumelt, %taskGroup : $Builtin.RawPointer %optTaskExecutor = enum $Optional, #Optional.some!enumelt, %taskExecutor : $Builtin.Executor %optTaskExecutorOwned = enum $Optional, #Optional.none + %taskName = enum $Optional, #Optional.none // CHECK: [[GROUP_RECORD:%.*]] = alloca %swift.task_group_task_option // CHECK: [[EXECUTOR_RECORD:%.*]] = alloca %swift.task_executor_task_option // CHECK-NOT: br i1 @@ -171,7 +177,7 @@ bb0(%taskGroup : $Builtin.RawPointer, %taskExecutor : $Builtin.Executor, %taskFu // CHECK: store [[INT]] %2, ptr [[EXECUTOR_IMPL_GEP]], align // CHECK: call swift{{(tail)?}}cc %swift.async_task_and_context @swift_task_create([[INT]] %5, ptr [[EXECUTOR_RECORD]] - %9 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor: $Optional, %optTaskExecutorOwned : $Optional, %taskFunction : $@async @callee_guaranteed () -> @error Error) : $(Builtin.NativeObject, Builtin.RawPointer) + %9 = builtin "createAsyncTask"(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor: $Optional, %optTaskExecutorOwned : $Optional, %taskName : $Optional, %taskFunction : $@async @callee_guaranteed () -> @error Error) : $(Builtin.NativeObject, Builtin.RawPointer) %10 = tuple_extract %9 : $(Builtin.NativeObject, Builtin.RawPointer), 0 strong_release %10 : $Builtin.NativeObject %12 = tuple () @@ -185,8 +191,9 @@ bb0(%flags : $Int, %taskGroup : $Builtin.RawPointer, %taskFunction: @owned $@asy %optTaskGroup = enum $Optional, #Optional.some!enumelt, %taskGroup : $Builtin.RawPointer %optTaskExecutor = enum $Optional, #Optional.none %optTaskExecutorOwned = enum $Optional, #Optional.none + %taskName = enum $Optional, #Optional.none // CHECK: [[NEW_TASK_AND_CONTEXT:%.*]] = call swift{{(tail)?}}cc %swift.async_task_and_context @swift_task_create( - %20 = builtin "createAsyncTask"<()>(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>) : $(Builtin.NativeObject, Builtin.RawPointer) + %20 = builtin "createAsyncTask"<()>(%flags : $Int, %optSerialExecutor : $Optional, %optTaskGroup : $Optional, %optTaskExecutor : $Optional, %optTaskExecutorOwned : $Optional, %taskName : $Optional, %taskFunction : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error Error) for <()>) : $(Builtin.NativeObject, Builtin.RawPointer) destroy_value %20 : $(Builtin.NativeObject, Builtin.RawPointer) %21 = tuple () return %21 : $() diff --git a/test/SILGen/async_builtins.swift b/test/SILGen/async_builtins.swift index 03cebdcec4b09..b7358581eea0b 100644 --- a/test/SILGen/async_builtins.swift +++ b/test/SILGen/async_builtins.swift @@ -22,10 +22,11 @@ public struct X { // CHECK: [[GROUP:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none + // CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE_FN:%.*]] = function_ref @$s4test1XV12launchFutureyyxlFxyYaKcfU_ : // CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]({{.*}}) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @error any Error) // CHECK: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for - // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) func launchFuture(_ value: T) { _ = Builtin.createAsyncTask(0) { () async throws -> T in return value @@ -38,10 +39,11 @@ public struct X { // CHECK: [[GROUP:%.*]] = enum $Optional, #Optional.some!enumelt, %1 : $Builtin.RawPointer // CHECK: [[TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none + // CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE_FN:%.*]] = function_ref @$s4test1XV16launchGroupChild_5groupyx_BptlFxyYaKcfU_ : // CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]({{.*}}) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @error any Error) // CHECK: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for - // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) func launchGroupChild(_ value: T, group: Builtin.RawPointer) { _ = Builtin.createAsyncTaskInGroup(0, group) { () async throws -> T in return value @@ -54,9 +56,10 @@ public struct X { // CHECK: [[GROUP:%.*]] = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.RawPointer // CHECK: [[TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none + // CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE_FN:%.*]] = function_ref @$s4test1XV26launchDiscardingGroupChild5groupyBp_tFyyYaKcfU_ : // CHECK: [[CLOSURE:%.*]] = thin_to_thick_function [[CLOSURE_FN]] - // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CLOSURE]] : $@async @callee_guaranteed () -> @error any Error) : $(Builtin.NativeObject, Builtin.RawPointer) + // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CLOSURE]] : $@async @callee_guaranteed () -> @error any Error) : $(Builtin.NativeObject, Builtin.RawPointer) func launchDiscardingGroupChild(group: Builtin.RawPointer) { _ = Builtin.createAsyncDiscardingTaskInGroup(0, group) { () async throws in return @@ -73,10 +76,11 @@ public struct X { // CHECK: [[GROUP:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.some!enumelt, %1 : $Builtin.Executor // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none + // CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE_FN:%.*]] = function_ref @$s4test1XV24launchFutureWithExecutor_8executoryx_BetlFxyYaKcfU_ : // CHECK: [[CLOSURE:%.*]] = partial_apply [callee_guaranteed] [[CLOSURE_FN]]({{.*}}) : $@convention(thin) @async <τ_0_0> (@in_guaranteed τ_0_0) -> (@out τ_0_0, @error any Error) // CHECK: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for - // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) + // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) func launchFutureWithExecutor(_ value: T, executor: Builtin.Executor) { _ = Builtin.createAsyncTaskWithExecutor(0, executor) { () async throws -> T in return value @@ -89,9 +93,10 @@ public struct X { // CHECK: [[GROUP:%.*]] = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.RawPointer // CHECK: [[TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.some!enumelt, %1 : $Builtin.Executor // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none + // CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE_FN:%.*]] = function_ref @$s4test1XV34launchDiscardingFutureWithExecutor5group8executoryBp_BetFyyYaKcfU_ : // CHECK: [[CLOSURE:%.*]] = thin_to_thick_function [[CLOSURE_FN]] - // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CLOSURE]] : $@async @callee_guaranteed () -> @error any Error) : $(Builtin.NativeObject, Builtin.RawPointer) + // CHECK: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[GROUP]] : $Optional, [[TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CLOSURE]] : $@async @callee_guaranteed () -> @error any Error) : $(Builtin.NativeObject, Builtin.RawPointer) func launchDiscardingFutureWithExecutor(group: Builtin.RawPointer, executor: Builtin.Executor) { _ = Builtin.createAsyncDiscardingTaskInGroupWithExecutor(0, group, executor) { () async throws in return @@ -105,9 +110,10 @@ public struct X { // CHECK-NEXT: [[OPT_GROUP:%.*]] = enum $Optional, #Optional.none // CHECK-NEXT: [[OPT_TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none +// CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE:%.*]] = partial_apply // CHECK-NEXT: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for -// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) func testCreateTask(value: T) { _ = Builtin.createTask(flags: 0) { value @@ -120,9 +126,10 @@ func testCreateTask(value: T) { // CHECK-NEXT: [[OPT_GROUP:%.*]] = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.RawPointer // CHECK-NEXT: [[OPT_TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none +// CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE:%.*]] = partial_apply // CHECK-NEXT: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for -// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) func testCreateTask(group: Builtin.RawPointer, value: T) { _ = Builtin.createTask(flags: 0, taskGroup: group) { value @@ -135,9 +142,10 @@ func testCreateTask(group: Builtin.RawPointer, value: T) { // CHECK-NEXT: [[OPT_GROUP:%.*]] = enum $Optional, #Optional.none // CHECK-NEXT: [[OPT_TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.Executor // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none +// CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE:%.*]] = partial_apply // CHECK-NEXT: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for -// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) func testCreateTask(taskExecutor: Builtin.Executor, value: T) { _ = Builtin.createTask(flags: 0, initialTaskExecutor: taskExecutor) { value @@ -150,9 +158,10 @@ func testCreateTask(taskExecutor: Builtin.Executor, value: T) { // CHECK-NEXT: [[OPT_GROUP:%.*]] = enum $Optional, #Optional.none // CHECK-NEXT: [[OPT_TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none +// CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE:%.*]] = partial_apply // CHECK-NEXT: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for -// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) func testCreateTask(serialExecutor: Builtin.Executor, value: T) { _ = Builtin.createTask(flags: 0, initialSerialExecutor: serialExecutor) { value @@ -164,15 +173,32 @@ func testCreateTask(serialExecutor: Builtin.Executor, value: T) { // CHECK-NEXT: [[OPT_GROUP:%.*]] = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.RawPointer // CHECK-NEXT: [[OPT_TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.some!enumelt, %1 : $Builtin.Executor // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none +// CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE:%.*]] = partial_apply // CHECK-NEXT: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for -// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) func testCreateTask(group: Builtin.RawPointer, executor: Builtin.Executor, value: T) { _ = Builtin.createTask(flags: 0, taskGroup: group, initialTaskExecutor: executor) { value } } +// CHECK-LABEL: sil hidden [ossa] @$s4test0A15CreateNamedTask5group8executor4name5valueyBp_BeBpxtlF +// CHECK: [[FLAGS:%.*]] = apply +// CHECK-NEXT: [[OPT_SERIAL_EXECUTOR:%.*]] = enum $Optional, #Optional.none!enumelt +// CHECK-NEXT: [[OPT_GROUP:%.*]] = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.RawPointer +// CHECK-NEXT: [[OPT_TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.some!enumelt, %1 : $Builtin.Executor +// CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none +// CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.some!enumelt, %2 : $Builtin.RawPointer +// CHECK: [[CLOSURE:%.*]] = partial_apply +// CHECK-NEXT: [[CONVERTED_CLOSURE:%.*]] = convert_function [[CLOSURE]] : $@async @callee_guaranteed () -> (@out T, @error any Error) to $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for +// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CONVERTED_CLOSURE]] : $@async @callee_guaranteed @substituted <τ_0_0> () -> (@out τ_0_0, @error any Error) for ) : $(Builtin.NativeObject, Builtin.RawPointer) +func testCreateNamedTask(group: Builtin.RawPointer, executor: Builtin.Executor, name: Builtin.RawPointer, value: T) { + _ = Builtin.createTask(flags: 0, taskGroup: group, initialTaskExecutor: executor, taskName: name) { + value + } +} + // A discarding task without a group is not currently a legal // combination; if we start enforcing that, feel free to change this // as needed. @@ -182,8 +208,9 @@ func testCreateTask(group: Builtin.RawPointer, executor: Builtin.Executor, va // CHECK-NEXT: [[OPT_GROUP:%.*]] = enum $Optional, #Optional.none // CHECK-NEXT: [[OPT_TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.none // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none +// CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE:%.*]] = partial_apply -// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CLOSURE]] : $@async @callee_guaranteed () -> @error any Error) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CLOSURE]] : $@async @callee_guaranteed () -> @error any Error) : $(Builtin.NativeObject, Builtin.RawPointer) func testCreateDiscardingTask(value: T) { _ = Builtin.createDiscardingTask(flags: 0) { _ = value @@ -199,13 +226,15 @@ func testCreateDiscardingTask(value: T) { // CHECK-NEXT: [[OPT_GROUP:%.*]] = enum $Optional, #Optional.some!enumelt, %0 : $Builtin.RawPointer // CHECK-NEXT: [[OPT_TASK_EXECUTOR:%.*]] = enum $Optional, #Optional.some!enumelt, %1 : $Builtin.Executor // CHECK: [[TASK_EXECUTOR_OWNED:%.*]] = enum $Optional<[[TASK_EXECUTOR_OWNED_TYPE:.*]]>, #Optional.none +// CHECK: [[TASK_NAME:%.*]] = enum $Optional, #Optional.none // CHECK: [[CLOSURE:%.*]] = partial_apply -// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[CLOSURE]] : $@async @callee_guaranteed () -> @error any Error) : $(Builtin.NativeObject, Builtin.RawPointer) +// CHECK-NEXT: builtin "createAsyncTask"([[FLAGS]] : $Int, [[OPT_SERIAL_EXECUTOR]] : $Optional, [[OPT_GROUP]] : $Optional, [[OPT_TASK_EXECUTOR]] : $Optional, [[TASK_EXECUTOR_OWNED]] : $Optional<[[TASK_EXECUTOR_OWNED_TYPE]]>, [[TASK_NAME]] : $Optional, [[CLOSURE]] : $@async @callee_guaranteed () -> @error any Error) : $(Builtin.NativeObject, Builtin.RawPointer) func testCreateDiscardingTask(group: Builtin.RawPointer, executor: Builtin.Executor, value: T) { _ = Builtin.createDiscardingTask(flags: 0, taskGroup: group, initialTaskExecutor: executor) { _ = value } } + // CHECK-LABEL: sil [ossa] @$s4test26usesWithUnsafeContinuationyyYaF : $@convention(thin) @async () -> () { public func usesWithUnsafeContinuation() async { // trivial resume type diff --git a/test/abi/macOS/arm64/concurrency.swift b/test/abi/macOS/arm64/concurrency.swift index d7198e513ac90..e0968cea980f7 100644 --- a/test/abi/macOS/arm64/concurrency.swift +++ b/test/abi/macOS/arm64/concurrency.swift @@ -383,3 +383,8 @@ Added: _$sSct16escalatePriority_2toySct_ScPtFZ Added: _$sScT16escalatePriority_2toyScTyxq_G_ScPtFZ Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolationxxyYaq_YKXE_yScPYbXEScA_pSgYitYaq_YKs5ErrorR_r0_lF Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolationxxyYaq_YKXE_yScPYbXEScA_pSgYitYaq_YKs5ErrorR_r0_lFTu + +// task names +Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ +Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV +Added: _swift_task_getCurrentTaskName \ No newline at end of file diff --git a/test/abi/macOS/x86_64/concurrency.swift b/test/abi/macOS/x86_64/concurrency.swift index 02b8852656961..96c50db4dbc7d 100644 --- a/test/abi/macOS/x86_64/concurrency.swift +++ b/test/abi/macOS/x86_64/concurrency.swift @@ -383,3 +383,8 @@ Added: _$sSct16escalatePriority_2toySct_ScPtFZ Added: _$sScT16escalatePriority_2toyScTyxq_G_ScPtFZ Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolationxxyYaq_YKXE_yScPYbXEScA_pSgYitYaq_YKs5ErrorR_r0_lF Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolationxxyYaq_YKXE_yScPYbXEScA_pSgYitYaq_YKs5ErrorR_r0_lFTu + +// task names +Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ +Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV +Added: _swift_task_getCurrentTaskName \ No newline at end of file diff --git a/test/embedded/concurrency-taskgroup2.swift b/test/embedded/concurrency-discardingtaskgroup.swift similarity index 100% rename from test/embedded/concurrency-taskgroup2.swift rename to test/embedded/concurrency-discardingtaskgroup.swift diff --git a/test/embedded/dependencies-concurrency-custom-executor.swift b/test/embedded/dependencies-concurrency-custom-executor.swift index 4f3e4c172c25f..8a88a02577cc9 100644 --- a/test/embedded/dependencies-concurrency-custom-executor.swift +++ b/test/embedded/dependencies-concurrency-custom-executor.swift @@ -26,6 +26,7 @@ // DEP: _putchar // DEP: _puts // DEP: _strlen +// DEP: _strcpy // DEP: _vprintf // DEP: _vsnprintf diff --git a/test/embedded/dependencies-concurrency-custom-executor2.swift b/test/embedded/dependencies-concurrency-custom-executor2.swift index edb1c91103c5b..540ac54e7d029 100644 --- a/test/embedded/dependencies-concurrency-custom-executor2.swift +++ b/test/embedded/dependencies-concurrency-custom-executor2.swift @@ -25,6 +25,7 @@ // DEP: _putchar // DEP: _puts // DEP: _strlen +// DEP: _strcpy // DEP: _vprintf // DEP: _vsnprintf diff --git a/test/embedded/dependencies-concurrency.swift b/test/embedded/dependencies-concurrency.swift index f4b9b61bfa1cb..70a12bbfb81e5 100644 --- a/test/embedded/dependencies-concurrency.swift +++ b/test/embedded/dependencies-concurrency.swift @@ -26,6 +26,7 @@ // DEP: _putchar // DEP: _puts // DEP: _strlen +// DEP: _strcpy // DEP: _vprintf // DEP: _vsnprintf diff --git a/test/embedded/dependencies-concurrency2.swift b/test/embedded/dependencies-concurrency2.swift index 3eb7d25076beb..50e3ba3a7029b 100644 --- a/test/embedded/dependencies-concurrency2.swift +++ b/test/embedded/dependencies-concurrency2.swift @@ -23,6 +23,7 @@ // DEP: _putchar // DEP: _puts // DEP: _strlen +// DEP: _strcpy // DEP: _swift_task_asyncMainDrainQueueImpl // DEP: _swift_task_enqueueGlobalImpl // DEP: _swift_task_getMainExecutorImpl From c5e18fad063e3bf592e3e8710360426c6d62b0ff Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 21 Feb 2025 11:29:39 +0900 Subject: [PATCH 7/9] [Concurrency] Workarounds and cleanups for gyb based addTask --- lib/IRGen/GenConcurrency.cpp | 6 +- stdlib/public/Concurrency/CMakeLists.txt | 3 - .../SourceCompatibilityShims.swift | 225 +++++++++++++++--- .../Concurrency/TaskGroup+Embedded.swift | 3 +- .../Concurrency/TaskGroup+addTask.swift.gyb | 2 +- 5 files changed, 202 insertions(+), 37 deletions(-) diff --git a/lib/IRGen/GenConcurrency.cpp b/lib/IRGen/GenConcurrency.cpp index 689688abba285..95945c087da3d 100644 --- a/lib/IRGen/GenConcurrency.cpp +++ b/lib/IRGen/GenConcurrency.cpp @@ -686,9 +686,9 @@ struct TaskGroupRecordTraits { void initialize(IRGenFunction &IGF, Address recordAddr, Explosion &taskGroup) const { - auto record = - IGF.Builder.CreateStructGEP(recordAddr, 1, 2 * IGF.IGM.getPointerSize()); - IGF.Builder.CreateStore(taskGroup.claimNext(), record); + IGF.Builder.CreateStore( + taskGroup.claimNext(), + IGF.Builder.CreateStructGEP(recordAddr, 1, 2 * IGF.IGM.getPointerSize())); } }; diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index bc45f951e9be9..f2fcde252df11 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -277,9 +277,6 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC ${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES} - # FIXME: Need to include the TaskGroup+addTask.swift.gyb here, - # but there's trouble with that: Unknown architecture: x86_64-apple-macos - SWIFT_COMPILE_FLAGS ${extra_swift_compile_flags} -enable-experimental-feature Embedded -parse-stdlib -DSWIFT_CONCURRENCY_EMBEDDED diff --git a/stdlib/public/Concurrency/SourceCompatibilityShims.swift b/stdlib/public/Concurrency/SourceCompatibilityShims.swift index a6caac782987a..b3ffbdc8a8796 100644 --- a/stdlib/public/Concurrency/SourceCompatibilityShims.swift +++ b/stdlib/public/Concurrency/SourceCompatibilityShims.swift @@ -89,7 +89,7 @@ extension Task where Success == Never, Failure == Never { @available(SwiftStdlib 5.1, *) extension Task where Failure == Error { -#if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @discardableResult @_alwaysEmitIntoClient @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") @@ -99,7 +99,7 @@ extension Task where Failure == Error { ) -> Task { fatalError("Unavailable in task-to-thread concurrency model") } -#else + #else @discardableResult @_alwaysEmitIntoClient @available(*, deprecated, message: "`Task.runDetached` was replaced by `Task.detached` and will be removed shortly.") @@ -109,7 +109,7 @@ extension Task where Failure == Error { ) -> Task { detached(priority: priority, operation: operation) } -#endif + #endif } #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @@ -264,9 +264,9 @@ extension Task where Success == Never, Failure == Never { @available(*, deprecated, message: "`Task.withGroup` was replaced by `withThrowingTaskGroup` and `withTaskGroup` and will be removed shortly.") @_alwaysEmitIntoClient public static func withGroup( - resultType: TaskResult.Type, - returning returnType: BodyResult.Type = BodyResult.self, - body: (inout Task.Group) async throws -> BodyResult + resultType: TaskResult.Type, + returning returnType: BodyResult.Type = BodyResult.self, + body: (inout Task.Group) async throws -> BodyResult ) async rethrows -> BodyResult { try await withThrowingTaskGroup(of: resultType) { group in try await body(&group) @@ -274,10 +274,34 @@ extension Task where Success == Never, Failure == Never { } } +@available(SwiftStdlib 5.1, *) +extension Task { + @available(*, deprecated, message: "get() has been replaced by .value") + @_alwaysEmitIntoClient + public func get() async throws -> Success { + return try await value + } + + @available(*, deprecated, message: "getResult() has been replaced by .result") + @_alwaysEmitIntoClient + public func getResult() async -> Result { + return await result + } +} + +@available(SwiftStdlib 5.1, *) +extension Task where Failure == Never { + @available(*, deprecated, message: "get() has been replaced by .value") + @_alwaysEmitIntoClient + public func get() async -> Success { + return await value + } +} + +#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.1, *) extension TaskGroup { - @available(SwiftStdlib 5.1, *) - @available(*, deprecated, renamed: "addTaskUnlessCancelled(priority:operation:)") + @available(*, deprecated, renamed: "addTask(priority:operation:)") @_alwaysEmitIntoClient public mutating func add( priority: TaskPriority? = nil, @@ -294,7 +318,7 @@ extension TaskGroup { priority: TaskPriority? = nil, operation: __owned @Sendable @escaping () async -> ChildTaskResult ) { - self.addTask(priority: priority, operation: operation) + addTask(priority: priority, operation: operation) } @available(*, deprecated, renamed: "addTaskUnlessCancelled(priority:operation:)") @@ -324,7 +348,94 @@ extension TaskGroup { addTaskUnlessCancelled(priority: priority, operation: operation) } } +#else +@available(SwiftStdlib 5.1, *) +extension TaskGroup { + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") + public mutating func add( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) async -> Bool { + fatalError("Unavailable in task-to-thread concurrency model") + } + @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") + @_alwaysEmitIntoClient + public mutating func add( + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) async -> Bool { + return self.addTaskUnlessCancelled { + await operation() + } + } + + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") + public mutating func spawn( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) { + fatalError("Unavailable in task-to-thread concurrency model") + } + + @available(*, deprecated, renamed: "addTask(operation:)") + @_alwaysEmitIntoClient + public mutating func spawn( + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) { + addTask(operation: operation) + } + + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") + public mutating func spawnUnlessCancelled( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) -> Bool { + fatalError("Unavailable in task-to-thread concurrency model") + } + + @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") + @_alwaysEmitIntoClient + public mutating func spawnUnlessCancelled( + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) -> Bool { + addTaskUnlessCancelled(operation: operation) + } + + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") + public mutating func async( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) { + fatalError("Unavailable in task-to-thread concurrency model") + } + + @available(*, deprecated, renamed: "addTask(operation:)") + @_alwaysEmitIntoClient + public mutating func async( + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) { + addTask(operation: operation) + } + + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") + public mutating func asyncUnlessCancelled( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) -> Bool { + fatalError("Unavailable in task-to-thread concurrency model") + } + + @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") + @_alwaysEmitIntoClient + public mutating func asyncUnlessCancelled( + operation: __owned @Sendable @escaping () async -> ChildTaskResult + ) -> Bool { + addTaskUnlessCancelled(operation: operation) + } +} +#endif + +#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.1, *) extension ThrowingTaskGroup { @available(*, deprecated, renamed: "addTask(priority:operation:)") @@ -374,34 +485,92 @@ extension ThrowingTaskGroup { addTaskUnlessCancelled(priority: priority, operation: operation) } } - +#else @available(SwiftStdlib 5.1, *) -extension Task { - @available(*, deprecated, message: "get() has been replaced by .value") +extension ThrowingTaskGroup { + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") + public mutating func add( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) async -> Bool { + fatalError("Unavailable in task-to-thread concurrency model") + } + + @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") @_alwaysEmitIntoClient - public func get() async throws -> Success { - return try await value + public mutating func add( + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) async -> Bool { + return self.addTaskUnlessCancelled { + try await operation() + } } - @available(*, deprecated, message: "getResult() has been replaced by .result") + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") + public mutating func spawn( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) { + fatalError("Unavailable in task-to-thread concurrency model") + } + + @available(*, deprecated, renamed: "addTask(operation:)") @_alwaysEmitIntoClient - public func getResult() async -> Result { - return await result + public mutating func spawn( + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) { + addTask(operation: operation) } -} -@available(SwiftStdlib 5.1, *) -extension Task where Failure == Never { - @available(*, deprecated, message: "get() has been replaced by .value") + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") + public mutating func spawnUnlessCancelled( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) -> Bool { + fatalError("Unavailable in task-to-thread concurrency model") + } + + @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") @_alwaysEmitIntoClient - public func get() async -> Success { - return await value + public mutating func spawnUnlessCancelled( + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) -> Bool { + addTaskUnlessCancelled(operation: operation) + } + + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTask(operation:)") + public mutating func async( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) { + fatalError("Unavailable in task-to-thread concurrency model") + } + + @available(*, deprecated, renamed: "addTask(operation:)") + @_alwaysEmitIntoClient + public mutating func async( + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) { + addTask(operation: operation) + } + + @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model", renamed: "addTaskUnlessCancelled(operation:)") + public mutating func asyncUnlessCancelled( + priority: TaskPriority? = nil, + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) -> Bool { + fatalError("Unavailable in task-to-thread concurrency model") } -} -// NOTE: We had to move sources touching TaskGroup addTask APIs into TaskGroup+addTask.swift.gyb -// due to the build having trouble with using the generated source in the same module -// rdar://145171772 + @available(*, deprecated, renamed: "addTaskUnlessCancelled(operation:)") + @_alwaysEmitIntoClient + public mutating func asyncUnlessCancelled( + operation: __owned @Sendable @escaping () async throws -> ChildTaskResult + ) -> Bool { + addTaskUnlessCancelled(operation: operation) + } +} +#endif @available(SwiftStdlib 5.1, *) @available(*, deprecated, message: "please use UnsafeContinuation<..., Error>") @@ -409,4 +578,4 @@ public typealias UnsafeThrowingContinuation = UnsafeContinuation @available(SwiftStdlib 5.1, *) @available(*, deprecated, renamed: "UnownedJob") -public typealias PartialAsyncTask = UnownedJob +public typealias PartialAsyncTask = UnownedJob \ No newline at end of file diff --git a/stdlib/public/Concurrency/TaskGroup+Embedded.swift b/stdlib/public/Concurrency/TaskGroup+Embedded.swift index 9ead03d677005..60e7b518b2a11 100644 --- a/stdlib/public/Concurrency/TaskGroup+Embedded.swift +++ b/stdlib/public/Concurrency/TaskGroup+Embedded.swift @@ -12,7 +12,7 @@ import Swift -// FIXME: This is a workaround for trouble including gyb-generated sources in the embedded build. +// FIXME: This is a workaround for trouble including gyb-generated sources #if SWIFT_CONCURRENCY_EMBEDDED @@ -38,7 +38,6 @@ extension TaskGroup { let builtinSerialExecutor = Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor - _ = Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, diff --git a/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb b/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb index b4e8fe1e2848d..07e509767b813 100644 --- a/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb +++ b/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb @@ -59,7 +59,7 @@ import Swift % ], % ['addTask', 'addTaskUnlessCancelled'], % [ -% 'executorPreference taskExecutor: (any TaskExecutor)?', +% 'executorPreference taskExecutor: (any TaskExecutor)? = nil', % 'priority: TaskPriority? = nil', % # throws and ChildTaskResult will be adjusted per task group type % 'operation: sending @escaping @isolated(any) () async throws -> ChildTaskResult' From 5b0322e69f70dd8632cc77487c706e72feda4456 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Fri, 21 Feb 2025 14:25:18 +0900 Subject: [PATCH 8/9] [Concurrency] Pass string down using utf8CString --- stdlib/public/Concurrency/Task.swift | 16 ++++++++-------- .../Concurrency/TaskGroup+addTask.swift.gyb | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 06ef5fa797a87..d8c60ca0de311 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -768,11 +768,11 @@ extension Task where Failure == Never { #if $BuiltinCreateAsyncTaskName if var name { task = - name.withUTF8 { nameBytes in + name.utf8CString.withUnsafeBufferPointer { nameBytes in Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, - taskName: nameBytes.baseAddress?._rawValue, + taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } } @@ -937,11 +937,11 @@ self._task = task #if $BuiltinCreateAsyncTaskName if var name { task = - name.withUTF8 { nameBytes in + name.utf8CString.withUnsafeBufferPointer { nameBytes in Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, - taskName: nameBytes.baseAddress?._rawValue, + taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } } @@ -1078,11 +1078,11 @@ extension Task where Failure == Never { #if $BuiltinCreateAsyncTaskName if var name { task = - name.withUTF8 { nameBytes in + name.utf8CString.withUnsafeBufferPointer { nameBytes in Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, - taskName: nameBytes.baseAddress?._rawValue, + taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } } @@ -1220,11 +1220,11 @@ extension Task where Failure == Error { #if $BuiltinCreateAsyncTaskName if var name { task = - name.withUTF8 { nameBytes in + name.utf8CString.withUnsafeBufferPointer { nameBytes in Builtin.createTask( flags: flags, initialSerialExecutor: builtinSerialExecutor, - taskName: nameBytes.baseAddress?._rawValue, + taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } } diff --git a/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb b/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb index 07e509767b813..6b829e37ddb49 100644 --- a/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb +++ b/stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb @@ -245,13 +245,13 @@ extension ${TYPE} { #if $BuiltinCreateAsyncTaskName if var name { task = - name.withUTF8 { nameBytes in + name.utf8CString.withUnsafeBufferPointer { nameBytes in ${TASK_CREATE_FN}( flags: flags, initialSerialExecutor: builtinSerialExecutor, taskGroup: _group, initialTaskExecutorConsuming: taskExecutor, - taskName: nameBytes.baseAddress?._rawValue, + taskName: nameBytes.baseAddress!._rawValue, operation: operation).0 } } From 94376cdea16542381b85ddfaf9cfd13ccbc9e807 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Fri, 21 Feb 2025 14:25:42 +0900 Subject: [PATCH 9/9] [Concurrency] Starting tasks synchronously on caller context --- include/swift/Runtime/Concurrency.h | 3 + .../CompatibilityOverrideConcurrency.def | 5 ++ stdlib/public/Concurrency/Actor.cpp | 23 +++++++ stdlib/public/Concurrency/Task.swift | 39 +++++++++-- .../Runtime/startSynchronously.swift | 65 +++++++++++++++++++ test/abi/macOS/arm64/concurrency.swift | 5 +- .../CompatibilityOverrideConcurrency.cpp | 9 +++ 7 files changed, 144 insertions(+), 5 deletions(-) create mode 100644 test/Concurrency/Runtime/startSynchronously.swift diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index e47231fc96def..cfdb080820cf3 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -1028,6 +1028,9 @@ const char *swift_task_getCurrentTaskName(void); SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) void swift_task_startOnMainActor(AsyncTask* job); +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_startSynchronously(AsyncTask* job); + /// Donate this thread to the global executor until either the /// given condition returns true or we've run out of cooperative /// tasks to run. diff --git a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def index ba7bd75d9437a..40cd713e4f629 100644 --- a/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def +++ b/stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def @@ -438,6 +438,11 @@ OVERRIDE_TASK(task_startOnMainActor, void, SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::, (AsyncTask *task), (task)) +// In ACTOR since we need ExecutorTracking info +OVERRIDE_ACTOR(task_startSynchronously, void, + SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), + swift::, (AsyncTask *task), (task)) + #undef OVERRIDE #undef OVERRIDE_ACTOR #undef OVERRIDE_TASK diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index 1b80ffd9e1d2c..bb87a1c89c0d6 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -579,6 +579,29 @@ swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor) { isCurrentExecutorFlag); } +SWIFT_CC(swift) +static void swift_task_startSynchronouslyImpl(AsyncTask* task) { + fprintf(stderr, "[%s:%d](%s) run it...\n", __FILE_NAME__, __LINE__, __FUNCTION__); + swift_retain(task); + + AsyncTask * originalTask = _swift_task_clearCurrent(); + auto currentTracking = ExecutorTrackingInfo::current(); + if (currentTracking) { + fprintf(stderr, "[%s:%d](%s) executor tracking active exec = %p\n", __FILE_NAME__, __LINE__, __FUNCTION__, currentTracking->getActiveExecutor()); + fprintf(stderr, "[%s:%d](%s) executor tracking task executor = %p\n", __FILE_NAME__, __LINE__, __FUNCTION__, currentTracking->getTaskExecutor()); + auto currentExecutor = currentTracking->getActiveExecutor(); + + fprintf(stderr, "[%s:%d](%s) run sync task = %p\n", __FILE_NAME__, __LINE__, __FUNCTION__, + task); + swift_job_run(task, currentExecutor); + } else { + assert(false && "to the special inline executor here"); + } + fprintf(stderr, "[%s:%d](%s) set current to original = %p\n", __FILE_NAME__, __LINE__, __FUNCTION__, + originalTask); + _swift_task_setCurrent(originalTask); +} + /// Logging level for unexpected executors: /// 0 - no logging -- will be IGNORED when Swift6 mode of isCurrentExecutor is used /// 1 - warn on each instance -- will be IGNORED when Swift6 mode of isCurrentExecutor is used diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index d8c60ca0de311..f7e611417cc32 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -273,6 +273,8 @@ extension Task: Equatable { } } +// ==== Starting tasks synchronously ------------------------------------------- + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY && !SWIFT_CONCURRENCY_EMBEDDED @available(SwiftStdlib 5.9, *) extension Task where Failure == Error { @@ -282,14 +284,14 @@ extension Task where Failure == Error { @discardableResult public static func startOnMainActor( priority: TaskPriority? = nil, - @_inheritActorContext @_implicitSelfCapture _ work: __owned @Sendable @escaping @MainActor() async throws -> Success + @_inheritActorContext @_implicitSelfCapture _ operation: __owned @Sendable @escaping @MainActor() async throws -> Success ) -> Task { let flags = taskCreateFlags(priority: priority, isChildTask: false, copyTaskLocals: true, inheritContext: true, enqueueJob: false, addPendingGroupTaskUnconditionally: false, isDiscardingTask: false) - let (task, _) = Builtin.createAsyncTask(flags, work) + let (task, _) = Builtin.createAsyncTask(flags, operation) _startTaskOnMainActor(task) return Task(task) } @@ -305,20 +307,46 @@ extension Task where Failure == Never { @discardableResult public static func startOnMainActor( priority: TaskPriority? = nil, - @_inheritActorContext @_implicitSelfCapture _ work: __owned @Sendable @escaping @MainActor() async -> Success + @_inheritActorContext @_implicitSelfCapture _ operation: __owned @Sendable @escaping @MainActor() async -> Success ) -> Task { let flags = taskCreateFlags(priority: priority, isChildTask: false, copyTaskLocals: true, inheritContext: true, enqueueJob: false, addPendingGroupTaskUnconditionally: false, isDiscardingTask: false) - let (task, _) = Builtin.createAsyncTask(flags, work) + let (task, _) = Builtin.createAsyncTask(flags, operation) _startTaskOnMainActor(task) return Task(task) } } #endif +@available(SwiftStdlib 5.9, *) +extension Task where Failure == Never { + @available(SwiftStdlib 5.9, *) + @discardableResult + public static func startSynchronously( + name: String? = nil, + priority: TaskPriority? = nil, + @_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> Success + ) -> Task { + let flags = taskCreateFlags( + priority: priority, + isChildTask: false, + copyTaskLocals: true, + inheritContext: true, + enqueueJob: false, // don't enqueue, we'll run it manually + addPendingGroupTaskUnconditionally: false, + isDiscardingTask: false + ) + + let (task, _) = Builtin.createAsyncTask(flags, operation) + _startTaskSynchronously(task) + return Task(task) + } +} + + // ==== Task Priority ---------------------------------------------------------- /// The priority of a task. @@ -1445,6 +1473,9 @@ public func _getCurrentAsyncTask() -> Builtin.NativeObject? @_silgen_name("swift_task_startOnMainActor") fileprivate func _startTaskOnMainActor(_ task: Builtin.NativeObject) +@_silgen_name("swift_task_startSynchronously") +fileprivate func _startTaskSynchronously(_ task: Builtin.NativeObject) + @available(SwiftStdlib 5.1, *) @_silgen_name("swift_task_getJobFlags") func getJobFlags(_ task: Builtin.NativeObject) -> JobFlags diff --git a/test/Concurrency/Runtime/startSynchronously.swift b/test/Concurrency/Runtime/startSynchronously.swift new file mode 100644 index 0000000000000..bf94595e2485d --- /dev/null +++ b/test/Concurrency/Runtime/startSynchronously.swift @@ -0,0 +1,65 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift -Xfrontend -disable-availability-checking %s -swift-version 6 -o %t/a.out +// RUN: %target-codesign %t/a.out +// RUN: %target-run %t/a.out | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: concurrency_runtime +// UNSUPPORTED: back_deployment_runtime +// UNSUPPORTED: back_deploy_concurrency +// UNSUPPORTED: use_os_stdlib +// UNSUPPORTED: freestanding + +import StdlibUnittest +@_spi(MainActorUtilities) import _Concurrency + +actor SomeActor { +} + +@globalActor +actor MyGlobalActor { + static let shared: MyGlobalActor = MyGlobalActor() +} + +// TODO: use better synchronization here rather than just rely on timing + +func syncOnMyGlobalActor() -> [Task] { + MyGlobalActor.shared.preconditionIsolated("Should be executing on the global actor here") + print("Confirmed to be on @MyGlobalActor") + + // This task must be guaranteed to happen AFTER 'tt' because we are already on this actor + // so this enqueue must happen after we give up the actor. + print("Step: schedule Task { @MyGlobalActor }, before startSynchronously \(#function) @ \(#line)") + let t1 = Task { @MyGlobalActor in + print("Step: inside Task { @MyGlobalActor }, before startSynchronously \(#function) @ \(#line)") + } + + print("Step: before startSynchronously \(#function) @ \(#line)") + let tt = Task.startSynchronously { @MyGlobalActor in + print("Step: inside startSynchronously \(#function) @ \(#line)") + try! await Task.sleep(for: .seconds(1)) + print("Step: after sleep, inside startSynchronously \(#function) @ \(#line)") + } + + // CHECK: Confirmed to be on @MyGlobalActor + // CHECK: Step: schedule Task { @MyGlobalActor }, before startSynchronously syncOnMyGlobalActor() + // CHECK: Step: before startSynchronously syncOnMyGlobalActor() + // We immediately entered the startSynchronously task: + // CHECK: Step: inside startSynchronously syncOnMyGlobalActor + // Only now we hit a suspension inside the sync task, and allow the other task to run + // CHECK: Step: inside Task { @MyGlobalActor }, before startSynchronously syncOnMyGlobalActor() + + return [t1, tt] +} + +print("==== ------------------------------------------------------------------") +await Task { @MyGlobalActor in + MyGlobalActor.shared.preconditionIsolated("Should be executing on the global actor here") + let ts = syncOnMyGlobalActor() + + print("Waiting for tasks...") + for t in ts { + await t.value + } +}.value diff --git a/test/abi/macOS/arm64/concurrency.swift b/test/abi/macOS/arm64/concurrency.swift index e0968cea980f7..9053dda4bb033 100644 --- a/test/abi/macOS/arm64/concurrency.swift +++ b/test/abi/macOS/arm64/concurrency.swift @@ -387,4 +387,7 @@ Added: _$ss33withTaskPriorityEscalationHandler9operation02onC9Escalated9isolatio // task names Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvgZ Added: _$sScTss5NeverORszABRs_rlE4nameSSSgvpZMV -Added: _swift_task_getCurrentTaskName \ No newline at end of file +Added: _swift_task_getCurrentTaskName + +// Task.startSynchronously +Added: _swift_task_startSynchronously \ No newline at end of file diff --git a/unittests/runtime/CompatibilityOverrideConcurrency.cpp b/unittests/runtime/CompatibilityOverrideConcurrency.cpp index e5dc8fe5e9cf6..265c96cffbe6c 100644 --- a/unittests/runtime/CompatibilityOverrideConcurrency.cpp +++ b/unittests/runtime/CompatibilityOverrideConcurrency.cpp @@ -109,6 +109,11 @@ static void swift_task_startOnMainActor_override(AsyncTask* task) { Ran = true; } +SWIFT_CC(swift) +static void swift_task_startSynchronously_override(AsyncTask* task) { + Ran = true; +} + #ifdef RUN_ASYNC_MAIN_DRAIN_QUEUE_TEST [[noreturn]] SWIFT_CC(swift) static void swift_task_asyncMainDrainQueue_override_fn( @@ -330,6 +335,10 @@ TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startOnMainActorImpl) { swift_task_startOnMainActor(nullptr); } +TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_startSynchronously) { + swift_task_startSynchronously(nullptr); +} + TEST_F(CompatibilityOverrideConcurrencyTest, test_swift_task_isCurrentExecutorWithFlags) { swift_task_isCurrentExecutorWithFlags(