@@ -975,7 +975,6 @@ class DefaultActorImplHeader : public HeapObject {
975
975
class DefaultActorImplFooter {
976
976
protected:
977
977
#if !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
978
- using SimpleQueue = swift::SimpleQueue<Job *, JobQueueTraits>;
979
978
using PriorityQueue = swift::PriorityQueue<Job *, JobQueueTraits>;
980
979
981
980
// When enqueued, jobs are atomically added to a linked list with the head
@@ -1080,9 +1079,13 @@ class DefaultActorImpl
1080
1079
// / new priority
1081
1080
void enqueueStealer (Job *job, JobPriority priority);
1082
1081
1083
- // / Dequeues one job from `prioritizedJobs `.
1082
+ // / Dequeues one job from `prioritisedJobs `.
1084
1083
// / The calling thread must be holding the actor lock while calling this
1085
1084
Job *drainOne ();
1085
+
1086
+ // / Atomically claims incoming jobs from ActiveActorStatus, and calls `handleUnprioritizedJobs()`.
1087
+ // / Called with actor lock held on current thread.
1088
+ void processIncomingQueue ();
1086
1089
#endif
1087
1090
1088
1091
// / Check if the actor is actually a distributed *remote* actor.
@@ -1119,14 +1122,10 @@ class DefaultActorImpl
1119
1122
// / when actor gets a priority override and we schedule a stealer.
1120
1123
void scheduleActorProcessJob (JobPriority priority);
1121
1124
1122
- // / Atomically takes a list of jobs from ActiveActorStatus, reversing them in
1123
- // / the process. Returns jobs of mixed priorities in FIFO order.
1124
- SimpleQueue collectJobs ();
1125
-
1126
- // / Check for new jobs in the incoming queue and move them to the
1127
- // / processing queue.
1125
+ // / Processes claimed incoming jobs into `prioritizedJobs`.
1126
+ // / Incoming jobs are of mixed priorities and in LIFO order.
1128
1127
// / Called with actor lock held on current thread.
1129
- void processJobs ( );
1128
+ void handleUnprioritizedJobs (Job *head );
1130
1129
#endif /* !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS */
1131
1130
1132
1131
void deallocateUnconditional ();
@@ -1372,13 +1371,12 @@ void DefaultActorImpl::enqueueStealer(Job *job, JobPriority priority) {
1372
1371
1373
1372
}
1374
1373
1375
- DefaultActorImpl::SimpleQueue DefaultActorImpl::collectJobs () {
1374
+ void DefaultActorImpl::processIncomingQueue () {
1376
1375
// Pairs with the store release in DefaultActorImpl::enqueue
1377
1376
bool distributedActorIsRemote = swift_distributed_actor_is_remote (this );
1378
1377
auto oldState = _status ().load (SWIFT_MEMORY_ORDER_CONSUME);
1379
1378
_swift_tsan_consume (this );
1380
1379
1381
- SimpleQueue result;
1382
1380
// We must ensure that any jobs not seen by collectJobs() don't have any
1383
1381
// dangling references to the jobs that have been collected. For that we must
1384
1382
// atomically set head pointer to NULL. If it fails because more jobs have
@@ -1387,7 +1385,7 @@ DefaultActorImpl::SimpleQueue DefaultActorImpl::collectJobs() {
1387
1385
// If there aren't any new jobs in the incoming queue, we can return
1388
1386
// immediately without updating the status.
1389
1387
if (!oldState.getFirstUnprioritisedJob ()) {
1390
- return result ;
1388
+ return ;
1391
1389
}
1392
1390
assert (oldState.isAnyRunning ());
1393
1391
@@ -1405,28 +1403,26 @@ DefaultActorImpl::SimpleQueue DefaultActorImpl::collectJobs() {
1405
1403
}
1406
1404
}
1407
1405
1408
- // Collect jobs, reversing them in the process
1409
- auto job = oldState.getFirstUnprioritisedJob ();
1410
- while (job) {
1411
- auto next = getNextJob (job);
1412
- result.prepend (job);
1413
- job = next;
1414
- }
1415
-
1416
- return result;
1406
+ handleUnprioritizedJobs (oldState.getFirstUnprioritisedJob ());
1417
1407
}
1418
1408
1419
1409
// Called with actor lock held on current thread
1420
- void DefaultActorImpl::processJobs () {
1421
- SimpleQueue jobs = collectJobs ();
1422
- prioritizedJobs.enqueueContentsOf (jobs.head );
1410
+ void DefaultActorImpl::handleUnprioritizedJobs (Job *head) {
1411
+ // Reverse jobs from LIFO to FIFO order
1412
+ Job *reversed = nullptr ;
1413
+ while (head) {
1414
+ auto next = getNextJob (head);
1415
+ setNextJob (head, reversed);
1416
+ reversed = head;
1417
+ head = next;
1418
+ }
1419
+ prioritizedJobs.enqueueContentsOf (reversed);
1423
1420
}
1424
1421
1425
1422
// Called with actor lock held on current thread
1426
1423
Job *DefaultActorImpl::drainOne () {
1427
1424
SWIFT_TASK_DEBUG_LOG (" Draining one job from default actor %p" , this );
1428
1425
1429
- processJobs ();
1430
1426
traceJobQueue (this , prioritizedJobs.peek ());
1431
1427
auto firstJob = prioritizedJobs.dequeue ();
1432
1428
if (!firstJob) {
@@ -1490,39 +1486,40 @@ static void defaultActorDrain(DefaultActorImpl *actor) {
1490
1486
TaskExecutorRef::undefined ());
1491
1487
1492
1488
while (true ) {
1493
- if (shouldYieldThread ()) {
1494
- currentActor->unlock (true );
1495
- break ;
1496
- }
1497
-
1498
1489
Job *job = currentActor->drainOne ();
1499
1490
if (job == NULL ) {
1500
1491
// No work left to do, try unlocking the actor. This may fail if there is
1501
1492
// work concurrently enqueued in which case, we'd try again in the loop
1502
- if (!currentActor->unlock (false )) {
1503
- continue ;
1493
+ if (currentActor->unlock (false )) {
1494
+ break ;
1495
+ }
1496
+ } else {
1497
+ if (AsyncTask *task = dyn_cast<AsyncTask>(job)) {
1498
+ auto taskExecutor = task->getPreferredTaskExecutor ();
1499
+ trackingInfo.setTaskExecutor (taskExecutor);
1504
1500
}
1505
- break ;
1506
- }
1507
1501
1508
- if (AsyncTask *task = dyn_cast<AsyncTask>(job)) {
1509
- auto taskExecutor = task->getPreferredTaskExecutor ();
1510
- trackingInfo.setTaskExecutor (taskExecutor);
1502
+ // This thread is now going to follow the task on this actor. It may hop off
1503
+ // the actor
1504
+ runJobInEstablishedExecutorContext (job);
1505
+
1506
+ // We could have come back from the job on a generic executor and not as
1507
+ // part of a default actor. If so, there is no more work left for us to do
1508
+ // here.
1509
+ auto currentExecutor = trackingInfo.getActiveExecutor ();
1510
+ if (!currentExecutor.isDefaultActor ()) {
1511
+ currentActor = nullptr ;
1512
+ break ;
1513
+ }
1514
+ currentActor = asImpl (currentExecutor.getDefaultActor ());
1511
1515
}
1512
1516
1513
- // This thread is now going to follow the task on this actor. It may hop off
1514
- // the actor
1515
- runJobInEstablishedExecutorContext (job);
1516
-
1517
- // We could have come back from the job on a generic executor and not as
1518
- // part of a default actor. If so, there is no more work left for us to do
1519
- // here.
1520
- auto currentExecutor = trackingInfo.getActiveExecutor ();
1521
- if (!currentExecutor.isDefaultActor ()) {
1522
- currentActor = nullptr ;
1517
+ if (shouldYieldThread ()) {
1518
+ currentActor->unlock (true );
1523
1519
break ;
1524
1520
}
1525
- currentActor = asImpl (currentExecutor.getDefaultActor ());
1521
+
1522
+ currentActor->processIncomingQueue ();
1526
1523
}
1527
1524
1528
1525
// Leave the tracking info.
@@ -1668,6 +1665,17 @@ retry:;
1668
1665
auto newState = oldState.withRunning ();
1669
1666
newState = newState.withoutEscalatedPriority ();
1670
1667
1668
+ // Claim incoming jobs when obtaining lock as a drainer, to save one
1669
+ // round of atomic load and compare-exchange.
1670
+ // This is not useful when obtaining lock for assuming thread during actor
1671
+ // switching, because arbitrary use code can run between locking and
1672
+ // draining the next job. So we still need to call processIncomingQueue() to
1673
+ // check for higher priority jobs that could have been scheduled in the
1674
+ // meantime. And processing is more efficient when done in larger batches.
1675
+ if (asDrainer) {
1676
+ newState = newState.withFirstUnprioritisedJob (nullptr );
1677
+ }
1678
+
1671
1679
// This needs an acquire since we are taking a lock
1672
1680
if (_status ().compare_exchange_weak (oldState, newState,
1673
1681
std::memory_order_acquire,
@@ -1677,6 +1685,9 @@ retry:;
1677
1685
assert (prioritizedJobs.empty ());
1678
1686
}
1679
1687
traceActorStateTransition (this , oldState, newState, distributedActorIsRemote);
1688
+ if (asDrainer) {
1689
+ handleUnprioritizedJobs (oldState.getFirstUnprioritisedJob ());
1690
+ }
1680
1691
return true ;
1681
1692
}
1682
1693
}
0 commit comments