Skip to content

Commit d792c07

Browse files
committed
Stealer support for actor runtime.
1 parent 3ca4a6e commit d792c07

File tree

1 file changed

+54
-16
lines changed

1 file changed

+54
-16
lines changed

stdlib/public/Concurrency/Actor.cpp

Lines changed: 54 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -912,10 +912,9 @@ class DefaultActorImpl : public HeapObject {
912912
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
913913
dispatch_lock_t *drainLockAddr();
914914
#endif
915-
/// Schedule a processing job. This can generally only be
916-
/// done if we know nobody else is trying to do it at the same time,
917-
/// e.g. if this thread just successfully transitioned the actor from
918-
/// Idle to Scheduled.
915+
/// Schedule a processing job.
916+
/// It can be done when actor transitions from Idle to Scheduled or
917+
/// when actor gets a priority override and we schedule a stealer.
919918
void scheduleActorProcessJob(JobPriority priority);
920919
#endif /* !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS */
921920

@@ -1153,12 +1152,14 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) {
11531152
swift_dispatch_lock_override_start_with_debounce(lockAddr, newState.currentDrainer(),
11541153
(qos_class_t) priority);
11551154
} else {
1156-
// TODO (rokhinip): Actor is scheduled - we need to schedule a
1157-
// stealer at the higher priority
1158-
//
1159-
// TODO (rokhinip): Add a signpost to flag that this is a potential
1160-
// priority inversion
1161-
SWIFT_TASK_DEBUG_LOG("[Override] Escalating actor %p which is enqueued", this);
1155+
// We are scheduling a stealer for an actor due to priority override.
1156+
// This extra processing job has a reference on the actor. See
1157+
// ownership rule (2).
1158+
SWIFT_TASK_DEBUG_LOG(
1159+
"[Override] Scheduling a stealer for actor %p at %#x priority",
1160+
this, newState.getMaxPriority());
1161+
swift_retain(this);
1162+
scheduleActorProcessJob(newState.getMaxPriority());
11621163
}
11631164
}
11641165
#endif
@@ -1251,8 +1252,13 @@ static void defaultActorDrain(DefaultActorImpl *actor) {
12511252
DefaultActorImpl *currentActor = actor;
12521253

12531254
bool actorLockAcquired = actor->tryLock(true);
1254-
// We always must succeed in taking the actor lock that we are draining
1255-
// because we don't have to compete with OOL jobs. See ownership rule (3)
1255+
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1256+
if (!actorLockAcquired) {
1257+
// tryLock may fail when we compete with other stealers for the actor.
1258+
goto done;
1259+
}
1260+
#endif
1261+
12561262
(void)actorLockAcquired;
12571263
assert(actorLockAcquired);
12581264

@@ -1295,6 +1301,9 @@ static void defaultActorDrain(DefaultActorImpl *actor) {
12951301
// Leave the tracking info.
12961302
trackingInfo.leave();
12971303

1304+
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1305+
done:
1306+
#endif
12981307
// Balances with the retain taken in ProcessOutOfLineJob::process
12991308
swift_release(actor);
13001309
}
@@ -1370,7 +1379,7 @@ bool DefaultActorImpl::tryLock(bool asDrainer) {
13701379
dispatch_thread_override_info_s threadOverrideInfo;
13711380
threadOverrideInfo = swift_dispatch_thread_get_current_override_qos_floor();
13721381
qos_class_t overrideFloor = threadOverrideInfo.override_qos_floor;
1373-
1382+
bool receivedOverride = false;
13741383
retry:;
13751384
#else
13761385
SWIFT_TASK_DEBUG_LOG("Thread attempting to jump onto %p, as drainer = %d", this, asDrainer);
@@ -1380,9 +1389,24 @@ retry:;
13801389
while (true) {
13811390

13821391
if (asDrainer) {
1383-
// TODO (rokhinip): Once we have multiple OOL process job support, this
1384-
// assert can potentially fail due to a race with an actor stealer that
1385-
// might have won the race and started running the actor
1392+
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1393+
if (!oldState.isScheduled()) {
1394+
// Some other actor stealer won the race and started running the actor
1395+
// and potentially be done with it if state is observed as idle here.
1396+
1397+
// This extra processing jobs releases its reference. See ownership rule
1398+
// (4).
1399+
swift_release(this);
1400+
1401+
if (receivedOverride) {
1402+
// Reset any override as a result of contending for the actor lock.
1403+
swift_dispatch_lock_override_end(overrideFloor);
1404+
}
1405+
return false;
1406+
}
1407+
#endif
1408+
1409+
// We are still in the race with other stealers to take over the actor.
13861410
assert(oldState.isScheduled());
13871411

13881412
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
@@ -1397,6 +1421,7 @@ retry:;
13971421

13981422
(void) swift_dispatch_thread_override_self(maxActorPriority);
13991423
overrideFloor = maxActorPriority;
1424+
receivedOverride = true;
14001425
goto retry;
14011426
}
14021427
#endif /* SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION */
@@ -1471,10 +1496,23 @@ bool DefaultActorImpl::unlock(bool forceUnlock)
14711496
}
14721497
// We need to schedule the actor - remove any escalation bits since we'll
14731498
// schedule the actor at the max priority currently on it
1499+
1500+
// N decreases by 1 as this processing job is going away; but R is
1501+
// still 1. We schedule a new processing job to maintain N >= R.
1502+
1503+
// It is possible that there are stealers scheduled for the actor already;
1504+
// but, we still schedule one anyway. This is because it is possible that
1505+
// those stealers got scheduled when we were running the actor and gone
1506+
// away. (See tryLock function.)
14741507
newState = newState.withScheduled();
14751508
newState = newState.withoutEscalatedPriority();
14761509
} else {
14771510
// There is no work left to do - actor goes idle
1511+
1512+
// R becomes 0 and N descreases by 1.
1513+
// But, we may still have stealers scheduled so N could be > 0. This is
1514+
// fine since N >= R. Every such stealer, once scheduled, will observe
1515+
// actor as idle, will release its ref and return. (See tryLock function.)
14781516
newState = newState.withIdle();
14791517
newState = newState.resetPriority();
14801518
}

0 commit comments

Comments
 (0)