Skip to content

Commit b1c345f

Browse files
authored
Merge pull request #80266 from al45tair/custom-executors-take2
[Concurrency] Provide a Swift interface for custom main and global executors.
2 parents e5d0cd4 + 6e28716 commit b1c345f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+3624
-427
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,15 @@ NOTE(rbi_add_generic_parameter_sendable_conformance,none,
11341134
"consider making generic parameter %0 conform to the 'Sendable' protocol",
11351135
(Type))
11361136

1137+
// Concurrency related diagnostics
1138+
ERROR(cannot_find_executor_factory_type, none,
1139+
"the specified executor factory '%0' could not be found", (StringRef))
1140+
ERROR(executor_factory_must_conform, none,
1141+
"the executor factory '%0' does not conform to 'ExecutorFactory'",
1142+
(StringRef))
1143+
ERROR(executor_factory_not_supported, none,
1144+
"deployment target too low for executor factory specification", ())
1145+
11371146
//===----------------------------------------------------------------------===//
11381147
// MARK: Misc Diagnostics
11391148
//===----------------------------------------------------------------------===//

include/swift/AST/FeatureAvailability.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,9 @@ FEATURE(IsolatedDeinit, (6, 1))
8080

8181
FEATURE(ValueGenericType, (6, 2))
8282
FEATURE(InitRawStructMetadata2, (6, 2))
83+
FEATURE(CustomGlobalExecutors, (6, 2))
84+
FEATURE(TaskExecutor, (6, 2))
8385

84-
FEATURE(TaskExecutor, FUTURE)
8586
FEATURE(Differentiation, FUTURE)
8687
FEATURE(ClearSensitive, FUTURE)
8788
FEATURE(UpdatePureObjCClassMetadata, FUTURE)

include/swift/AST/KnownProtocols.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ PROTOCOL(Executor)
9797
PROTOCOL(SerialExecutor)
9898
PROTOCOL(TaskExecutor)
9999
PROTOCOL(GlobalActor)
100+
PROTOCOL(ExecutorFactory)
100101

101102
PROTOCOL_(BridgedNSError)
102103
PROTOCOL_(BridgedStoredNSError)

include/swift/Basic/LangOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,10 @@ namespace swift {
409409
/// Specifies how strict concurrency checking will be.
410410
StrictConcurrency StrictConcurrencyLevel = StrictConcurrency::Minimal;
411411

412+
/// Specifies the name of the executor factory to use to create the
413+
/// default executors for Swift Concurrency.
414+
std::optional<std::string> ExecutorFactory;
415+
412416
/// Enable experimental concurrency model.
413417
bool EnableExperimentalConcurrency = false;
414418

include/swift/Option/Options.td

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,16 @@ def default_isolation_EQ : Joined<["-"], "default-isolation=">,
998998
Flags<[FrontendOption]>,
999999
Alias<default_isolation>;
10001000

1001+
def executor_factory : JoinedOrSeparate<["-"], "executor-factory">,
1002+
Flags<[FrontendOption]>,
1003+
HelpText<"Specify the factory to use to create the default executors for "
1004+
"Swift Concurrency. This must be a type conforming to the "
1005+
"'ExecutorFactory' protocol.">,
1006+
MetaVarName<"<factory-type>">;
1007+
def executor_factory_EQ : Joined<["-"], "executor-factory=">,
1008+
Flags<[FrontendOption]>,
1009+
Alias<executor_factory>;
1010+
10011011
def enable_experimental_feature :
10021012
Separate<["-"], "enable-experimental-feature">,
10031013
Flags<[FrontendOption, ModuleInterfaceOption]>,

include/swift/Runtime/Concurrency.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,29 @@ void swift_task_dealloc(void *ptr);
127127
SWIFT_EXPORT_FROM(swift_Concurrency)
128128
SWIFT_CC(swift) void swift_task_dealloc_through(void *ptr);
129129

130+
/// Deallocate memory in a task.
131+
///
132+
/// The pointer provided must be the last pointer allocated on
133+
/// this task that has not yet been deallocated; that is, memory
134+
/// must be allocated and deallocated in a strict stack discipline.
135+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
136+
void swift_task_dealloc(void *ptr);
137+
138+
/// Allocate memory in a job.
139+
///
140+
/// All allocations will be rounded to a multiple of MAX_ALIGNMENT;
141+
/// if the job does not support allocation, this will return NULL.
142+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
143+
void *swift_job_allocate(Job *job, size_t size);
144+
145+
/// Deallocate memory in a job.
146+
///
147+
/// The pointer provided must be the last pointer allocated on
148+
/// this task that has not yet been deallocated; that is, memory
149+
/// must be allocated and deallocated in a strict stack discipline.
150+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
151+
void swift_job_deallocate(Job *job, void *ptr);
152+
130153
/// Cancel a task and all of its child tasks.
131154
///
132155
/// This can be called from any thread.

lib/AST/ASTContext.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
14581458
case KnownProtocolKind::Executor:
14591459
case KnownProtocolKind::TaskExecutor:
14601460
case KnownProtocolKind::SerialExecutor:
1461+
case KnownProtocolKind::ExecutorFactory:
14611462
M = getLoadedModule(Id_Concurrency);
14621463
break;
14631464
case KnownProtocolKind::DistributedActor:

lib/ClangImporter/SortedCFDatabase.def.gyb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ with codecs.open(CFDatabaseFile, encoding='utf-8', errors='strict') as f:
3939
continue
4040

4141
# Otherwise, check for lines like FOO(BAR)
42-
m = re.match('^\w+\((\w+)\)', line)
42+
m = re.match(r'^\w+\((\w+)\)', line)
4343
if m:
4444
lineForName[m.group(1)] = line
4545
}%

