17
17
#ifndef SWIFT_ABI_TASK_H
18
18
#define SWIFT_ABI_TASK_H
19
19
20
+ #include " swift/ABI/TaskLocal.h"
20
21
#include " swift/ABI/Executor.h"
21
22
#include " swift/ABI/HeapObject.h"
22
23
#include " swift/ABI/Metadata.h"
23
24
#include " swift/ABI/MetadataValues.h"
24
25
#include " swift/Runtime/Config.h"
25
26
#include " swift/Basic/STLExtras.h"
26
27
#include " bitset"
27
- #include " string"
28
- #include " queue"
28
+ #include " queue" // TODO: remove and replace with our own mpsc
29
29
30
30
namespace swift {
31
31
class AsyncTask ;
@@ -147,7 +147,6 @@ class ActiveTaskStatus {
147
147
// / An AsyncTask may have the following fragments:
148
148
// /
149
149
// / +--------------------------+
150
- // / | taskLocalValuesFragment |
151
150
// / | childFragment? |
152
151
// / | groupChildFragment? |
153
152
// / | futureFragment? |*
@@ -173,12 +172,16 @@ class AsyncTask : public HeapObject, public Job {
173
172
// / Reserved for the use of the task-local stack allocator.
174
173
void *AllocatorPrivate[4 ];
175
174
175
+ // / Task local values storage container.
176
+ TaskLocal::Storage Local;
177
+
176
178
AsyncTask (const HeapMetadata *metadata, JobFlags flags,
177
179
TaskContinuationFunction *run,
178
180
AsyncContext *initialContext)
179
181
: HeapObject(metadata), Job(flags, run),
180
182
ResumeContext (initialContext),
181
- Status(ActiveTaskStatus()) {
183
+ Status(ActiveTaskStatus()),
184
+ Local(TaskLocal::Storage()) {
182
185
assert (flags.isAsyncTask ());
183
186
}
184
187
@@ -196,237 +199,20 @@ class AsyncTask : public HeapObject, public Job {
196
199
return Status.load (std::memory_order_relaxed).isCancelled ();
197
200
}
198
201
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 ----------------------------------------------------
361
203
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);
425
207
}
426
208
427
209
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 );
430
216
}
431
217
432
218
// ==== Child Fragment -------------------------------------------------------
@@ -476,7 +262,6 @@ class AsyncTask : public HeapObject, public Job {
476
262
477
263
auto offset = reinterpret_cast <char *>(this );
478
264
offset += sizeof (AsyncTask);
479
- offset += sizeof (TaskLocalValuesFragment);
480
265
481
266
return reinterpret_cast <ChildFragment*>(offset);
482
267
}
@@ -516,7 +301,6 @@ class AsyncTask : public HeapObject, public Job {
516
301
517
302
auto offset = reinterpret_cast <char *>(this );
518
303
offset += sizeof (AsyncTask);
519
- offset += sizeof (TaskLocalValuesFragment);
520
304
if (hasChildFragment ())
521
305
offset += sizeof (ChildFragment);
522
306
@@ -625,7 +409,6 @@ class AsyncTask : public HeapObject, public Job {
625
409
assert (isFuture ());
626
410
auto offset = reinterpret_cast <char *>(this );
627
411
offset += sizeof (AsyncTask);
628
- offset += sizeof (TaskLocalValuesFragment);
629
412
if (hasChildFragment ())
630
413
offset += sizeof (ChildFragment);
631
414
if (hasGroupChildFragment ())
@@ -665,7 +448,7 @@ class AsyncTask : public HeapObject, public Job {
665
448
};
666
449
667
450
// The compiler will eventually assume these.
668
- static_assert (sizeof (AsyncTask) == 12 * sizeof(void *),
451
+ static_assert (sizeof (AsyncTask) == 14 * sizeof(void *),
669
452
"AsyncTask size is wrong");
670
453
static_assert (alignof (AsyncTask) == 2 * alignof(void *),
671
454
"AsyncTask alignment is wrong");
0 commit comments