@@ -639,10 +639,13 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus {
639
639
640
640
// Bit 3
641
641
DistributedRemote = 0x8 ,
642
+ // Bit 4
643
+ isPriorityEscalated = 0x10 ,
642
644
643
645
// Bits 8 - 15. We only need 8 bits of the whole size_t to represent Job
644
646
// Priority
645
647
PriorityMask = 0xFF00 ,
648
+ PriorityAndOverrideMask = PriorityMask | isPriorityEscalated,
646
649
PriorityShift = 0x8 ,
647
650
};
648
651
@@ -752,11 +755,37 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus {
752
755
JobPriority getMaxPriority () const {
753
756
return (JobPriority) ((Flags & PriorityMask) >> PriorityShift);
754
757
}
755
- ActiveActorStatus withMaxPriority (JobPriority priority) const {
758
+ ActiveActorStatus withNewPriority (JobPriority priority) const {
759
+ uint32_t flags = Flags & ~PriorityAndOverrideMask;
760
+ flags |= (uint32_t (priority) << PriorityShift);
756
761
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
757
- return ActiveActorStatus ((Flags & ~PriorityMask) | ( uint32_t (priority) << PriorityShift) , DrainLock, FirstJob);
762
+ return ActiveActorStatus (flags , DrainLock, FirstJob);
758
763
#else
759
- return ActiveActorStatus ((Flags & ~PriorityMask) | (uint32_t (priority) << PriorityShift), FirstJob);
764
+ return ActiveActorStatus (flags, FirstJob);
765
+ #endif
766
+ }
767
+ ActiveActorStatus resetPriority () const {
768
+ return withNewPriority (JobPriority::Unspecified);
769
+ }
770
+
771
+ bool isMaxPriorityEscalated () const { return Flags & isPriorityEscalated; }
772
+ ActiveActorStatus withEscalatedPriority (JobPriority priority) const {
773
+ JobPriority currentPriority = JobPriority ((Flags & PriorityMask) >> PriorityShift);
774
+ assert (priority > currentPriority);
775
+
776
+ uint32_t flags = (Flags & ~PriorityMask) | (uint32_t (priority) << PriorityShift);
777
+ flags |= isPriorityEscalated;
778
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
779
+ return ActiveActorStatus (flags, DrainLock, FirstJob);
780
+ #else
781
+ return ActiveActorStatus (flags, FirstJob);
782
+ #endif
783
+ }
784
+ ActiveActorStatus withoutEscalatedPriority () const {
785
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
786
+ return ActiveActorStatus (Flags & ~isPriorityEscalated, DrainLock, FirstJob);
787
+ #else
788
+ return ActiveActorStatus (Flags & ~isPriorityEscalated, FirstJob);
760
789
#endif
761
790
}
762
791
@@ -774,13 +803,20 @@ class alignas(sizeof(void *) * 2) ActiveActorStatus {
774
803
uint32_t getOpaqueFlags () const {
775
804
return Flags;
776
805
}
806
+
777
807
uint32_t currentDrainer () const {
778
808
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
779
809
return dispatch_lock_owner (DrainLock);
780
810
#else
781
811
return 0 ;
782
812
#endif
783
813
}
814
+
815
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
816
+ static size_t drainLockOffset () {
817
+ return offsetof (ActiveActorStatus, DrainLock);
818
+ }
819
+ #endif
784
820
};
785
821
786
822
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION && SWIFT_POINTER_IS_4_BYTES
@@ -911,6 +947,10 @@ class DefaultActorImpl : public HeapObject {
911
947
}
912
948
913
949
private:
950
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
951
+ dispatch_lock_t *drainLockAddr ();
952
+ #endif
953
+
914
954
void deallocateUnconditional ();
915
955
916
956
// / Schedule an inline processing job. This can generally only be
@@ -1121,6 +1161,13 @@ void DefaultActorImpl::deallocate() {
1121
1161
deallocateUnconditional ();
1122
1162
}
1123
1163
1164
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1165
+ dispatch_lock_t *DefaultActorImpl::drainLockAddr () {
1166
+ ActiveActorStatus *actorStatus = (ActiveActorStatus *) &this ->StatusStorage ;
1167
+ return (dispatch_lock_t *) (((char *) actorStatus) + ActiveActorStatus::drainLockOffset ());
1168
+ }
1169
+ #endif
1170
+
1124
1171
void DefaultActorImpl::deallocateUnconditional () {
1125
1172
concurrency::trace::actor_deallocate (this );
1126
1173
@@ -1149,17 +1196,41 @@ void DefaultActorImpl::scheduleActorProcessJob(JobPriority priority, bool useInl
1149
1196
1150
1197
1151
1198
bool DefaultActorImpl::tryLock (bool asDrainer) {
1152
- SWIFT_TASK_DEBUG_LOG (" Attempting to jump onto %p, as drainer = %d" , this , asDrainer);
1199
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1200
+ SWIFT_TASK_DEBUG_LOG (" Thread %#x attempting to jump onto %p, as drainer = %d" , dispatch_lock_value_for_self (), this , asDrainer);
1201
+ dispatch_thread_override_info_s threadOverrideInfo;
1202
+ threadOverrideInfo = swift_dispatch_thread_get_current_override_qos_floor ();
1203
+ qos_class_t overrideFloor = threadOverrideInfo.override_qos_floor ;
1204
+
1205
+ retry:;
1206
+ #else
1207
+ SWIFT_TASK_DEBUG_LOG (" Thread attempting to jump onto %p, as drainer = %d" , this , asDrainer);
1208
+ #endif
1153
1209
1154
1210
auto oldState = _status ().load (std::memory_order_relaxed);
1155
1211
while (true ) {
1156
1212
1157
- while (true ) {
1158
1213
if (asDrainer) {
1159
1214
// TODO (rokhinip): Once we have OOL process job support, this assert can
1160
1215
// potentially fail due to a race with an actor stealer that might have
1161
1216
// won the race and started running the actor
1162
1217
assert (oldState.isScheduled ());
1218
+
1219
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1220
+ // We only want to self override a thread if we are taking the actor lock
1221
+ // as a drainer because there might have been higher priority work
1222
+ // enqueued that might have escalated the max priority of the actor to be
1223
+ // higher than the original thread request.
1224
+ qos_class_t maxActorPriority = (qos_class_t ) oldState.getMaxPriority ();
1225
+
1226
+ if (threadOverrideInfo.can_override && (maxActorPriority > overrideFloor)) {
1227
+ SWIFT_TASK_DEBUG_LOG (" [Override] Self-override thread with oq_floor %#x to match max actor %p's priority %#x" , overrideFloor, this , maxActorPriority);
1228
+
1229
+ (void ) swift_dispatch_thread_override_self (maxActorPriority);
1230
+ overrideFloor = maxActorPriority;
1231
+ goto retry;
1232
+ }
1233
+ #endif
1163
1234
} else {
1164
1235
// We're trying to take the lock in an uncontended manner
1165
1236
if (oldState.isRunning () || oldState.isScheduled ()) {
@@ -1202,11 +1273,10 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) {
1202
1273
// Someone gave up the actor lock after we failed fast path.
1203
1274
// Schedule the actor
1204
1275
newState = newState.withScheduled ();
1205
- newState = newState.withMaxPriority (priority);
1206
-
1276
+ newState = newState.withNewPriority (priority);
1207
1277
} else {
1208
1278
if (priority > oldState.getMaxPriority ()) {
1209
- newState = newState.withMaxPriority (priority);
1279
+ newState = newState.withEscalatedPriority (priority);
1210
1280
}
1211
1281
}
1212
1282
@@ -1221,14 +1291,24 @@ void DefaultActorImpl::enqueue(Job *job, JobPriority priority) {
1221
1291
return scheduleActorProcessJob (newState.getMaxPriority (), true );
1222
1292
}
1223
1293
1294
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1224
1295
if (oldState.getMaxPriority () != newState.getMaxPriority ()) {
1225
1296
if (newState.isRunning ()) {
1226
- // TODO (rokhinip): Override the thread running the actor
1227
- return ;
1297
+ // Actor is running on a thread, escalate the thread running it
1298
+ SWIFT_TASK_DEBUG_LOG (" [Override] Escalating actor %p which is running on %#x to %#x priority" , this , newState.currentDrainer (), priority);
1299
+ dispatch_lock_t *lockAddr = this ->drainLockAddr ();
1300
+ swift_dispatch_lock_override_start_with_debounce (lockAddr, newState.currentDrainer (),
1301
+ (qos_class_t ) priority);
1228
1302
} else {
1229
- // TODO (rokhinip): Schedule the stealer
1303
+ // TODO (rokhinip): Actor is scheduled - we need to schedule a
1304
+ // stealer at the higher priority
1305
+ //
1306
+ // TODO (rokhinip): Add a signpost to flag that this is a potential
1307
+ // priority inversion
1308
+ SWIFT_TASK_DEBUG_LOG (" [Override] Escalating actor %p which is enqueued" , this );
1230
1309
}
1231
1310
}
1311
+ #endif
1232
1312
return ;
1233
1313
}
1234
1314
}
@@ -1252,30 +1332,38 @@ bool DefaultActorImpl::unlock(bool forceUnlock)
1252
1332
_swift_tsan_release (this );
1253
1333
while (true ) {
1254
1334
assert (oldState.isAnyRunning ());
1255
- // TODO (rokhinip): Further assert that the current thread is the one
1256
- // running the actor
1335
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1336
+ assert (dispatch_lock_is_locked_by_self (*(this ->drainLockAddr ())));
1337
+ #endif
1257
1338
1258
1339
if (oldState.isZombie_ReadyForDeallocation ()) {
1259
- // TODO (rokhinip): This is where we need to reset any override the thread
1260
- // might have as a result of this actor
1340
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1341
+ // Reset any override on this thread as a result of this thread running
1342
+ // the actor
1343
+ if (oldState.isMaxPriorityEscalated ()) {
1344
+ swift_dispatch_lock_override_end ((qos_class_t )oldState.getMaxPriority ());
1345
+ }
1346
+ #endif
1261
1347
deallocateUnconditional ();
1262
1348
SWIFT_TASK_DEBUG_LOG (" Unlock-ing actor %p succeeded with full deallocation" , this );
1263
1349
return true ;
1264
1350
}
1265
1351
1266
1352
auto newState = oldState;
1267
1353
if (oldState.getFirstJob () != NULL ) {
1268
- // There is work left to do
1354
+ // There is work left to do, don't unlock the actor
1269
1355
if (!forceUnlock) {
1270
1356
SWIFT_TASK_DEBUG_LOG (" Unlock-ing actor %p failed" , this );
1271
1357
return false ;
1272
1358
}
1273
-
1359
+ // We need to schedule the actor - remove any escalation bits since we'll
1360
+ // schedule the actor at the max priority currently on it
1274
1361
newState = newState.withScheduled ();
1362
+ newState = newState.withoutEscalatedPriority ();
1275
1363
} else {
1276
1364
// There is no work left to do - actor goes idle
1277
1365
newState = newState.withIdle ();
1278
- newState = newState.withMaxPriority (JobPriority::Unspecified );
1366
+ newState = newState.resetPriority ( );
1279
1367
}
1280
1368
1281
1369
if (_status ().compare_exchange_weak (oldState, newState,
@@ -1292,9 +1380,13 @@ bool DefaultActorImpl::unlock(bool forceUnlock)
1292
1380
SWIFT_TASK_DEBUG_LOG (" Actor %p is idle now" , this );
1293
1381
}
1294
1382
1295
- // TODO (rokhinip): Reset any overrides the thread might have had as a
1296
- // result of the actor
1297
-
1383
+ #if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
1384
+ // Reset any override on this thread as a result of this thread running
1385
+ // the actor. Only do this after we have reenqueued the actor
1386
+ if (oldState.isMaxPriorityEscalated ()) {
1387
+ swift_dispatch_lock_override_end ((qos_class_t ) oldState.getMaxPriority ());
1388
+ }
1389
+ #endif
1298
1390
return true ;
1299
1391
}
1300
1392
}
0 commit comments