1717#ifndef SWIFT_ABI_TASK_H
1818#define SWIFT_ABI_TASK_H
1919
20+ #include " swift/ABI/TaskLocal.h"
2021#include " swift/ABI/Executor.h"
2122#include " swift/ABI/HeapObject.h"
2223#include " swift/ABI/Metadata.h"
2324#include " swift/ABI/MetadataValues.h"
2425#include " swift/Runtime/Config.h"
2526#include " swift/Basic/STLExtras.h"
2627#include " bitset"
27- #include " string"
28- #include " queue"
28+ #include " queue" // TODO: remove and replace with our own mpsc
2929
3030namespace swift {
3131class AsyncTask ;
@@ -147,7 +147,6 @@ class ActiveTaskStatus {
147147// / An AsyncTask may have the following fragments:
148148// /
149149// / +--------------------------+
150- // / | taskLocalValuesFragment |
151150// / | childFragment? |
152151// / | groupChildFragment? |
153152// / | futureFragment? |*
@@ -173,12 +172,16 @@ class AsyncTask : public HeapObject, public Job {
173172 // / Reserved for the use of the task-local stack allocator.
174173 void *AllocatorPrivate[4 ];
175174
175+ // / Task local values storage container.
176+ TaskLocal::Storage Local;
177+
176178 AsyncTask (const HeapMetadata *metadata, JobFlags flags,
177179 TaskContinuationFunction *run,
178180 AsyncContext *initialContext)
179181 : HeapObject(metadata), Job(flags, run),
180182 ResumeContext (initialContext),
181- Status(ActiveTaskStatus()) {
183+ Status(ActiveTaskStatus()),
184+ Local(TaskLocal::Storage()) {
182185 assert (flags.isAsyncTask ());
183186 }
184187
@@ -196,237 +199,20 @@ class AsyncTask : public HeapObject, public Job {
196199 return Status.load (std::memory_order_relaxed).isCancelled ();
197200 }
198201
199- // ==== Task Locals Values ---------------------------------------------------
200-
201- // / Storage fragment for task local values.
202- class TaskLocalValuesFragment {
203- public:
204- // / Type of the pointed at `next` task local item.
205- enum class NextLinkType : uintptr_t {
206- // / This task is known to be a "terminal" node in the lookup of task locals.
207- // / In other words, even if it had a parent, the parent (and its parents)
208- // / are known to not contain any any more task locals, and thus any further
209- // / search beyond this task.
210- IsTerminal = 0b00 ,
211- // / The storage pointer points at the next TaskLocalChainItem in this task.
212- IsNext = 0b01 ,
213- // / The storage pointer points at a parent AsyncTask, in which we should
214- // / continue the lookup.
215- // /
216- // / Note that this may not necessarily be the same as the task's parent
217- // / task -- we may point to a super-parent if we know / that the parent
218- // / does not "contribute" any task local values. This is to speed up
219- // / lookups by skipping empty parent tasks during get(), and explained
220- // / in depth in `createParentLink`.
221- IsParent = 0b11
222- };
223-
224- // / Values must match `TaskLocalInheritance` declared in `TaskLocal.swift`.
225- enum class TaskLocalInheritance : uint8_t {
226- Default = 0 ,
227- Never = 1
228- };
229-
230- class TaskLocalItem {
231- private:
232- // / Mask used for the low status bits in a task local chain item.
233- static const uintptr_t statusMask = 0x03 ;
234-
235- // / Pointer to the next task local item; be it in this task or in a parent.
236- // / Low bits encode `NextLinkType`.
237- // / TaskLocalItem *next = nullptr;
238- uintptr_t next;
239-
240- public:
241- // / The type of the key with which this value is associated.
242- const Metadata *keyType;
243- // / The type of the value stored by this item.
244- const Metadata *valueType;
245-
246- // Trailing storage for the value itself. The storage will be
247- // uninitialized or contain an instance of \c valueType.
248-
249- private:
250- explicit TaskLocalItem (const Metadata *keyType, const Metadata *valueType)
251- : next(0 ),
252- keyType(keyType),
253- valueType(valueType) { }
254-
255- public:
256- // / TaskLocalItem which does not by itself store any value, but only points
257- // / to the nearest task-local-value containing parent's first task item.
258- // /
259- // / This item type is used to link to the appropriate parent task's item,
260- // / when the current task itself does not have any task local values itself.
261- // /
262- // / When a task actually has its own task locals, it should rather point
263- // / to the parent's *first* task-local item in its *last* item, extending
264- // / the TaskLocalItem linked list into the appropriate parent.
265- static TaskLocalItem* createParentLink (AsyncTask *task, AsyncTask *parent) {
266- assert (parent);
267- size_t amountToAllocate = TaskLocalItem::itemSize (/* valueType*/ nullptr );
268- // assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
269- void *allocation = malloc (amountToAllocate); // TODO: use task-local allocator
270-
271- TaskLocalItem *item =
272- new (allocation) TaskLocalItem (nullptr , nullptr );
273-
274- auto parentHead = parent->localValuesFragment ()->head ;
275- if (parentHead) {
276- if (parentHead->isEmpty ()) {
277- switch (parentHead->getNextLinkType ()) {
278- case NextLinkType::IsParent:
279- // it has no values, and just points to its parent,
280- // therefore skip also skip pointing to that parent and point
281- // to whichever parent it was pointing to as well, it may be its
282- // immediate parent, or some super-parent.
283- item->next = reinterpret_cast <uintptr_t >(parentHead->getNext ()) |
284- static_cast <uintptr_t >(NextLinkType::IsParent);
285- break ;
286- case NextLinkType::IsNext:
287- assert (false && " empty taskValue head in parent task, yet parent's 'head' is `IsNext`, "
288- " this should not happen, as it implies the parent must have stored some value." );
289- break ;
290- case NextLinkType::IsTerminal:
291- item->next = reinterpret_cast <uintptr_t >(parentHead->getNext ()) |
292- static_cast <uintptr_t >(NextLinkType::IsTerminal);
293- break ;
294- }
295- } else {
296- item->next = reinterpret_cast <uintptr_t >(parentHead) |
297- static_cast <uintptr_t >(NextLinkType::IsParent);
298- }
299- } else {
300- item->next = reinterpret_cast <uintptr_t >(parentHead) |
301- static_cast <uintptr_t >(NextLinkType::IsTerminal);
302- }
303-
304- return item;
305- }
306-
307- static TaskLocalItem* createLink (AsyncTask *task,
308- const Metadata *keyType,
309- const Metadata *valueType) {
310- assert (task);
311- size_t amountToAllocate = TaskLocalItem::itemSize (valueType);
312- // assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
313- void *allocation = malloc (amountToAllocate); // TODO: use task-local allocator rdar://74218679
314- TaskLocalItem *item =
315- new (allocation) TaskLocalItem (keyType, valueType);
316-
317- auto next = task->localValuesFragment ()->head ;
318- auto nextLinkType = next ? NextLinkType::IsNext : NextLinkType::IsTerminal;
319- item->next = reinterpret_cast <uintptr_t >(next) |
320- static_cast <uintptr_t >(nextLinkType);
321-
322- return item;
323- }
324-
325- void destroy () {
326- if (valueType) {
327- valueType->vw_destroy (getStoragePtr ());
328- }
329- }
330-
331- TaskLocalItem *getNext () {
332- return reinterpret_cast <TaskLocalItem *>(next & ~statusMask);
333- }
334-
335- NextLinkType getNextLinkType () {
336- return static_cast <NextLinkType>(next & statusMask);
337- }
338-
339- // / Item does not contain any actual value, and is only used to point at
340- // / a specific parent item.
341- bool isEmpty () {
342- return !valueType;
343- }
344-
345- // / Retrieve a pointer to the storage of the value.
346- OpaqueValue *getStoragePtr () {
347- return reinterpret_cast <OpaqueValue *>(
348- reinterpret_cast <char *>(this ) + storageOffset (valueType));
349- }
350-
351- // / Compute the offset of the storage from the base of the item.
352- static size_t storageOffset (const Metadata *valueType) {
353- size_t offset = sizeof (TaskLocalItem);
354- if (valueType) {
355- size_t alignment = valueType->vw_alignment ();
356- return (offset + alignment - 1 ) & ~(alignment - 1 );
357- } else {
358- return offset;
359- }
360- }
202+ // ==== Task Local Values ----------------------------------------------------
361203
362- // / Determine the size of the item given a particular value type.
363- static size_t itemSize (const Metadata *valueType) {
364- size_t offset = storageOffset (valueType);
365- if (valueType) {
366- offset += valueType->vw_size ();
367- }
368- return offset;
369- }
370- };
371-
372- private:
373- // / A stack (single-linked list) of task local values.
374- // /
375- // / Once task local values within this task are traversed, the list continues
376- // / to the "next parent that contributes task local values," or if no such
377- // / parent exists it terminates with null.
378- // /
379- // / If the TaskLocalValuesFragment was allocated, it is expected that this
380- // / value should be NOT null; it either has own values, or at least one
381- // / parent that has values. If this task does not have any values, the head
382- // / pointer MAY immediately point at this task's parent task which has values.
383- // /
384- // / ### Concurrency
385- // / Access to the head is only performed from the task itself, when it
386- // / creates child tasks, the child during creation will inspect its parent's
387- // / task local value stack head, and point to it. This is done on the calling
388- // / task, and thus needs not to be synchronized. Subsequent traversal is
389- // / performed by child tasks concurrently, however they use their own
390- // / pointers/stack and can never mutate the parent's stack.
391- // /
392- // / The stack is only pushed/popped by the owning task, at the beginning and
393- // / end a `body` block of `withLocal(_:boundTo:body:)` respectively.
394- // /
395- // / Correctness of the stack strongly relies on the guarantee that tasks
396- // / never outline a scope in which they are created. Thanks to this, if
397- // / tasks are created inside the `body` of `withLocal(_:,boundTo:body:)`
398- // / all tasks created inside the `withLocal` body must complete before it
399- // / returns, as such, any child tasks potentially accessing the value stack
400- // / are guaranteed to be completed by the time we pop values off the stack
401- // / (after the body has completed).
402- TaskLocalItem *head = nullptr ;
403-
404- public:
405- TaskLocalValuesFragment () {}
406-
407- void destroy ();
408-
409- // / If the parent task has task local values defined, point to in
410- // / the task local values chain.
411- void initializeLinkParent (AsyncTask* task, AsyncTask* parent);
412-
413- void pushValue (AsyncTask *task, const Metadata *keyType,
414- /* +1 */ OpaqueValue *value, const Metadata *valueType);
415-
416- void popValue (AsyncTask *task);
417-
418- OpaqueValue* get (const Metadata *keType, TaskLocalInheritance inheritance);
419- };
420-
421- TaskLocalValuesFragment *localValuesFragment () {
422- auto offset = reinterpret_cast <char *>(this );
423- offset += sizeof (AsyncTask);
424- return reinterpret_cast <TaskLocalValuesFragment*>(offset);
204+ void localValuePush (const Metadata *keyType,
205+ /* +1 */ OpaqueValue *value, const Metadata *valueType) {
206+ Local.pushValue (this , keyType, value, valueType);
425207 }
426208
427209 OpaqueValue* localValueGet (const Metadata *keyType,
428- TaskLocalValuesFragment::TaskLocalInheritance inheritance) {
429- return localValuesFragment ()->get (keyType, inheritance);
210+ TaskLocal::TaskLocalInheritance inherit) {
211+ return Local.getValue (this , keyType, inherit);
212+ }
213+
214+ void localValuePop () {
215+ Local.popValue (this );
430216 }
431217
432218 // ==== Child Fragment -------------------------------------------------------
@@ -476,7 +262,6 @@ class AsyncTask : public HeapObject, public Job {
476262
477263 auto offset = reinterpret_cast <char *>(this );
478264 offset += sizeof (AsyncTask);
479- offset += sizeof (TaskLocalValuesFragment);
480265
481266 return reinterpret_cast <ChildFragment*>(offset);
482267 }
@@ -516,7 +301,6 @@ class AsyncTask : public HeapObject, public Job {
516301
517302 auto offset = reinterpret_cast <char *>(this );
518303 offset += sizeof (AsyncTask);
519- offset += sizeof (TaskLocalValuesFragment);
520304 if (hasChildFragment ())
521305 offset += sizeof (ChildFragment);
522306
@@ -625,7 +409,6 @@ class AsyncTask : public HeapObject, public Job {
625409 assert (isFuture ());
626410 auto offset = reinterpret_cast <char *>(this );
627411 offset += sizeof (AsyncTask);
628- offset += sizeof (TaskLocalValuesFragment);
629412 if (hasChildFragment ())
630413 offset += sizeof (ChildFragment);
631414 if (hasGroupChildFragment ())
@@ -665,7 +448,7 @@ class AsyncTask : public HeapObject, public Job {
665448};
666449
667450// The compiler will eventually assume these.
668- static_assert (sizeof (AsyncTask) == 12 * sizeof (void *),
451+ static_assert (sizeof (AsyncTask) == 14 * sizeof (void *),
669452 " AsyncTask size is wrong" );
670453static_assert (alignof (AsyncTask) == 2 * alignof (void *),
671454 "AsyncTask alignment is wrong");
0 commit comments