|
24 | 24 | #include "swift/ABI/Metadata.h" |
25 | 25 | #include "llvm/ADT/PointerIntPair.h" |
26 | 26 | #include "TaskPrivate.h" |
| 27 | +#include <set> |
27 | 28 |
|
28 | 29 | #if defined(__APPLE__) |
29 | 30 | #include <asl.h> |
@@ -134,6 +135,22 @@ static void swift_task_localValuePopImpl() { |
134 | 135 | assert(false && "Attempted to pop value but no task or thread-local storage available!"); |
135 | 136 | } |
136 | 137 |
|
| 138 | +SWIFT_CC(swift) |
| 139 | +static void swift_task_localsCopyToImpl(AsyncTask *task) { |
| 140 | + TaskLocal::Storage *Local = nullptr; |
| 141 | + |
| 142 | + if (AsyncTask *task = swift_task_getCurrent()) { |
| 143 | + Local = &task->Local; |
| 144 | + } else if (auto *storage = FallbackTaskLocalStorage::get()) { |
| 145 | + Local = storage; |
| 146 | + } else { |
| 147 | + // bail out, there are no values to copy |
| 148 | + return; |
| 149 | + } |
| 150 | + |
| 151 | + Local->copyTo(task); |
| 152 | +} |
| 153 | + |
137 | 154 | // ============================================================================= |
138 | 155 | // ==== Initialization --------------------------------------------------------- |
139 | 156 |
|
@@ -185,6 +202,38 @@ TaskLocal::Item::createParentLink(AsyncTask *task, AsyncTask *parent) { |
185 | 202 | return item; |
186 | 203 | } |
187 | 204 |
|
| 205 | +TaskLocal::Item* |
| 206 | +TaskLocal::Item::createLink(AsyncTask *task, |
| 207 | + const HeapObject *key, |
| 208 | + const Metadata *valueType) { |
| 209 | + size_t amountToAllocate = Item::itemSize(valueType); |
| 210 | + void *allocation = task ? _swift_task_alloc_specific(task, amountToAllocate) |
| 211 | + : malloc(amountToAllocate); |
| 212 | + Item *item = new (allocation) Item(key, valueType); |
| 213 | + |
| 214 | + auto next = task ? task->Local.head : FallbackTaskLocalStorage::get()->head; |
| 215 | + item->next = reinterpret_cast<uintptr_t>(next) | |
| 216 | + static_cast<uintptr_t>(NextLinkType::IsNext); |
| 217 | + |
| 218 | + return item; |
| 219 | +} |
| 220 | + |
| 221 | + |
| 222 | +void TaskLocal::Item::copyTo(AsyncTask *target) { |
| 223 | + assert(target && "TaskLocal item attempt to copy to null target task!"); |
| 224 | + |
| 225 | + auto item = Item::createLink(target, this->key, this->valueType); |
| 226 | + valueType->vw_initializeWithTake(item->getStoragePtr(), this->getStoragePtr()); |
| 227 | + |
| 228 | + /// A `copyTo` may ONLY be invoked BEFORE the task is actually scheduled, |
| 229 | + /// so right now we can safely copy the value into the task without additional |
| 230 | + /// synchronization. |
| 231 | + target->Local.head = item; |
| 232 | +} |
| 233 | + |
| 234 | +// ============================================================================= |
| 235 | +// ==== checks ----------------------------------------------------------------- |
| 236 | + |
188 | 237 | SWIFT_CC(swift) |
189 | 238 | static void swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroupImpl( |
190 | 239 | const unsigned char *file, uintptr_t fileLength, |
@@ -257,22 +306,6 @@ static void swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroupImpl( |
257 | 306 | abort(); |
258 | 307 | } |
259 | 308 |
|
260 | | -TaskLocal::Item* |
261 | | -TaskLocal::Item::createLink(AsyncTask *task, |
262 | | - const HeapObject *key, |
263 | | - const Metadata *valueType) { |
264 | | - size_t amountToAllocate = Item::itemSize(valueType); |
265 | | - void *allocation = task ? _swift_task_alloc_specific(task, amountToAllocate) |
266 | | - : malloc(amountToAllocate); |
267 | | - Item *item = new (allocation) Item(key, valueType); |
268 | | - |
269 | | - auto next = task ? task->Local.head : FallbackTaskLocalStorage::get()->head; |
270 | | - item->next = reinterpret_cast<uintptr_t>(next) | |
271 | | - static_cast<uintptr_t>(NextLinkType::IsNext); |
272 | | - |
273 | | - return item; |
274 | | -} |
275 | | - |
276 | 309 | // ============================================================================= |
277 | 310 | // ==== destroy ---------------------------------------------------------------- |
278 | 311 |
|
@@ -311,7 +344,7 @@ void TaskLocal::Storage::destroy(AsyncTask *task) { |
311 | 344 | } |
312 | 345 |
|
313 | 346 | // ============================================================================= |
314 | | -// ==== push / pop / get ------------------------------------------------------- |
| 347 | +// ==== Task Local Storage: operations ----------------------------------------- |
315 | 348 |
|
316 | 349 | void TaskLocal::Storage::pushValue(AsyncTask *task, |
317 | 350 | const HeapObject *key, |
@@ -350,5 +383,32 @@ OpaqueValue* TaskLocal::Storage::getValue(AsyncTask *task, |
350 | 383 | return nullptr; |
351 | 384 | } |
352 | 385 |
|
| 386 | + |
| 387 | +void TaskLocal::Storage::copyTo(AsyncTask *target) { |
| 388 | + assert(target && "task must not be null when copying values into it"); |
| 389 | + assert(!(target->Local.head) && |
| 390 | + "Task must not have any task-local values bound before copying into it"); |
| 391 | + |
| 392 | + AsyncTask *task = swift_task_getCurrent(); |
| 393 | + |
| 394 | + // Set of keys for which we already have copied to the new task. |
| 395 | + // We only ever need to copy the *first* encounter of any given key, |
| 396 | + // because it is the most "specific"/"recent" binding and any other binding |
| 397 | + // of a key does not matter for the target task as it will never be able to |
| 398 | + // observe it. |
| 399 | + std::set<const HeapObject*> copied = {}; |
| 400 | + |
| 401 | + auto item = head; |
| 402 | + while (item) { |
| 403 | + // we only have to copy an item if it is the most recent binding of a key. |
| 404 | + // i.e. if we've already seen an item for this key, we can skip it. |
| 405 | + if (copied.emplace(item->key).second) { |
| 406 | + item->copyTo(target); |
| 407 | + } |
| 408 | + |
| 409 | + item = item->getNext(); |
| 410 | + } |
| 411 | +} |
| 412 | + |
353 | 413 | #define OVERRIDE_TASK_LOCAL COMPATIBILITY_OVERRIDE |
354 | 414 | #include COMPATIBILITY_OVERRIDE_INCLUDE_PATH |
0 commit comments