Skip to content

Commit d77edf6

Browse files
authored
Merge pull request #41088 from apple/rokhinip/76127624-task-priority-escalation
Task Priority Escalation
2 parents f33599c + 66d4af0 commit d77edf6

File tree

15 files changed

+883
-254
lines changed

15 files changed

+883
-254
lines changed

include/swift/ABI/Task.h

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -290,23 +290,35 @@ class AsyncTask : public Job {
290290
return ResumeTask(ResumeContext); // 'return' forces tail call
291291
}
292292

293+
/// A task can have the following states:
294+
/// * suspended: In this state, a task is considered not runnable
295+
/// * enqueued: In this state, a task is considered runnable
296+
/// * running on a thread
297+
/// * completed
298+
///
299+
/// The following state transitions are possible:
300+
/// suspended -> enqueued
301+
/// suspended -> running
302+
/// enqueued -> running
303+
/// running -> suspended
304+
/// running -> completed
305+
/// running -> enqueued
306+
///
307+
/// The 4 methods below are how a task switches from one state to another.
308+
293309
/// Flag that this task is now running. This can update
294310
/// the priority stored in the job flags if the priority has been
295311
/// escalated.
296312
///
297313
/// Generally this should be done immediately after updating
298314
/// ActiveTask.
299315
void flagAsRunning();
300-
void flagAsRunning_slow();
301-
302-
/// Flag that this task is now suspended. This can update the
303-
/// priority stored in the job flags if the priority hsa been
304-
/// escalated. Generally this should be done immediately after
305-
/// clearing ActiveTask and immediately before enqueuing the task
306-
/// somewhere. TODO: record where the task is enqueued if
307-
/// possible.
316+
317+
/// Flag that this task is now suspended.
308318
void flagAsSuspended();
309-
void flagAsSuspended_slow();
319+
320+
/// Flag that the task is to be enqueued on the provided executor
321+
void flagAsEnqueuedOnExecutor(ExecutorRef newExecutor);
310322

311323
/// Flag that this task is now completed. This normally does not do anything
312324
/// but can be used to locally insert logging.

include/swift/Reflection/ReflectionContext.h

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ class ReflectionContext
106106
typename super::StoredPointer target_future_adapter = 0;
107107
typename super::StoredPointer target_task_wait_throwing_resume_adapter = 0;
108108
typename super::StoredPointer target_task_future_wait_resume_adapter = 0;
109+
bool supportsPriorityEscalation = false;
109110

110111
public:
111112
using super::getBuilder;
@@ -164,7 +165,7 @@ class ReflectionContext
164165

165166
ReflectionContext(const ReflectionContext &other) = delete;
166167
ReflectionContext &operator=(const ReflectionContext &other) = delete;
167-
168+
168169
MemoryReader &getReader() {
169170
return *this->Reader;
170171
}
@@ -367,7 +368,7 @@ class ReflectionContext
367368

368369
auto Begin = RemoteRef<void>(Addr, BufStart);
369370
auto Size = COFFSec->VirtualSize;
370-
371+
371372
// FIXME: This code needs to be cleaned up and updated
372373
// to make it work for 32 bit platforms.
373374
Begin = Begin.atByteOffset(8);
@@ -605,7 +606,7 @@ class ReflectionContext
605606
}
606607

