@@ -44,6 +44,14 @@ class TaskLocal {
4444 // / lookups by skipping empty parent tasks during get(), and explained
4545 // / in depth in `createParentLink`.
4646 IsParent = 0b01 ,
47+ // / The task local binding was created inside the body of a `withTaskGroup`,
48+ // / and therefore must either copy it, or crash when a child task is created
49+ // / using 'group.addTask' and it would refer to this task local.
50+ // /
51+ // / Items of this kind must be copied by a group child task for access
52+ // / safety reasons, as otherwise the pop would happen before the child task
53+ // / has completed.
54+ IsNextCreatedInTaskGroupBody = 0b10 ,
4755 };
4856
4957 class Item {
@@ -101,20 +109,55 @@ class TaskLocal {
101109 // / the Item linked list into the appropriate parent.
102110 static Item *createParentLink (AsyncTask *task, AsyncTask *parent);
103111
112+ static Item *createLink (AsyncTask *task,
113+ const HeapObject *key,
114+ const Metadata *valueType,
115+ bool inTaskGroupBody);
116+
104117 static Item *createLink (AsyncTask *task,
105118 const HeapObject *key,
106119 const Metadata *valueType);
107120
121+ static Item *createLinkInTaskGroup (
122+ AsyncTask *task,
123+ const HeapObject *key,
124+ const Metadata *valueType);
125+
108126 void destroy (AsyncTask *task);
109127
110128 Item *getNext () {
111129 return reinterpret_cast <Item *>(next & ~statusMask);
112130 }
113131
132+ void relinkTaskGroupLocalHeadToSafeNext (Item* nextOverride) {
133+ assert (!getNext () &&
134+ " Can only relink task local item that was not pointing at anything yet" );
135+ assert ((nextOverride->isNextLinkPointer () ||
136+ nextOverride->isParentPointer ()) &&
137+ " Currently relinking is only done within a task group to "
138+ " avoid within-taskgroup next pointers; attempted to point at "
139+ " task local declared within task group body though!" );
140+
141+ next = reinterpret_cast <uintptr_t >(nextOverride) |
142+ static_cast <uintptr_t >((nextOverride->isNextLinkPointer ()
143+ ? NextLinkType::IsNextCreatedInTaskGroupBody
144+ : NextLinkType::IsParent));
145+ }
146+
114147 NextLinkType getNextLinkType () const {
115148 return static_cast <NextLinkType>(next & statusMask);
116149 }
117150
151+ bool isNextLinkPointer () const {
152+ return static_cast <NextLinkType>(next & statusMask) ==
153+ NextLinkType::IsNext;
154+ }
155+
156+ bool isNextLinkPointerCreatedInTaskGroupBody () const {
157+ return static_cast <NextLinkType>(next & statusMask) ==
158+ NextLinkType::IsNextCreatedInTaskGroupBody;
159+ }
160+
118161 // / Item does not contain any actual value, and is only used to point at
119162 // / a specific parent item.
120163 bool isEmpty () const {
@@ -127,7 +170,7 @@ class TaskLocal {
127170 reinterpret_cast <char *>(this ) + storageOffset (valueType));
128171 }
129172
130- void copyTo (AsyncTask *task);
173+ TaskLocal::Item* copyTo (AsyncTask *task);
131174
132175 // / Compute the offset of the storage from the base of the item.
133176 static size_t storageOffset (const Metadata *valueType) {
@@ -136,9 +179,9 @@ class TaskLocal {
136179 if (valueType) {
137180 size_t alignment = valueType->vw_alignment ();
138181 return (offset + alignment - 1 ) & ~(alignment - 1 );
139- } else {
140- return offset;
141182 }
183+
184+ return offset;
142185 }
143186
144187 // / Determine the size of the item given a particular value type.
@@ -187,6 +230,10 @@ class TaskLocal {
187230
188231 public:
189232
233+ // / Get the "current" task local storage from either the passed in
234+ // / task, or fall back to the *thread* local stored storage.
235+ static Storage* getCurrent (AsyncTask *task);
236+
190237 void initializeLinkParent (AsyncTask *task, AsyncTask *parent);
191238
192239 void pushValue (AsyncTask *task,
@@ -200,6 +247,9 @@ class TaskLocal {
200247 // / can be safely disposed of.
201248 bool popValue (AsyncTask *task);
202249
250+ // / Peek at the head item and get its type.
251+ std::optional<NextLinkType> peekHeadLinkType () const ;
252+
203253 // / Copy all task-local bindings to the target task.
204254 // /
205255 // / The new bindings allocate their own items and can out-live the current task.
@@ -212,6 +262,10 @@ class TaskLocal {
212262 // / "pop" of the `B` value - it was spawned from a scope where only B was observable.
213263 void copyTo (AsyncTask *target);
214264
265+ // FIXME(concurrency): We currently copy from "all" task groups we encounter
266+ // however in practice we only
267+ void copyToOnlyOnlyFromCurrentGroup (AsyncTask *target);
268+
215269 // / Destroy and deallocate all items stored by this specific task.
216270 // /
217271 // / Items owned by a parent task are left untouched, since we do not own them.
0 commit comments