lib/Driver/ToolChains.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI,
377377
arguments.push_back(inputArgs.MakeArgString(globalRemapping));
378378
}
379379

380+
if (inputArgs.hasArg(options::OPT_executor_factory)) {
381+
inputArgs.AddLastArg(arguments, options::OPT_executor_factory);
382+
}
383+
380384
// Pass through the values passed to -Xfrontend.
381385
inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend);
382386

lib/Frontend/CompilerInvocation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,12 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args,
13641364
Opts.enableFeature(Feature::RegionBasedIsolation);
13651365
}
13661366

1367+
// Get the executor factory name
1368+
if (const Arg *A = Args.getLastArg(OPT_executor_factory)) {
1369+
printf("Got executor-factory option\n");
1370+
Opts.ExecutorFactory = A->getValue();
1371+
}
1372+
13671373
Opts.WarnImplicitOverrides =
13681374
Args.hasArg(OPT_warn_implicit_overrides);
13691375

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7135,6 +7135,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
71357135
case KnownProtocolKind::Executor:
71367136
case KnownProtocolKind::SerialExecutor:
71377137
case KnownProtocolKind::TaskExecutor:
7138+
case KnownProtocolKind::ExecutorFactory:
71387139
case KnownProtocolKind::Sendable:
71397140
case KnownProtocolKind::UnsafeSendable:
71407141
case KnownProtocolKind::RangeReplaceableCollection:

lib/SILGen/SILGen.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,10 @@ FuncDecl *SILGenModule::getDeinitOnExecutor() {
458458
return lookupConcurrencyIntrinsic(getASTContext(), "_deinitOnExecutor");
459459
}
460460

