Skip to content

Commit 0bc2198

Browse files
committed
Properly save and restore the current task in the runtime so that tasks
can be reentrantly executed. I don't think doing this is *actually a good idea*, but corrupting the runtime is an even worse idea, and the overhead here is very low.
1 parent 9647cdd commit 0bc2198

File tree

2 files changed

+22
-7
lines changed

2 files changed

+22
-7
lines changed

include/swift/Threading/ThreadLocalStorage.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ class ThreadLocal {
115115
T get() { return value; }
116116

117117
void set(T newValue) { value = newValue; }
118+
119+
T swap(T newValue) {
120+
// There's an implicit optimization here because we implicitly
121+
// materialize the address of the thread-local once instead of
122+
// separately for two calls to get and set.
123+
auto curValue = get();
124+
set(newValue);
125+
return curValue;
126+
}
118127
};
119128
#else
120129
// A wrapper around a TLS key that is lazily initialized using swift::once.
@@ -168,6 +177,12 @@ class ThreadLocal {
168177
memcpy(&storedValue, &newValue, sizeof(T));
169178
tls_set(key.getKey(), storedValue);
170179
}
180+
181+
T swap(T newValue) {
182+
auto curValue = get();
183+
set(newValue);
184+
return curValue;
185+
}
171186
};
172187
#endif
173188

stdlib/public/Concurrency/Actor.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ class ActiveTask {
196196
public:
197197
static void set(AsyncTask *task) { Value.set(task); }
198198
static AsyncTask *get() { return Value.get(); }
199+
static AsyncTask *swap(AsyncTask *newTask) {
200+
return Value.swap(newTask);
201+
}
199202
};
200203

201204
/// Define the thread-locals.
@@ -217,7 +220,7 @@ void swift::runJobInEstablishedExecutorContext(Job *job) {
217220

218221
if (auto task = dyn_cast<AsyncTask>(job)) {
219222
// Update the active task in the current thread.
220-
ActiveTask::set(task);
223+
auto oldTask = ActiveTask::swap(task);
221224

222225
// Update the task status to say that it's running on the
223226
// current thread. If the task suspends somewhere, it should
@@ -231,6 +234,7 @@ void swift::runJobInEstablishedExecutorContext(Job *job) {
231234

232235
assert(ActiveTask::get() == nullptr &&
233236
"active task wasn't cleared before suspending?");
237+
if (oldTask) ActiveTask::set(oldTask);
234238
} else {
235239
// There's no extra bookkeeping to do for simple jobs besides swapping in
236240
// the voucher.
@@ -259,15 +263,11 @@ AsyncTask *swift::swift_task_getCurrent() {
259263
}
260264

261265
AsyncTask *swift::_swift_task_clearCurrent() {
262-
auto task = ActiveTask::get();
263-
ActiveTask::set(nullptr);
264-
return task;
266+
return ActiveTask::swap(nullptr);
265267
}
266268

267269
AsyncTask *swift::_swift_task_setCurrent(AsyncTask *new_task) {
268-
auto task = ActiveTask::get();
269-
ActiveTask::set(new_task);
270-
return task;
270+
return ActiveTask::swap(new_task);
271271
}
272272

273273
SWIFT_CC(swift)

0 commit comments

Comments
 (0)