1111// ===----------------------------------------------------------------------===//
1212
1313#include " ../CompatibilityOverride/CompatibilityOverride.h"
14- #include " swift/ABI/TaskLocal.h"
15- #include " swift/ABI/Actor.h"
16- #include " swift/ABI/Task.h"
17- #include " swift/ABI/Metadata.h"
14+ #include " ../runtime/ThreadLocalStorage.h"
15+ #include " swift/Runtime/Atomic.h"
16+ #include " swift/Runtime/Casting.h"
1817#include " swift/Runtime/Once.h"
1918#include " swift/Runtime/Mutex.h"
2019#include " swift/Runtime/Concurrency.h"
20+ #include " swift/Runtime/ThreadLocal.h"
21+ #include " swift/ABI/TaskLocal.h"
22+ #include " swift/ABI/Task.h"
23+ #include " swift/ABI/Actor.h"
24+ #include " swift/ABI/Metadata.h"
25+ #include " llvm/ADT/PointerIntPair.h"
2126#include " TaskPrivate.h"
2227
2328#if defined(__APPLE__)
2631#include < android/log.h>
2732#endif
2833
34+ #if HAVE_PTHREAD_H
35+ #include < pthread.h>
36+ #endif
37+
2938#if defined(_WIN32)
3039#include < io.h>
40+ #include < handleapi.h>
41+ #include < processthreadsapi.h>
3142#endif
3243
3344using namespace swift ;
3445
3546// =============================================================================
47+
48+ // / An extremely silly class which exists to make pointer
49+ // / default-initialization constexpr.
50+ template <class T > struct Pointer {
51+ T *Value;
52+ constexpr Pointer () : Value(nullptr ) {}
53+ constexpr Pointer (T *value) : Value(value) {}
54+ operator T *() const { return Value; }
55+ T *operator ->() const { return Value; }
56+ };
57+
58+ // / THIS IS RUNTIME INTERNAL AND NOT ABI.
59+ class FallbackTaskLocalStorage {
60+ static SWIFT_RUNTIME_DECLARE_THREAD_LOCAL (
61+ Pointer<TaskLocal::Storage>,
62+ Value);
63+
64+ public:
65+ static void set (TaskLocal::Storage *task) { Value.set (task); }
66+ static TaskLocal::Storage *get () { return Value.get (); }
67+ };
68+
69+ // / Define the thread-locals.
70+ SWIFT_RUNTIME_DECLARE_THREAD_LOCAL (
71+ Pointer<TaskLocal::Storage>,
72+ FallbackTaskLocalStorage::Value);
73+
3674// ==== ABI --------------------------------------------------------------------
3775
3876SWIFT_CC (swift)
39- static void swift_task_localValuePushImpl(AsyncTask *task,
40- const HeapObject *key,
41- /* +1 */ OpaqueValue *value,
42- const Metadata *valueType) {
43- task->localValuePush (key, value, valueType);
77+ static void swift_task_localValuePushImpl(const HeapObject *key,
78+ /* +1 */ OpaqueValue *value,
79+ const Metadata *valueType) {
80+ if (AsyncTask *task = swift_task_getCurrent ()) {
81+ task->localValuePush (key, value, valueType);
82+ return ;
83+ }
84+
85+ // no AsyncTask available so we must check the fallback
86+ TaskLocal::Storage *Local = nullptr ;
87+ if (auto storage = FallbackTaskLocalStorage::get ()) {
88+ Local = storage;
89+ } else {
90+ void *allocation = malloc (sizeof (TaskLocal::Storage));
91+ auto *freshStorage = new (allocation) TaskLocal::Storage ();
92+
93+ FallbackTaskLocalStorage::set (freshStorage);
94+ Local = freshStorage;
95+ }
96+
97+ Local->pushValue (/* task=*/ nullptr , key, value, valueType);
4498}
4599
46100SWIFT_CC (swift)
47- static OpaqueValue* swift_task_localValueGetImpl(AsyncTask *task,
48- const HeapObject *key) {
49- return task->localValueGet (key);
101+ static OpaqueValue* swift_task_localValueGetImpl(const HeapObject *key) {
102+ if (AsyncTask *task = swift_task_getCurrent ()) {
103+ // we're in the context of a task and can use the task's storage
104+ return task->localValueGet (key);
105+ }
106+
107+ // no AsyncTask available so we must check the fallback
108+ if (auto Local = FallbackTaskLocalStorage::get ()) {
109+ return Local->getValue (/* task*/ nullptr , key);
110+ }
111+
112+ // no value found in task-local or fallback thread-local storage.
113+ return nullptr ;
50114}
51115
52116SWIFT_CC (swift)
53- static void swift_task_localValuePopImpl(AsyncTask *task) {
54- task->localValuePop ();
117+ static void swift_task_localValuePopImpl() {
118+ if (AsyncTask *task = swift_task_getCurrent ()) {
119+ task->localValuePop ();
120+ return ;
121+ }
122+
123+ if (TaskLocal::Storage *Local = FallbackTaskLocalStorage::get ()) {
124+ bool hasRemainingBindings = Local->popValue (nullptr );
125+ if (!hasRemainingBindings) {
126+ // We clean up eagerly, it may be that this non-swift-concurrency thread
127+ // never again will use task-locals, and as such we better remove the storage.
128+ FallbackTaskLocalStorage::set (nullptr );
129+ free (Local);
130+ }
131+ return ;
132+ }
133+
134+ assert (false && " Attempted to pop value but no task or thread-local storage available!" );
55135}
56136
57137// =============================================================================
@@ -179,17 +259,14 @@ static void swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroupImpl(
179259
180260TaskLocal::Item*
181261TaskLocal::Item::createLink (AsyncTask *task,
182- const HeapObject *key,
183- const Metadata *valueType) {
184- assert (task);
185-
262+ const HeapObject *key,
263+ const Metadata *valueType) {
186264 size_t amountToAllocate = Item::itemSize (valueType);
187- // assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
188-
189- void *allocation = _swift_task_alloc_specific (task, amountToAllocate);
190- Item *item = new (allocation) Item (key, valueType);
265+ void *allocation = task ? _swift_task_alloc_specific (task, amountToAllocate)
266+ : malloc (amountToAllocate);
267+ Item *item = new (allocation) Item (key, valueType);
191268
192- auto next = task->Local .head ;
269+ auto next = task ? task ->Local .head : FallbackTaskLocalStorage::get ()-> head ;
193270 item->next = reinterpret_cast <uintptr_t >(next) |
194271 static_cast <uintptr_t >(NextLinkType::IsNext);
195272
@@ -205,7 +282,10 @@ void TaskLocal::Item::destroy(AsyncTask *task) {
205282 valueType->vw_destroy (getStoragePtr ());
206283 }
207284
208- _swift_task_dealloc_specific (task, this );
285+ // if task is available, we must have used the task allocator to allocate this item,
286+ // so we must deallocate it using the same. Otherwise, we must have used malloc.
287+ if (task) _swift_task_dealloc_specific (task, this );
288+ else free (this );
209289}
210290
211291void TaskLocal::Storage::destroy (AsyncTask *task) {
@@ -244,11 +324,14 @@ void TaskLocal::Storage::pushValue(AsyncTask *task,
244324 head = item;
245325}
246326
247- void TaskLocal::Storage::popValue (AsyncTask *task) {
327+ bool TaskLocal::Storage::popValue (AsyncTask *task) {
248328 assert (head && " attempted to pop value off empty task-local stack" );
249329 auto old = head;
250330 head = head->getNext ();
251331 old->destroy (task);
332+
333+ // / if pointing at not-null next item, there are remaining bindings.
334+ return head != nullptr ;
252335}
253336
254337OpaqueValue* TaskLocal::Storage::getValue (AsyncTask *task,
0 commit comments