461+
FuncDecl *SILGenModule::getCreateExecutors() {
462+
return lookupConcurrencyIntrinsic(getASTContext(), "_createExecutors");
463+
}
464+
461465
FuncDecl *SILGenModule::getExit() {
462466
ASTContext &C = getASTContext();
463467

@@ -507,6 +511,40 @@ FuncDecl *SILGenModule::getExit() {
507511
return exitFunction;
508512
}
509513

514+
Type SILGenModule::getConfiguredExecutorFactory() {
515+
auto &ctx = getASTContext();
516+
517+
ModuleDecl *module;
518+
519+
// Parse the executor factory name
520+
StringRef qualifiedName = *ctx.LangOpts.ExecutorFactory;
521+
StringRef typeName;
522+
523+
auto parts = qualifiedName.split('.');
524+
525+
if (parts.second.empty()) {
526+
// This was an unqualified name; assume it's relative to the main module
527+
module = ctx.MainModule;
528+
typeName = qualifiedName;
529+
} else {
530+
Identifier moduleName = ctx.getIdentifier(parts.first);
531+
module = ctx.getModuleByIdentifier(moduleName);
532+
typeName = parts.second;
533+
}
534+
535+
return ctx.getNamedSwiftType(module, typeName);
536+
}
537+
538+
Type SILGenModule::getDefaultExecutorFactory() {
539+
auto &ctx = getASTContext();
540+
541+
ModuleDecl *module = ctx.getModuleByIdentifier(ctx.Id_Concurrency);
542+
if (!module)
543+
return Type();
544+
545+
return ctx.getNamedSwiftType(module, "DefaultExecutorFactory");
546+
}
547+
510548
ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {
511549
if (NSErrorConformanceToError)
512550
return *NSErrorConformanceToError;

lib/SILGen/SILGen.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
559559
// Retrieve the _SwiftConcurrencyShims.exit intrinsic.
560560
FuncDecl *getExit();
561561

562+
/// Get the configured ExecutorFactory type.
563+
Type getConfiguredExecutorFactory();
564+
565+
/// Get the DefaultExecutorFactory type.
566+
Type getDefaultExecutorFactory();
567+
568+
/// Get the swift_createExecutors function.
569+
FuncDecl *getCreateExecutors();
570+
562571
SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess,
563572
KeyPathTypeKind typeKind);
564573

lib/SILGen/SILGenFunction.cpp

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ SILGenFunction::getOrCreateScope(const ast_scope::ASTScopeImpl *ASTScope,
486486
// Collapse BraceStmtScopes whose parent is a .*BodyScope.
487487
if (auto Parent = ASTScope->getParent().getPtrOrNull())
488488
if (Parent->getSourceRangeOfThisASTNode() ==
489-
ASTScope->getSourceRangeOfThisASTNode())
489+
ASTScope->getSourceRangeOfThisASTNode())
490490
return cache(getOrCreateScope(Parent, FnScope, InlinedAt));
491491

492492
// The calls to defer closures have cleanup source locations pointing to the
@@ -1387,6 +1387,28 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) {
13871387
}
13881388
}
13891389

