@@ -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 ;
13741383retry:;
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