Skip to content

Commit 0b5081d

Browse files
authored
Merge pull request #40191 from rjmccall/job-priority-sorting-prep
2 parents 4ca4625 + 0bd0f77 commit 0b5081d

File tree

7 files changed

+650
-392
lines changed

7 files changed

+650
-392
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2022,6 +2022,12 @@ enum class JobPriority : size_t {
20222022
Unspecified = 0x00,
20232023
};
20242024

2025+
/// A tri-valued comparator which orders higher priorities first.
2026+
inline int descendingPriorityOrder(JobPriority lhs,
2027+
JobPriority rhs) {
2028+
return (lhs == rhs ? 0 : lhs > rhs ? -1 : 1);
2029+
}
2030+
20252031
/// Flags for task creation.
20262032
class TaskCreateFlags : public FlagSet<size_t> {
20272033
public:

include/swift/Basic/ListMerger.h

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,99 @@ class ListMerger {
133133
setLastInsertionPoint(newNode, /*known last of equals*/ true);
134134
}
135135

136+
/// Add a single node to this merger's current list.
137+
///
138+
/// The next reference of the node will be overwritten and does not
139+
/// need to be meaningful.
140+
///
141+
/// The relative order of nodes in the current list will not change,
142+
/// and if there are nodes in the current list which compare equal
143+
/// to the new node, it will be inserted *before* them.
144+
///
145+
/// This is useful for the pattern where nodes are naturally encountered
146+
/// in the opposite of their desired order in the final list and
147+
/// need to be reversed. It generally doesn't make any sense to mix
148+
/// this with calls to insert or merge on the same merger.
149+
void insertAtFront(Node newNode) {
150+
assert(newNode && "inserting a null node");
151+
152+
auto insertBetween = [newNode, this](Node prev, Node next) {
153+
if (prev) {
154+
assert(NodeTraits::getNext(prev) == next);
155+
assert(NodeTraits::compare(prev, newNode) < 0);
156+
NodeTraits::setNext(prev, newNode);
157+
} else {
158+
assert(root == next);
159+
root = newNode;
160+
}
161+
162+
assert(!next || NodeTraits::compare(newNode, next) <= 0);
163+
NodeTraits::setNext(newNode, next);
164+
setLastInsertionPoint(prev, /*known last of equals*/ true);
165+
};
166+
167+
Node prev = Node();
168+
Node cur = root;
169+
170+
// If we have a previous insertion point, check for the presumed-common
171+
// case that we're inserting something that should immediately follow it.
172+
if (auto lastIP = lastInsertionPoint) {
173+
lastIP = findLastOfEqualsFromLastIP(lastIP);
174+
175+
// Compare against the next node after lastIP, if it exists.
176+
if (Node nextAfterLastIP = NodeTraits::getNext(lastIP)) {
177+
int comparison = NodeTraits::compare(nextAfterLastIP, newNode);
178+
179+
// If the new node compares equal to the next node, insert here.
180+
if (comparison == 0) {
181+
insertBetween(lastIP, nextAfterLastIP);
182+
return;
183+
}
184+
185+
// If the new node should follow the next node, start scanning
186+
// after it.
187+
if (comparison < 0) {
188+
prev = nextAfterLastIP;
189+
cur = NodeTraits::getNext(nextAfterLastIP);
190+
}
191+
192+
// Otherwise, we'll need to scan from the beginning.
193+
194+
// If there is no next node, compare against the previous.
195+
} else {
196+
int comparison = NodeTraits::compare(lastIP, newNode);
197+
198+
// If the new node should follow the last node, we can
199+
// insert here.
200+
if (comparison < 0) {
201+
insertBetween(lastIP, Node());
202+
return;
203+
}
204+
205+
// Otherwise, we'll need to scan from the beginning.
206+
}
207+
}
208+
209+
assert(!prev || NodeTraits::compare(prev, newNode) < 0);
210+
211+
// Scan forward, looking for a node which the new node must be
212+
// inserted prior to.
213+
// Invariant: prev < newNode, if prev exists
214+
while (cur) {
215+
// Compare the new node against the current IP.
216+
int comparison = NodeTraits::compare(cur, newNode);
217+
218+
// If the new node isn't strictly greater than cur, insert here.
219+
if (comparison >= 0) break;
220+
221+
// Otherwise, continue.
222+
prev = cur;
223+
cur = NodeTraits::getNext(prev);
224+
}
225+
226+
insertBetween(prev, cur);
227+
}
228+
136229
/// Add a sorted list of nodes to this merger's current list.
137230
/// The list must be well-formed (i.e. appropriately terminated).
138231
///
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
///===--- CooperativeGlobalExecutor.inc ---------------------*- C++ -*--===///
2+
///
3+
/// This source file is part of the Swift.org open source project
4+
///
5+
/// Copyright (c) 2014 - 2020 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+
/// The implementation of the cooperative global executor.
14+
///
15+
/// This file is included into GlobalExecutor.cpp only when
16+
/// the cooperative global executor is enabled. It is expected to
17+
/// declare the following functions:
18+
/// swift_task_enqueueGlobalImpl
19+
/// swift_task_enqueueGlobalWithDelayImpl
20+
/// swift_task_enqueueMainExecutorImpl
21+
/// as well as any cooperative-executor-specific functions in the runtime.
22+
///
23+
///===------------------------------------------------------------------===///
24+
25+
#include <chrono>
26+
#include <thread>
27+
28+
static Job *JobQueue = nullptr;
29+
30+
class DelayedJob {
31+
public:
32+
Job *job;
33+
unsigned long long when;
34+
DelayedJob *next;
35+
36+
DelayedJob(Job *job, unsigned long long when) : job(job), when(when), next(nullptr) {}
37+
};
38+
39+
static DelayedJob *DelayedJobQueue = nullptr;
40+
41+
/// Get the next-in-queue storage slot.
42+
static Job *&nextInQueue(Job *cur) {
43+
return reinterpret_cast<Job*&>(cur->SchedulerPrivate[Job::NextWaitingTaskIndex]);
44+
}
45+
46+
/// Insert a job into the cooperative global queue.
47+
SWIFT_CC(swift)
48+
static void swift_task_enqueueGlobalImpl(Job *job) {
49+
assert(job && "no job provided");
50+
51+
Job **position = &JobQueue;
52+
while (auto cur = *position) {
53+
// If we find a job with lower priority, insert here.
54+
if (cur->getPriority() < newJob->getPriority()) {
55+
nextInQueue(newJob) = cur;
56+
*position = newJob;
57+
return;
58+
}
59+
60+
// Otherwise, keep advancing through the queue.
61+
position = &nextInQueue(cur);
62+
}
63+
nextInQueue(newJob) = nullptr;
64+
*position = newJob;
65+
}
66+
67+
/// Enqueues a task on the main executor.
68+
SWIFT_CC(swift)
69+
static void swift_task_enqueueMainExecutorImpl(Job *job) {
70+
// The cooperative executor does not distinguish between the main
71+
// queue and the global queue.
72+
swift_task_enqueueGlobalImpl(job);
73+
}
74+
75+
static unsigned long long currentNanos() {
76+
auto now = std::chrono::steady_clock::now();
77+
auto nowNanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
78+
auto value = std::chrono::duration_cast<std::chrono::nanoseconds>(nowNanos.time_since_epoch());
79+
return value.count();
80+
}
81+
82+
/// Insert a job into the cooperative global queue with a delay.
83+
SWIFT_CC(swift)
84+
static void swift_task_enqueueGlobalWithDelayImpl(unsigned long long delay,
85+
Job *job) {
86+
assert(job && "no job provided");
87+
88+
DelayedJob **position = &DelayedJobQueue;
89+
DelayedJob *newJob = new DelayedJob(job, currentNanos() + delay);
90+
91+
while (auto cur = *position) {
92+
// If we find a job with lower priority, insert here.
93+
if (cur->when > newJob->when) {
94+
newJob->next = cur;
95+
*position = newJob;
96+
return;
97+
}
98+
99+
// Otherwise, keep advancing through the queue.
100+
position = &cur->next;
101+
}
102+
*position = newJob;
103+
}
104+
105+
/// Claim the next job from the cooperative global queue.
106+
static Job *claimNextFromCooperativeGlobalQueue() {
107+
// Check delayed jobs first
108+
while (true) {
109+
if (auto delayedJob = DelayedJobQueue) {
110+
if (delayedJob->when < currentNanos()) {
111+
DelayedJobQueue = delayedJob->next;
112+
auto job = delayedJob->job;
113+
114+
delete delayedJob;
115+
116+
return job;
117+
}
118+
}
119+
if (auto job = JobQueue) {
120+
JobQueue = nextInQueue(job);
121+
return job;
122+
}
123+
// there are only delayed jobs left, but they are not ready,
124+
// so we sleep until the first one is
125+
if (auto delayedJob = DelayedJobQueue) {
126+
std::this_thread::sleep_for(std::chrono::nanoseconds(delayedJob->when - currentNanos()));
127+
continue;
128+
}
129+
return nullptr;
130+
}
131+
}
132+
133+
void swift::
134+
swift_task_donateThreadToGlobalExecutorUntil(bool (*condition)(void *),
135+
void *conditionContext) {
136+
while (!condition(conditionContext)) {
137+
auto job = claimNextFromCooperativeGlobalQueue();
138+
if (!job) return;
139+
swift_job_run(job, ExecutorRef::generic());
140+
}
141+
}

0 commit comments

Comments
 (0)