forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGlobalExecutor.cpp
211 lines (187 loc) · 7.8 KB
/
GlobalExecutor.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
///===--- GlobalExecutor.cpp - Global concurrent executor ------------------===///
///
/// This source file is part of the Swift.org open source project
///
/// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
/// Licensed under Apache License v2.0 with Runtime Library Exception
///
/// See https:///swift.org/LICENSE.txt for license information
/// See https:///swift.org/CONTRIBUTORS.txt for the list of Swift project authors
///
///===----------------------------------------------------------------------===///
///
/// Routines related to the global concurrent execution service.
///
/// The execution side of Swift's concurrency model centers around
/// scheduling work onto various execution services ("executors").
/// Executors vary in several different dimensions:
///
/// First, executors may be exclusive or concurrent. An exclusive
/// executor can only execute one job at once; a concurrent executor
/// can execute many. Exclusive executors are usually used to achieve
/// some higher-level requirement, like exclusive access to some
/// resource or memory. Concurrent executors are usually used to
/// manage a pool of threads and prevent the number of allocated
/// threads from growing without limit.
///
/// Second, executors may own dedicated threads, or they may schedule
/// work onto some underlying executor. Dedicated threads can
/// improve the responsiveness of a subsystem *locally*, but they impose
/// substantial costs which can drive down performance *globally*
/// if not used carefully. When an executor relies on running work
/// on its own dedicated threads, jobs that need to run briefly on
/// that executor may need to suspend and restart. Dedicating threads
/// to an executor is a decision that should be made carefully
/// and holistically.
///
/// If most executors should not have dedicated threads, they must
/// be backed by some underlying executor, typically a concurrent
/// executor. The purpose of most concurrent executors is to
/// manage threads and prevent excessive growth in the number
/// of threads. Having multiple independent concurrent executors
/// with their own dedicated threads would undermine that.
/// Therefore, it is sensible to have a single, global executor
/// that will ultimately schedule most of the work in the system.
/// With that as a baseline, special needs can be recognized and
/// carved out from the global executor with its cooperation.
///
/// This file defines Swift's interface to that global executor.
///
/// The default implementation is backed by libdispatch, but there
/// may be good reasons to provide alternatives (e.g. when building
/// a single-threaded runtime).
///
///===----------------------------------------------------------------------===///
#include "../CompatibilityOverride/CompatibilityOverride.h"
#include "swift/Runtime/Concurrency.h"
#include "swift/Runtime/EnvironmentVariables.h"
#include "TaskPrivate.h"
#include "Error.h"
using namespace swift;
SWIFT_CC(swift)
void (*swift::swift_task_enqueueGlobal_hook)(
Job *job, swift_task_enqueueGlobal_original original) = nullptr;
SWIFT_CC(swift)
void (*swift::swift_task_enqueueGlobalWithDelay_hook)(
JobDelay delay, Job *job,
swift_task_enqueueGlobalWithDelay_original original) = nullptr;
SWIFT_CC(swift)
void (*swift::swift_task_enqueueGlobalWithDeadline_hook)(
long long sec,
long long nsec,
long long tsec,
long long tnsec,
int clock, Job *job,
swift_task_enqueueGlobalWithDeadline_original original) = nullptr;
SWIFT_CC(swift)
bool (*swift::swift_task_isOnExecutor_hook)(
HeapObject *executor,
const Metadata *selfType,
const SerialExecutorWitnessTable *wtable,
swift_task_isOnExecutor_original original) = nullptr;
SWIFT_CC(swift)
void (*swift::swift_task_enqueueMainExecutor_hook)(
Job *job, swift_task_enqueueMainExecutor_original original) = nullptr;
#if SWIFT_CONCURRENCY_COOPERATIVE_GLOBAL_EXECUTOR
#include "CooperativeGlobalExecutor.inc"
#elif SWIFT_CONCURRENCY_ENABLE_DISPATCH
#include "DispatchGlobalExecutor.inc"
#else
#include "NonDispatchGlobalExecutor.inc"
#endif
void swift::swift_task_enqueueGlobal(Job *job) {
_swift_tsan_release(job);
concurrency::trace::job_enqueue_global(job);
if (swift_task_enqueueGlobal_hook)
swift_task_enqueueGlobal_hook(job, swift_task_enqueueGlobalImpl);
else
swift_task_enqueueGlobalImpl(job);
}
void swift::swift_task_enqueueGlobalWithDelay(JobDelay delay, Job *job) {
concurrency::trace::job_enqueue_global_with_delay(delay, job);
if (swift_task_enqueueGlobalWithDelay_hook)
swift_task_enqueueGlobalWithDelay_hook(
delay, job, swift_task_enqueueGlobalWithDelayImpl);
else
swift_task_enqueueGlobalWithDelayImpl(delay, job);
}
void swift::swift_task_enqueueGlobalWithDeadline(
long long sec,
long long nsec,
long long tsec,
long long tnsec,
int clock, Job *job) {
if (swift_task_enqueueGlobalWithDeadline_hook)
swift_task_enqueueGlobalWithDeadline_hook(
sec, nsec, tsec, tnsec, clock, job, swift_task_enqueueGlobalWithDeadlineImpl);
else
swift_task_enqueueGlobalWithDeadlineImpl(sec, nsec, tsec, tnsec, clock, job);
}
// Implemented in Swift because we need to obtain the user-defined flags on the executor ref.
//
// We could inline this with effort, though.
extern "C" SWIFT_CC(swift)
ExecutorRef _task_serialExecutor_getExecutorRef(
HeapObject *executor,
const Metadata *selfType,
const SerialExecutorWitnessTable *wtable);
SWIFT_CC(swift)
static bool swift_task_isOnExecutorImpl(HeapObject *executor,
const Metadata *selfType,
const SerialExecutorWitnessTable *wtable) {
auto executorRef = _task_serialExecutor_getExecutorRef(executor, selfType, wtable);
return swift_task_isCurrentExecutor(executorRef);
}
bool swift::swift_task_isOnExecutor(HeapObject *executor,
const Metadata *selfType,
const SerialExecutorWitnessTable *wtable) {
if (swift_task_isOnExecutor_hook)
return swift_task_isOnExecutor_hook(
executor, selfType, wtable, swift_task_isOnExecutorImpl);
else
return swift_task_isOnExecutorImpl(executor, selfType, wtable);
}
bool swift::swift_executor_isComplexEquality(ExecutorRef ref) {
return ref.isComplexEquality();
}
uint64_t swift::swift_task_getJobTaskId(Job *job) {
if (auto task = dyn_cast<AsyncTask>(job)) {
// TaskID is actually:
// 32bits of Job's Id
// + 32bits stored in the AsyncTask
return task->getTaskId();
} else {
return job->getJobId();
}
}
/*****************************************************************************/
/****************************** MAIN EXECUTOR *******************************/
/*****************************************************************************/
void swift::swift_task_enqueueMainExecutor(Job *job) {
concurrency::trace::job_enqueue_main_executor(job);
if (swift_task_enqueueMainExecutor_hook)
swift_task_enqueueMainExecutor_hook(job,
swift_task_enqueueMainExecutorImpl);
else
swift_task_enqueueMainExecutorImpl(job);
}
ExecutorRef swift::swift_task_getMainExecutor() {
#if !SWIFT_CONCURRENCY_ENABLE_DISPATCH
// FIXME: this isn't right for the non-cooperative environment
return ExecutorRef::generic();
#else
return ExecutorRef::forOrdinary(
reinterpret_cast<HeapObject*>(&_dispatch_main_q),
_swift_task_getDispatchQueueSerialExecutorWitnessTable());
#endif
}
bool ExecutorRef::isMainExecutor() const {
#if !SWIFT_CONCURRENCY_ENABLE_DISPATCH
// FIXME: this isn't right for the non-cooperative environment
return isGeneric();
#else
return Identity == reinterpret_cast<HeapObject*>(&_dispatch_main_q);
#endif
}
#define OVERRIDE_GLOBAL_EXECUTOR COMPATIBILITY_OVERRIDE
#include COMPATIBILITY_OVERRIDE_INCLUDE_PATH