607608
/// Parses metadata information from an ELF image. Because the Section
608-
/// Header Table maybe be missing (for example, when reading from a
609+
/// Header Table maybe be missing (for example, when reading from a
609610
/// process) this method optionally receives a buffer with the contents
610611
/// of the image's file, from where it will the necessary information.
611612
///
@@ -652,19 +653,19 @@ class ReflectionContext
652653
auto Magic = this->getReader().readBytes(ImageStart, sizeof(uint32_t));
653654
if (!Magic)
654655
return false;
655-
656+
656657
uint32_t MagicWord;
657658
memcpy(&MagicWord, Magic.get(), sizeof(MagicWord));
658-
659+
659660
// 32- and 64-bit Mach-O.
660661
if (MagicWord == llvm::MachO::MH_MAGIC) {
661662
return readMachOSections<MachOTraits<4>>(ImageStart);
662663
}
663-
664+
664665
if (MagicWord == llvm::MachO::MH_MAGIC_64) {
665666
return readMachOSections<MachOTraits<8>>(ImageStart);
666667
}
667-
668+
668669
// PE. (This just checks for the DOS header; `readPECOFF` will further
669670
// validate the existence of the PE header.)
670671
auto MagicBytes = (const char*)Magic.get();
@@ -739,7 +740,7 @@ class ReflectionContext
739740
return true;
740741
return ownsAddress(RemoteAddress(*MetadataAddress));
741742
}
742-
743+
743744
/// Returns true if the address falls within a registered image.
744745
bool ownsAddressRaw(RemoteAddress Address) {
745746
for (auto Range : imageRanges) {
@@ -1193,7 +1194,7 @@ class ReflectionContext
11931194
IterateConformanceTable(ConformancesAddr->getResolvedAddress(), Call);
11941195
return llvm::None;
11951196
}
1196-
1197+
11971198
/// Fetch the metadata pointer from a metadata allocation, or 0 if this
11981199
/// allocation's tag is not handled or an error occurred.
11991200
StoredPointer allocationMetadataPointer(
@@ -1291,7 +1292,7 @@ class ReflectionContext
12911292
getReader().readPointer(AllocationPoolAddrAddr, sizeof(StoredPointer));
12921293
if (!AllocationPoolAddr)
12931294
return "failed to read value of " + AllocationPoolPointerName;
1294-
1295+
12951296
struct PoolRange {
12961297
StoredPointer Begin;
12971298
StoredSize Remaining;
@@ -1337,10 +1338,10 @@ class ReflectionContext
13371338
Allocation.Ptr = RemoteAddr;
13381339
Allocation.Size = Header->Size;
13391340
Call(Allocation);
1340-
1341+
13411342
Offset += sizeof(AllocationHeader) + Header->Size;
13421343
}
1343-
1344+
13441345
TrailerPtr = Trailer->PrevTrailer;
13451346
}
13461347
return llvm::None;
@@ -1421,13 +1422,20 @@ class ReflectionContext
14211422

14221423
std::pair<llvm::Optional<std::string>, AsyncTaskInfo>
14231424
asyncTaskInfo(StoredPointer AsyncTaskPtr) {
1424-
auto AsyncTaskObj = readObj<AsyncTask<Runtime>>(AsyncTaskPtr);
1425+
loadTargetPointers();
1426+
1427+
if (supportsPriorityEscalation) {
1428+
return {std::string("Failure reading async task with escalation support"), {}};
1429+
}
1430+
1431+
using AsyncTask = AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>>;
1432+
auto AsyncTaskObj = readObj<AsyncTask>(AsyncTaskPtr);
14251433
if (!AsyncTaskObj)
14261434
return {std::string("failure reading async task"), {}};
14271435

14281436
AsyncTaskInfo Info{};
14291437
Info.JobFlags = AsyncTaskObj->Flags;
1430-
Info.TaskStatusFlags = AsyncTaskObj->PrivateStorage.Status.Flags;
1438+
Info.TaskStatusFlags = AsyncTaskObj->PrivateStorage.Status.Flags[0];
14311439
Info.Id =
14321440
AsyncTaskObj->Id | ((uint64_t)AsyncTaskObj->PrivateStorage.Id << 32);
14331441
Info.AllocatorSlabPtr = AsyncTaskObj->PrivateStorage.Allocator.FirstSlab;
@@ -1461,7 +1469,7 @@ class ReflectionContext
14611469
Info.ChildTasks.push_back(ChildTask);
14621470

14631471
StoredPointer ChildFragmentAddr =
1464-
ChildTask + sizeof(AsyncTask<Runtime>);
1472+
ChildTask + sizeof(AsyncTask);
14651473
auto ChildFragmentObj =
14661474
readObj<ChildFragment<Runtime>>(ChildFragmentAddr);
14671475
if (ChildFragmentObj)
@@ -1478,8 +1486,8 @@ class ReflectionContext
14781486
// that's available.
14791487
int IsCancelledFlag = 0x100;
14801488
int IsRunningFlag = 0x800;
1481-
if (!(AsyncTaskObj->PrivateStorage.Status.Flags & IsCancelledFlag) &&
1482-
!(AsyncTaskObj->PrivateStorage.Status.Flags & IsRunningFlag)) {
1489+
if (!(AsyncTaskObj->PrivateStorage.Status.Flags[0] & IsCancelledFlag) &&
1490+
!(AsyncTaskObj->PrivateStorage.Status.Flags[0] & IsRunningFlag)) {
14831491
auto ResumeContext = AsyncTaskObj->ResumeContextAndReserved[0];
14841492
while (ResumeContext) {
14851493
auto ResumeContextObj = readObj<AsyncContext<Runtime>>(ResumeContext);
@@ -1530,7 +1538,7 @@ class ReflectionContext
15301538
private:
15311539
// Get the most human meaningful "run job" function pointer from the task,
15321540
// like AsyncTask::getResumeFunctionForLogging does.
1533-
StoredPointer getRunJob(const AsyncTask<Runtime> *AsyncTaskObj) {
1541+
StoredPointer getRunJob(const AsyncTask<Runtime, ActiveTaskStatusWithoutEscalation<Runtime>> *AsyncTaskObj) {
15341542
auto Fptr = stripSignedPointer(AsyncTaskObj->RunJob);
15351543

15361544
loadTargetPointers();
@@ -1589,6 +1597,11 @@ class ReflectionContext
15891597
getFunc("_swift_concurrency_debug_task_wait_throwing_resume_adapter");
15901598
target_task_future_wait_resume_adapter =
15911599
getFunc("_swift_concurrency_debug_task_future_wait_resume_adapter");
1600+
auto supportsPriorityEscalationAddr = getReader().getSymbolAddress("_swift_concurrency_debug_supportsPriorityEscalation");
1601+
if (supportsPriorityEscalationAddr) {
1602+
getReader().readInteger(supportsPriorityEscalationAddr, &supportsPriorityEscalation);
1603+
}
1604+
15921605
setupTargetPointers = true;
15931606
}
15941607

include/swift/Reflection/RuntimeInternals.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -94,28 +94,34 @@ struct StackAllocator {
9494
};
9595

9696
template <typename Runtime>
97-
struct ActiveTaskStatus {
97+
struct ActiveTaskStatusWithEscalation {
98+
uint32_t Flags;
99+
uint32_t DrainLock[(sizeof(typename Runtime::StoredPointer) == 8) ? 1 : 2];
98100
typename Runtime::StoredPointer Record;
99-
typename Runtime::StoredSize Flags;
100101
};
101102

102103
template <typename Runtime>
104+
struct ActiveTaskStatusWithoutEscalation {
105+
uint32_t Flags[sizeof(typename Runtime::StoredPointer) == 8 ? 2 : 1];
106+
typename Runtime::StoredPointer Record;
107+
};
108+
109+
template <typename Runtime, typename ActiveTaskStatus>
103110
struct AsyncTaskPrivateStorage {
104-
ActiveTaskStatus<Runtime> Status;
111+
ActiveTaskStatus Status;
105112
StackAllocator<Runtime> Allocator;
106113
typename Runtime::StoredPointer Local;
107114
typename Runtime::StoredPointer ExclusivityAccessSet[2];
108115
uint32_t Id;
109116
};
110117

111-
template <typename Runtime>
118+
template <typename Runtime, typename ActiveTaskStatus>
112119
struct AsyncTask: Job<Runtime> {
113-
// On 64-bit, there's a Reserved64 after ResumeContext.
120+
// On 64-bit, there's a Reserved64 after ResumeContext.
114121
typename Runtime::StoredPointer ResumeContextAndReserved[
115122
sizeof(typename Runtime::StoredPointer) == 8 ? 2 : 1];
116-
117123
union {
118-
AsyncTaskPrivateStorage<Runtime> PrivateStorage;
124+
AsyncTaskPrivateStorage<Runtime, ActiveTaskStatus> PrivateStorage;
119125
typename Runtime::StoredPointer PrivateStorageRaw[14];
120126
};
121127
};

include/swift/Runtime/Concurrency.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@
4141
#endif
4242
#endif
4343

44+
// Does the runtime provide priority escalation support?
45+
#ifndef SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
46+
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH && \
47+
__has_include(<dispatch/swift_concurrency_private.h>) && __APPLE__ && \
48+
SWIFT_POINTER_IS_8_BYTES
49+
#define SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION 1
50+
#else
51+
#define SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION 0
52+
#endif
53+
#endif /* SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION */
54+
4455
namespace swift {
4556
class DefaultActor;
4657
class TaskOptionRecord;
@@ -616,6 +627,17 @@ void swift_task_switch(SWIFT_ASYNC_CONTEXT AsyncContext *resumeToContext,
616627
TaskContinuationFunction *resumeFunction,
617628
ExecutorRef newExecutor);
618629

630+
/// Mark a task for enqueue on a new executor and then enqueue it.
631+
///
632+
/// The resumption function pointer and continuation should be set
633+
/// appropriately in the task.
634+
///
635+
/// Generally you should call swift_task_switch to switch execution
636+
/// synchronously when possible.
637+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
638+
void
639+
swift_task_enqueueTaskOnExecutor(AsyncTask *task, ExecutorRef executor);
640+
619641
/// Enqueue the given job to run asynchronously on the given executor.
620642
///
621643
/// The resumption function pointer and continuation should be set

include/swift/Runtime/DispatchShims.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//===--- DispatchShims.h - Shims for dispatch vended APIs --------------------*- C++ -*-//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_CONCURRENCY_DISPATCHSHIMS_H
14+
#define SWIFT_CONCURRENCY_DISPATCHSHIMS_H
15+
16+
#include "Concurrency.h"
17+
18+
#if SWIFT_CONCURRENCY_ENABLE_PRIORITY_ESCALATION
19+
#include <dispatch/swift_concurrency_private.h>
20+
21+
// Provide wrappers with runtime checks to make sure that the dispatch functions
22+
// are only called on OS-es where they are supported
23+
dispatch_thread_override_info_s
24+
swift_dispatch_thread_get_current_override_qos_floor()
25+
{
26+
if (__builtin_available(macOS 9998, iOS 9998, tvOS 9998, watchOS 9998, *)) {
27+
return dispatch_thread_get_current_override_qos_floor();
28+
}
29+
30+
return (dispatch_thread_override_info_s) {0};
31+
}
32+
33+
int
34+
swift_dispatch_thread_override_self(qos_class_t override_qos) {
35+
36+
if (__builtin_available(macOS 9998, iOS 9998, tvOS 9998, watchOS 9998, *)) {
37+
return dispatch_thread_override_self(override_qos);
38+
}
39+
40+
return 0;
41+
}
42+
43+
int
44+
swift_dispatch_lock_override_start_with_debounce(dispatch_lock_t *lock_addr,
45+
dispatch_tid_t expected_thread, qos_class_t override_to_apply) {
46+
47+
if (__builtin_available(macOS 9998, iOS 9998, tvOS 9998, watchOS 9998, *)) {
48+
return dispatch_lock_override_start_with_debounce(lock_addr, expected_thread, override_to_apply);
49+
}
50+
51+
return 0;
52+
}
53+
54+
int
55+
swift_dispatch_lock_override_end(qos_class_t override_to_end) {
56+
if (__builtin_available(macOS 9998, iOS 9998, tvOS 9998, watchOS 9998, *)) {
57+
return dispatch_lock_override_end(override_to_end);
58+
}
59+
60+
return 0;
61+
}
62+
#endif
63+
64+
#endif /* SWIFT_CONCURRENCY_DISPATCHSHIMS_H */

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ OVERRIDE_TASK(task_suspend, AsyncTask *,
159159
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
160160
swift::, ,)
161161

162+
OVERRIDE_TASK(task_enqueueTaskOnExecutor, void,
163+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
164+
(AsyncTask *task, ExecutorRef newExecutor), (task, newExecutor))
165+
162166
OVERRIDE_TASK(continuation_init, AsyncTask *,
163167
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
164168
swift::, (ContinuationAsyncContext *context,

stdlib/public/Concurrency/Actor.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,9 +1617,9 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
16171617
// executor.
16181618
SWIFT_TASK_DEBUG_LOG("switch failed, task %p enqueued on executor %p", task,
16191619
newExecutor.getIdentity());
1620-
task->flagAsSuspended();
1620+
1621+
task->flagAsEnqueuedOnExecutor(newExecutor);
16211622
_swift_task_clearCurrent();
1622-
swift_task_enqueue(task, newExecutor);
16231623
}
16241624

16251625
/*****************************************************************************/

stdlib/public/Concurrency/Debug.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ const void *const _swift_concurrency_debug_task_wait_throwing_resume_adapter;
4545
SWIFT_EXPORT_FROM(swift_Concurrency)
4646
const void *const _swift_concurrency_debug_task_future_wait_resume_adapter;
4747

48+
/// Whether the runtime we are inspecting supports priority escalation
49+
SWIFT_EXPORT_FROM(swift_Concurrency)
50+
bool _swift_concurrency_debug_supportsPriorityEscalation;
51+
4852
} // namespace swift
4953

5054
#endif

0 commit comments

Comments
 (0)