@@ -1355,20 +1355,31 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) {
1355
1355
}
1356
1356
}
1357
1357
1358
+ // Fetch the task executor from the job for later use. This can be somewhat
1359
+ // expensive, so only do it if we're likely to need it. The conditions here
1360
+ // match the conditions of the if statements below which use `taskExecutor`.
1361
+ TaskExecutorRef taskExecutor;
1362
+ bool needsScheduling = !oldState.isScheduled () && newState.isScheduled ();
1363
+ bool needsStealer =
1364
+ oldState.getMaxPriority () != newState.getMaxPriority () &&
1365
+ newState.isRunning ();
1366
+ if (needsScheduling || needsStealer)
1367
+ taskExecutor = TaskExecutorRef::fromTaskExecutorPreference (job);
1368
+
1358
1369
// This needs to be a store release so that we also publish the contents of
1359
1370
// the new Job we are adding to the atomic job queue. Pairs with consume
1360
1371
// in drainOne.
1361
1372
if (_status ().compare_exchange_weak (oldState, newState,
1362
1373
/* success */ std::memory_order_release,
1363
1374
/* failure */ std::memory_order_relaxed)) {
1375
+ // NOTE: `job` is off limits after this point, as another thread might run
1376
+ // and destroy it now that it's enqueued.
1377
+
1364
1378
traceActorStateTransition (this , oldState, newState, distributedActorIsRemote);
1365
1379
1366
1380
if (!oldState.isScheduled () && newState.isScheduled ()) {
1367
1381
// We took responsibility to schedule the actor for the first time. See
1368
1382
// also ownership rule (1)
1369
- TaskExecutorRef taskExecutor =
1370
- TaskExecutorRef::fromTaskExecutorPreference (job);
1371
-
1372
1383
return scheduleActorProcessJob (newState.getMaxPriority (), taskExecutor);
1373
1384
}
1374
1385
@@ -1389,8 +1400,6 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) {
1389
1400
this , newState.getMaxPriority ());
1390
1401
swift_retain (this );
1391
1402
1392
- TaskExecutorRef taskExecutor =
1393
- TaskExecutorRef::fromTaskExecutorPreference (job);
1394
1403
scheduleActorProcessJob (newState.getMaxPriority (), taskExecutor);
1395
1404
}
1396
1405
}
@@ -1446,9 +1455,19 @@ void DefaultActorImpl::enqueueStealer(Job *job, JobPriority priority) {
1446
1455
if (oldState == newState)
1447
1456
return ;
1448
1457
1458
+ // Fetch the task executor from the job for later use. This can be somewhat
1459
+ // expensive, so only do it if we're likely to need it. The conditions here
1460
+ // match the conditions of the if statements below which use `taskExecutor`.
1461
+ TaskExecutorRef taskExecutor;
1462
+ if (!newState.isRunning () && newState.isScheduled ())
1463
+ taskExecutor = TaskExecutorRef::fromTaskExecutorPreference (job);
1464
+
1449
1465
if (_status ().compare_exchange_weak (oldState, newState,
1450
1466
/* success */ std::memory_order_relaxed,
1451
1467
/* failure */ std::memory_order_relaxed)) {
1468
+ // NOTE: `job` is off limits after this point, as another thread might run
1469
+ // and destroy it now that it's enqueued.
1470
+
1452
1471
traceActorStateTransition (this , oldState, newState, distributedActorIsRemote);
1453
1472
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1454
1473
if (newState.isRunning ()) {
@@ -1466,7 +1485,6 @@ void DefaultActorImpl::enqueueStealer(Job *job, JobPriority priority) {
1466
1485
" [Override] Scheduling a stealer for actor %p at %#x priority" ,
1467
1486
this , newState.getMaxPriority ());
1468
1487
swift_retain (this );
1469
- auto taskExecutor = TaskExecutorRef::fromTaskExecutorPreference (job);
1470
1488
scheduleActorProcessJob (newState.getMaxPriority (), taskExecutor);
1471
1489
}
1472
1490
#endif
0 commit comments