@@ -217,6 +217,7 @@ ExecutorTrackingInfo::ActiveInfoInThread;
217
217
218
218
void swift::runJobInEstablishedExecutorContext (Job *job) {
219
219
_swift_tsan_acquire (job);
220
+ SWIFT_TASK_DEBUG_LOG (" Run job in established context %p" , job);
220
221
221
222
#if SWIFT_OBJC_INTEROP
222
223
auto pool = objc_autoreleasePoolPush ();
@@ -386,6 +387,20 @@ static void checkIsCurrentExecutorMode(void *context) {
386
387
: Swift6_UseCheckIsolated_AllowCrash;
387
388
}
388
389
390
+ // Implemented in Swift to avoid some annoying hard-coding about
391
+ // TaskExecutor's protocol witness table. We could inline this
392
+ // with effort, though.
393
+ extern " C" SWIFT_CC(swift) void _swift_task_enqueueOnTaskExecutor (
394
+ Job *job, HeapObject *executor, const Metadata *selfType,
395
+ const TaskExecutorWitnessTable *wtable);
396
+
397
+ // Implemented in Swift to avoid some annoying hard-coding about
398
+ // SerialExecutor's protocol witness table. We could inline this
399
+ // with effort, though.
400
+ extern " C" SWIFT_CC(swift) void _swift_task_enqueueOnExecutor (
401
+ Job *job, HeapObject *executor, const Metadata *executorType,
402
+ const SerialExecutorWitnessTable *wtable);
403
+
389
404
SWIFT_CC (swift)
390
405
static bool swift_task_isCurrentExecutorImpl (SerialExecutorRef expectedExecutor) {
391
406
auto current = ExecutorTrackingInfo::current ();
@@ -464,9 +479,17 @@ static bool swift_task_isCurrentExecutorImpl(SerialExecutorRef expectedExecutor)
464
479
465
480
// Complex equality means that if two executors of the same type have some
466
481
// special logic to check if they are "actually the same".
482
+ //
483
+ // If any of the executors does not have a witness table we can't complex
484
+ // equality compare with it.
485
+ //
486
+ // We may be able to prove we're on the same executor as expected by
487
+ // using 'checkIsolated' later on though.
467
488
if (expectedExecutor.isComplexEquality ()) {
468
489
if (currentExecutor.getIdentity () &&
490
+ currentExecutor.hasSerialExecutorWitnessTable () &&
469
491
expectedExecutor.getIdentity () &&
492
+ expectedExecutor.hasSerialExecutorWitnessTable () &&
470
493
swift_compareWitnessTables (
471
494
reinterpret_cast <const WitnessTable *>(
472
495
currentExecutor.getSerialExecutorWitnessTable ()),
@@ -1171,7 +1194,10 @@ class DefaultActorImpl
1171
1194
// / Schedule a processing job.
1172
1195
// / It can be done when actor transitions from Idle to Scheduled or
1173
1196
// / when actor gets a priority override and we schedule a stealer.
1174
- void scheduleActorProcessJob (JobPriority priority);
1197
+ // /
1198
+ // / When the task executor is `undefined` ths task will be scheduled on the
1199
+ // / default global executor.
1200
+ void scheduleActorProcessJob (JobPriority priority, TaskExecutorRef taskExecutor);
1175
1201
1176
1202
// / Processes claimed incoming jobs into `prioritizedJobs`.
1177
1203
// / Incoming jobs are of mixed priorities and in LIFO order.
@@ -1271,14 +1297,36 @@ dispatch_lock_t *DefaultActorImpl::drainLockAddr() {
1271
1297
}
1272
1298
#endif /* SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION */
1273
1299
1274
- void DefaultActorImpl::scheduleActorProcessJob (JobPriority priority) {
1300
+ void DefaultActorImpl::scheduleActorProcessJob (
1301
+ JobPriority priority, TaskExecutorRef taskExecutor) {
1275
1302
Job *job = new ProcessOutOfLineJob (this , priority);
1276
1303
SWIFT_TASK_DEBUG_LOG (
1277
- " Scheduling processing job %p for actor %p at priority %#zx" , job, this ,
1278
- priority);
1304
+ " Scheduling processing job %p for actor %p at priority %#zx, with taskExecutor %p" , job, this ,
1305
+ priority, taskExecutor.getIdentity ());
1306
+
1307
+ if (taskExecutor.isDefined ()) {
1308
+ #if SWIFT_CONCURRENCY_EMBEDDED
1309
+ swift_unreachable (" task executors not supported in embedded Swift" );
1310
+ #else
1311
+ auto taskExecutorIdentity = taskExecutor.getIdentity ();
1312
+ auto taskExecutorType = swift_getObjectType (taskExecutorIdentity);
1313
+ auto taskExecutorWtable = taskExecutor.getTaskExecutorWitnessTable ();
1314
+
1315
+ return _swift_task_enqueueOnTaskExecutor (
1316
+ job, taskExecutorIdentity, taskExecutorType, taskExecutorWtable);
1317
+ #endif
1318
+ }
1319
+
1279
1320
swift_task_enqueueGlobal (job);
1280
1321
}
1281
1322
1323
+ TaskExecutorRef TaskExecutorRef::fromTaskExecutorPreference (Job *job) {
1324
+ if (auto task = dyn_cast<AsyncTask>(job)) {
1325
+ return task->getPreferredTaskExecutor ();
1326
+ }
1327
+ return TaskExecutorRef::undefined ();
1328
+ }
1329
+
1282
1330
void DefaultActorImpl::enqueue (Job *job, JobPriority priority) {
1283
1331
// We can do relaxed loads here, we are just using the current head in the
1284
1332
// atomic state and linking that into the new job we are inserting, we don't
@@ -1287,6 +1335,7 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) {
1287
1335
this , priority);
1288
1336
concurrency::trace::actor_enqueue (this , job);
1289
1337
bool distributedActorIsRemote = swift_distributed_actor_is_remote (this );
1338
+
1290
1339
auto oldState = _status ().load (std::memory_order_relaxed);
1291
1340
while (true ) {
1292
1341
auto newState = oldState;
@@ -1317,7 +1366,10 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) {
1317
1366
if (!oldState.isScheduled () && newState.isScheduled ()) {
1318
1367
// We took responsibility to schedule the actor for the first time. See
1319
1368
// also ownership rule (1)
1320
- return scheduleActorProcessJob (newState.getMaxPriority ());
1369
+ TaskExecutorRef taskExecutor =
1370
+ TaskExecutorRef::fromTaskExecutorPreference (job);
1371
+
1372
+ return scheduleActorProcessJob (newState.getMaxPriority (), taskExecutor);
1321
1373
}
1322
1374
1323
1375
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
@@ -1336,7 +1388,10 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) {
1336
1388
" [Override] Scheduling a stealer for actor %p at %#x priority" ,
1337
1389
this , newState.getMaxPriority ());
1338
1390
swift_retain (this );
1339
- scheduleActorProcessJob (newState.getMaxPriority ());
1391
+
1392
+ TaskExecutorRef taskExecutor =
1393
+ TaskExecutorRef::fromTaskExecutorPreference (job);
1394
+ scheduleActorProcessJob (newState.getMaxPriority (), taskExecutor);
1340
1395
}
1341
1396
}
1342
1397
#endif
@@ -1411,7 +1466,8 @@ void DefaultActorImpl::enqueueStealer(Job *job, JobPriority priority) {
1411
1466
" [Override] Scheduling a stealer for actor %p at %#x priority" ,
1412
1467
this , newState.getMaxPriority ());
1413
1468
swift_retain (this );
1414
- scheduleActorProcessJob (newState.getMaxPriority ());
1469
+ auto taskExecutor = TaskExecutorRef::fromTaskExecutorPreference (job);
1470
+ scheduleActorProcessJob (newState.getMaxPriority (), taskExecutor);
1415
1471
}
1416
1472
#endif
1417
1473
}
@@ -1805,7 +1861,7 @@ bool DefaultActorImpl::unlock(bool forceUnlock)
1805
1861
} else {
1806
1862
// There is no work left to do - actor goes idle
1807
1863
1808
- // R becomes 0 and N descreases by 1.
1864
+ // R becomes 0 and N decreases by 1.
1809
1865
// But, we may still have stealers scheduled so N could be > 0. This is
1810
1866
// fine since N >= R. Every such stealer, once scheduled, will observe
1811
1867
// actor as idle, will release its ref and return. (See tryLock function.)
@@ -1822,7 +1878,8 @@ bool DefaultActorImpl::unlock(bool forceUnlock)
1822
1878
1823
1879
if (newState.isScheduled ()) {
1824
1880
// See ownership rule (6) in DefaultActorImpl
1825
- scheduleActorProcessJob (newState.getMaxPriority ());
1881
+ // FIXME: should we specify some task executor here, since otherwise we'll schedule on the global pool
1882
+ scheduleActorProcessJob (newState.getMaxPriority (), TaskExecutorRef::undefined ());
1826
1883
} else {
1827
1884
// See ownership rule (5) in DefaultActorImpl
1828
1885
SWIFT_TASK_DEBUG_LOG (" Actor %p is idle now" , this );
@@ -1854,7 +1911,11 @@ static void swift_job_runImpl(Job *job, SerialExecutorRef executor) {
1854
1911
// is generic.
1855
1912
if (!executor.isGeneric ()) trackingInfo.disallowSwitching ();
1856
1913
1857
- trackingInfo.enterAndShadow (executor, TaskExecutorRef::undefined ());
1914
+ auto taskExecutor = executor.isGeneric ()
1915
+ ? TaskExecutorRef::fromTaskExecutorPreference (job)
1916
+ : TaskExecutorRef::undefined ();
1917
+
1918
+ trackingInfo.enterAndShadow (executor, taskExecutor);
1858
1919
1859
1920
SWIFT_TASK_DEBUG_LOG (" job %p" , job);
1860
1921
runJobInEstablishedExecutorContext (job);
@@ -1872,9 +1933,11 @@ static void swift_job_runImpl(Job *job, SerialExecutorRef executor) {
1872
1933
1873
1934
SWIFT_CC (swift)
1874
1935
static void swift_job_run_on_serial_and_task_executorImpl(Job *job,
1875
- SerialExecutorRef serialExecutor,
1876
- TaskExecutorRef taskExecutor) {
1936
+ SerialExecutorRef serialExecutor,
1937
+ TaskExecutorRef taskExecutor) {
1877
1938
ExecutorTrackingInfo trackingInfo;
1939
+ SWIFT_TASK_DEBUG_LOG (" Run job %p on serial executor %p task executor %p" , job,
1940
+ serialExecutor.getIdentity (), taskExecutor.getIdentity ());
1878
1941
1879
1942
// TODO: we don't allow switching
1880
1943
trackingInfo.disallowSwitching ();
@@ -2092,16 +2155,13 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
2092
2155
auto currentTaskExecutor = (trackingInfo ? trackingInfo->getTaskExecutor ()
2093
2156
: TaskExecutorRef::undefined ());
2094
2157
auto newTaskExecutor = task->getPreferredTaskExecutor ();
2095
- SWIFT_TASK_DEBUG_LOG (" Task %p trying to switch executors: executor %p to "
2096
- " %p%s; task executor: from %p%s to %p%s" ,
2097
- task, currentExecutor.getIdentity (),
2098
- currentExecutor.isMainExecutor () ? " (MainActorExecutor)"
2099
- : currentExecutor.isGeneric () ? " (GenericExecutor)"
2100
- : " " ,
2158
+ SWIFT_TASK_DEBUG_LOG (" Task %p trying to switch executors: executor %p%s to "
2159
+ " new serial executor: %p%s; task executor: from %p%s to %p%s" ,
2160
+ task,
2161
+ currentExecutor.getIdentity (),
2162
+ currentExecutor.getIdentityDebugName (),
2101
2163
newExecutor.getIdentity (),
2102
- newExecutor.isMainExecutor () ? " (MainActorExecutor)"
2103
- : newExecutor.isGeneric () ? " (GenericExecutor)"
2104
- : " " ,
2164
+ newExecutor.getIdentityDebugName (),
2105
2165
currentTaskExecutor.getIdentity (),
2106
2166
currentTaskExecutor.isDefined () ? " " : " (undefined)" ,
2107
2167
newTaskExecutor.getIdentity (),
@@ -2149,84 +2209,66 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
2149
2209
/* ************************ GENERIC ACTOR INTERFACES **************************/
2150
2210
/* ****************************************************************************/
2151
2211
2152
- // Implemented in Swift to avoid some annoying hard-coding about
2153
- // SerialExecutor's protocol witness table. We could inline this
2154
- // with effort, though.
2155
- extern " C" SWIFT_CC(swift)
2156
- void _swift_task_enqueueOnExecutor (Job *job, HeapObject *executor,
2157
- const Metadata *selfType,
2158
- const SerialExecutorWitnessTable *wtable);
2159
- extern " C" SWIFT_CC(swift) void _swift_task_enqueueOnTaskExecutor (
2160
- Job *job, HeapObject *executor, const Metadata *selfType,
2161
- const TaskExecutorWitnessTable *wtable);
2162
-
2163
2212
extern " C" SWIFT_CC(swift) void _swift_task_makeAnyTaskExecutor (
2164
2213
HeapObject *executor, const Metadata *selfType,
2165
2214
const TaskExecutorWitnessTable *wtable);
2166
2215
2167
2216
SWIFT_CC (swift)
2168
- static void swift_task_enqueueImpl (Job *job, SerialExecutorRef executor) {
2169
- SWIFT_TASK_DEBUG_LOG (" enqueue job %p on serial executor %p" , job,
2170
- executor.getIdentity ());
2217
+ static void swift_task_enqueueImpl (Job *job, SerialExecutorRef serialExecutorRef) {
2218
+ #ifndef NDEBUG
2219
+ {
2220
+ auto _taskExecutorRef = TaskExecutorRef::undefined ();
2221
+ if (auto task = dyn_cast<AsyncTask>(job)) {
2222
+ _taskExecutorRef = task->getPreferredTaskExecutor ();
2223
+ }
2224
+ SWIFT_TASK_DEBUG_LOG (
2225
+ " enqueue job %p on serial serialExecutor %p, taskExecutor = %p" , job,
2226
+ serialExecutorRef.getIdentity (), _taskExecutorRef.getIdentity ());
2227
+ }
2228
+ #endif
2171
2229
2172
2230
assert (job && " no job provided" );
2173
2231
2174
2232
_swift_tsan_release (job);
2175
2233
2176
- if (executor.isGeneric ()) {
2177
- // TODO: check the task for a flag if we need to look for task executor
2234
+ if (serialExecutorRef.isGeneric ()) {
2178
2235
if (auto task = dyn_cast<AsyncTask>(job)) {
2179
- auto taskExecutor = task->getPreferredTaskExecutor ();
2180
- if (taskExecutor .isDefined ()) {
2236
+ auto taskExecutorRef = task->getPreferredTaskExecutor ();
2237
+ if (taskExecutorRef .isDefined ()) {
2181
2238
#if SWIFT_CONCURRENCY_EMBEDDED
2182
2239
swift_unreachable (" task executors not supported in embedded Swift" );
2183
2240
#else
2184
- auto wtable = taskExecutor.getTaskExecutorWitnessTable ();
2185
- auto taskExecutorObject = taskExecutor.getIdentity ();
2186
- auto taskExecutorType = swift_getObjectType (taskExecutorObject);
2187
- return _swift_task_enqueueOnTaskExecutor (job, taskExecutorObject,
2188
- taskExecutorType, wtable);
2241
+ auto taskExecutorIdentity = taskExecutorRef.getIdentity ();
2242
+ auto taskExecutorType = swift_getObjectType (taskExecutorIdentity);
2243
+ auto taskExecutorWtable = taskExecutorRef.getTaskExecutorWitnessTable ();
2244
+
2245
+ return _swift_task_enqueueOnTaskExecutor (
2246
+ job,
2247
+ taskExecutorIdentity, taskExecutorType, taskExecutorWtable);
2189
2248
#endif // SWIFT_CONCURRENCY_EMBEDDED
2190
2249
} // else, fall-through to the default global enqueue
2191
2250
}
2192
2251
return swift_task_enqueueGlobal (job);
2193
2252
}
2194
2253
2195
- if (executor.isDefaultActor ()) {
2196
- auto taskExecutor = TaskExecutorRef::undefined ();
2197
- if (auto task = dyn_cast<AsyncTask>(job)) {
2198
- taskExecutor = task->getPreferredTaskExecutor ();
2199
- }
2200
-
2201
- #if SWIFT_CONCURRENCY_EMBEDDED
2202
- swift_unreachable (" task executors not supported in embedded Swift" );
2203
- #else
2204
- if (taskExecutor.isDefined ()) {
2205
- auto wtable = taskExecutor.getTaskExecutorWitnessTable ();
2206
- auto executorObject = taskExecutor.getIdentity ();
2207
- auto executorType = swift_getObjectType (executorObject);
2208
- return _swift_task_enqueueOnTaskExecutor (job, executorObject,
2209
- executorType, wtable);
2210
- } else {
2211
- return swift_defaultActor_enqueue (job, executor.getDefaultActor ());
2212
- }
2213
- #endif // SWIFT_CONCURRENCY_EMBEDDED
2254
+ if (serialExecutorRef.isDefaultActor ()) {
2255
+ return swift_defaultActor_enqueue (job, serialExecutorRef.getDefaultActor ());
2214
2256
}
2215
2257
2216
2258
#if SWIFT_CONCURRENCY_EMBEDDED
2217
2259
swift_unreachable (" custom executors not supported in embedded Swift" );
2218
2260
#else
2219
2261
// For main actor or actors with custom executors
2220
- auto wtable = executor.getSerialExecutorWitnessTable ();
2221
- auto executorObject = executor.getIdentity ();
2222
- auto executorType = swift_getObjectType (executorObject);
2223
- _swift_task_enqueueOnExecutor (job, executorObject, executorType, wtable);
2262
+ auto serialExecutorIdentity = serialExecutorRef.getIdentity ();
2263
+ auto serialExecutorType = swift_getObjectType (serialExecutorIdentity);
2264
+ auto serialExecutorWtable = serialExecutorRef.getSerialExecutorWitnessTable ();
2265
+ _swift_task_enqueueOnExecutor (job, serialExecutorIdentity, serialExecutorType,
2266
+ serialExecutorWtable);
2224
2267
#endif // SWIFT_CONCURRENCY_EMBEDDED
2225
2268
}
2226
2269
2227
2270
static void
2228
- swift_actor_escalate (DefaultActorImpl *actor, AsyncTask *task, JobPriority newPriority)
2229
- {
2271
+ swift_actor_escalate (DefaultActorImpl *actor, AsyncTask *task, JobPriority newPriority) {
2230
2272
#if !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
2231
2273
return actor->enqueueStealer (task, newPriority);
2232
2274
#endif
0 commit comments