1390+
static bool isCreateExecutorsFunctionAvailable(SILGenModule &SGM) {
1391+
FuncDecl *createExecutorsFuncDecl = SGM.getCreateExecutors();
1392+
if (!createExecutorsFuncDecl)
1393+
return false;
1394+
1395+
auto &ctx = createExecutorsFuncDecl->getASTContext();
1396+
1397+
if (ctx.LangOpts.hasFeature(Feature::Embedded))
1398+
return true;
1399+
1400+
if (!ctx.LangOpts.DisableAvailabilityChecking) {
1401+
auto deploymentAvailability = AvailabilityRange::forDeploymentTarget(ctx);
1402+
auto runtimeAvailability = AvailabilityRange::forRuntimeTarget(ctx);
1403+
auto declAvailability = ctx.getCustomGlobalExecutorsAvailability();
1404+
auto declRtAvailability = ctx.getCustomGlobalExecutorsRuntimeAvailability();
1405+
return deploymentAvailability.isContainedIn(declAvailability)
1406+
&& runtimeAvailability.isContainedIn(declRtAvailability);
1407+
}
1408+
1409+
return true;
1410+
}
1411+
13901412
void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) {
13911413
auto moduleLoc = entryPoint.getAsRegularLocation();
13921414
auto *entryBlock = B.getInsertionBB();
@@ -1402,6 +1424,53 @@ void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) {
14021424

14031425
B.setInsertionPoint(entryBlock);
14041426

1427+
// If we're using a new enough deployment target, call swift_createExecutors()
1428+
if (ctx.LangOpts.ExecutorFactory) {
1429+
if (!isCreateExecutorsFunctionAvailable(SGM)) {
1430+
ctx.Diags.diagnose(SourceLoc(), diag::executor_factory_not_supported);
1431+
} else {
1432+
CanType factoryTy = SGM.getConfiguredExecutorFactory()->getCanonicalType();
1433+
1434+
if (!factoryTy) {
1435+
ctx.Diags.diagnose(SourceLoc(), diag::cannot_find_executor_factory_type,
1436+
*ctx.LangOpts.ExecutorFactory);
1437+
}
1438+
1439+
ProtocolDecl *executorFactoryProtocol
1440+
= ctx.getProtocol(KnownProtocolKind::ExecutorFactory);
1441+
auto conformance = lookupConformance(factoryTy, executorFactoryProtocol);
1442+
1443+
if (conformance.isInvalid()) {
1444+
// If this type doesn't conform, ignore it and use the default factory
1445+
SourceLoc loc = extractNearestSourceLoc(factoryTy);
1446+
1447+
ctx.Diags.diagnose(loc, diag::executor_factory_must_conform,
1448+
*ctx.LangOpts.ExecutorFactory);
1449+
1450+
factoryTy = SGM.getDefaultExecutorFactory()->getCanonicalType();
1451+
conformance = lookupConformance(factoryTy, executorFactoryProtocol);
1452+
1453+
assert(!conformance.isInvalid());
1454+
}
1455+
1456+
FuncDecl *createExecutorsFuncDecl = SGM.getCreateExecutors();
1457+
assert(createExecutorsFuncDecl
1458+
&& "Failed to find swift_createExecutors function decl");
1459+
SILFunction *createExecutorsSILFunc =
1460+
SGM.getFunction(SILDeclRef(createExecutorsFuncDecl, SILDeclRef::Kind::Func),
1461+
NotForDefinition);
1462+
SILValue createExecutorsFunc =
1463+
B.createFunctionRefFor(moduleLoc, createExecutorsSILFunc);
1464+
MetatypeType *factoryThickMetaTy
1465+
= MetatypeType::get(factoryTy, MetatypeRepresentation::Thick);
1466+
SILValue factorySILMetaTy
1467+
= B.createMetatype(moduleLoc, getLoweredType(factoryThickMetaTy));
1468+
auto ceSubs = SubstitutionMap::getProtocolSubstitutions(
1469+
conformance.getProtocol(), factoryTy, conformance);
1470+
B.createApply(moduleLoc, createExecutorsFunc, ceSubs, { factorySILMetaTy });
1471+
}
1472+
}
1473+
14051474
auto wrapCallArgs = [this, &moduleLoc](SILValue originalValue, FuncDecl *fd,
14061475
uint32_t paramIndex) -> SILValue {
14071476
Type parameterType = fd->getParameters()->get(paramIndex)->getTypeInContext();

stdlib/public/Concurrency/Actor.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "../CompatibilityOverride/CompatibilityOverride.h"
2626
#include "swift/ABI/Actor.h"
2727
#include "swift/ABI/Task.h"
28+
#include "ExecutorBridge.h"
2829
#include "TaskPrivate.h"
2930
#include "swift/Basic/HeaderFooterLayout.h"
3031
#include "swift/Basic/PriorityQueue.h"
@@ -299,6 +300,46 @@ static bool isExecutingOnMainThread() {
299300
#endif
300301
}
301302

303+
#pragma clang diagnostic push
304+
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
305+
306+
extern "C" SWIFT_CC(swift)
307+
SerialExecutorRef _swift_getActiveExecutor() {
308+
auto currentTracking = ExecutorTrackingInfo::current();
309+
if (currentTracking) {
310+
SerialExecutorRef executor = currentTracking->getActiveExecutor();
311+
// This might be an actor, in which case return nil ("generic")
312+
if (executor.isDefaultActor())
313+
return SerialExecutorRef::generic();
314+
return executor;
315+
}
316+
317+
// If there's no tracking and we're on the main thread, then the main
318+
// executor is notionally active.
319+
if (isExecutingOnMainThread())
320+
return swift_getMainExecutor();
321+
322+
return SerialExecutorRef::generic();
323+
}
324+
325+
extern "C" SWIFT_CC(swift)
326+
TaskExecutorRef _swift_getCurrentTaskExecutor() {
327+
auto currentTracking = ExecutorTrackingInfo::current();
328+
if (currentTracking)
329+
return currentTracking->getTaskExecutor();
330+
return TaskExecutorRef::undefined();
331+
}
332+
333+
extern "C" SWIFT_CC(swift)
334+
TaskExecutorRef _swift_getPreferredTaskExecutor() {
335+
AsyncTask *task = swift_task_getCurrent();
336+
if (!task)
337+
return TaskExecutorRef::undefined();
338+
return task->getPreferredTaskExecutor();
339+
}
340+
341+
#pragma clang diagnostic pop
342+
302343
JobPriority swift::swift_task_getCurrentThreadPriority() {
303344
#if SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY
304345
return JobPriority::UserInitiated;

0 commit comments

Comments
 (0)