@@ -137,11 +137,12 @@ class ActiveTaskStatus {
137
137
// / ### Fragments
138
138
// / An AsyncTask may have the following fragments:
139
139
// /
140
- // / +------------------+
141
- // / | childFragment? |
142
- // / | groupFragment? |
143
- // / | futureFragment? |*
144
- // / +------------------+
140
+ // / +--------------------------+
141
+ // / | childFragment? |
142
+ // / | taskLocalValuesFragment |
143
+ // / | groupFragment? |
144
+ // / | futureFragment? |*
145
+ // / +--------------------------+
145
146
// /
146
147
// / The future fragment is dynamic in size, based on the future result type
147
148
// / it can hold, and thus must be the *last* fragment.
@@ -175,13 +176,15 @@ class AsyncTask : public HeapObject, public Job {
175
176
void run (ExecutorRef currentExecutor) {
176
177
ResumeTask (this , currentExecutor, ResumeContext);
177
178
}
178
-
179
+
179
180
// / Check whether this task has been cancelled.
180
181
// / Checking this is, of course, inherently race-prone on its own.
181
182
bool isCancelled () const {
182
183
return Status.load (std::memory_order_relaxed).isCancelled ();
183
184
}
184
185
186
+ // ==== Child Fragment -------------------------------------------------------
187
+
185
188
// / A fragment of an async task structure that happens to be a child task.
186
189
class ChildFragment {
187
190
// / The parent task of this task.
@@ -205,14 +208,212 @@ class AsyncTask : public HeapObject, public Job {
205
208
}
206
209
};
207
210
208
- // TODO: rename? all other functions are `is...` rather than `has...Fragment`
209
- bool hasChildFragment () const { return Flags.task_isChildTask (); }
211
+ bool hasChildFragment () const {
212
+ return Flags.task_isChildTask ();
213
+ }
210
214
211
215
ChildFragment *childFragment () {
212
216
assert (hasChildFragment ());
213
217
return reinterpret_cast <ChildFragment*>(this + 1 );
214
218
}
215
219
220
+ // ==== Task Locals Values-- -------------------------------------------------
221
+
222
+ class TaskLocalValuesFragment {
223
+ public:
224
+ // / Type of the pointed at `next` task local item.
225
+ enum class NextLinkType : uintptr_t {
226
+ // / This task is known to be a "terminal" node in the lookup of task locals.
227
+ // / In other words, even if it had a parent, the parent (and its parents)
228
+ // / are known to not contain any any more task locals, and thus any further
229
+ // / search beyond this task.
230
+ IsTerminal = 0b00 ,
231
+ // / The storage pointer points at the next TaskLocalChainItem in this task.
232
+ IsNext = 0b01 ,
233
+ // / The storage pointer points at a parent AsyncTask,
234
+ // / in which we should continue the lookup.
235
+ IsParent = 0b11
236
+ };
237
+
238
+ class TaskLocalItem {
239
+ private:
240
+ // / Mask used for the low status bits in a task local chain item.
241
+ static const uintptr_t statusMask = 0x03 ;
242
+
243
+ // / Pointer to the next task local item; be it in this task or in a parent.
244
+ // / Low bits encode `NextLinkType`.
245
+ // / TaskLocalItem *next = nullptr;
246
+ uintptr_t next;
247
+
248
+ public:
249
+ // / The type of the key with which this value is associated.
250
+ const Metadata *keyType;
251
+ // / The type of the value stored by this item.
252
+ const Metadata *valueType;
253
+
254
+ // Trailing storage for the value itself. The storage will be
255
+ // uninitialized or contain an instance of \c valueType.
256
+
257
+ private:
258
+ explicit TaskLocalItem (const Metadata *keyType, const Metadata *valueType)
259
+ : keyType(keyType),
260
+ valueType(valueType),
261
+ next(0 ) { }
262
+
263
+ public:
264
+ // / TaskLocalItem which does not by itself store any value, but only points
265
+ // / to the nearest task-local-value containing parent's first task item.
266
+ // /
267
+ // / This item type is used to link to the appropriate parent task's item,
268
+ // / when the current task itself does not have any task local values itself.
269
+ // /
270
+ // / When a task actually has its own task locals, it should rather point
271
+ // / to the parent's *first* task-local item in its *last* item, extending
272
+ // / the TaskLocalItem linked list into the appropriate parent.
273
+ static TaskLocalItem* createParentLink (AsyncTask *task, AsyncTask *parent) {
274
+ assert (parent);
275
+ assert (parent->hasTaskLocalValues ());
276
+ assert (task->hasTaskLocalValues ());
277
+ size_t amountToAllocate = TaskLocalItem::itemSize (/* valueType*/ nullptr );
278
+ // assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
279
+ void *allocation = malloc (amountToAllocate); // TODO: use task-local allocator
280
+ fprintf (stderr, " MALLOC parent link item: %d\n " , allocation);
281
+
282
+ TaskLocalItem *item =
283
+ new (allocation) TaskLocalItem (nullptr , nullptr );
284
+
285
+ auto next = parent->localValuesFragment ()->head ;
286
+ auto nextLinkType = next ? NextLinkType::IsParent : NextLinkType::IsTerminal;
287
+ item->next = reinterpret_cast <uintptr_t >(next) |
288
+ static_cast <uintptr_t >(nextLinkType);
289
+
290
+ fprintf (stderr, " error: %s [%s:%d] created parent item: task=%d -> parentTask=%d :: item=%d -> item->getNext()=%d\n " , __FUNCTION__, __FILE_NAME__, __LINE__,
291
+ task, parent, item, item->getNext ());
292
+
293
+ return item;
294
+ }
295
+
296
+ static TaskLocalItem* createLink (AsyncTask *task,
297
+ const Metadata *keyType,
298
+ const Metadata *valueType) {
299
+ assert (task);
300
+ assert (task->hasTaskLocalValues ());
301
+ size_t amountToAllocate = TaskLocalItem::itemSize (valueType);
302
+ // assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
303
+ void *allocation = malloc (amountToAllocate); // TODO: use task-local allocator
304
+ fprintf (stderr, " MALLOC link item: %d\n " , allocation);
305
+ TaskLocalItem *item =
306
+ new (allocation) TaskLocalItem (keyType, valueType);
307
+
308
+ auto next = task->localValuesFragment ()->head ;
309
+ auto nextLinkType = next ? NextLinkType::IsNext : NextLinkType::IsTerminal;
310
+ item->next = reinterpret_cast <uintptr_t >(next) |
311
+ static_cast <uintptr_t >(nextLinkType);
312
+
313
+ return item;
314
+ }
315
+
316
+ void destroy () {
317
+ if (valueType) {
318
+ valueType->vw_destroy (getStoragePtr ());
319
+ }
320
+ }
321
+
322
+ TaskLocalItem *getNext () {
323
+ return reinterpret_cast <TaskLocalItem *>(next & ~statusMask);
324
+ }
325
+
326
+ NextLinkType getNextLinkType () {
327
+ return static_cast <NextLinkType>(next & statusMask);
328
+ }
329
+
330
+ // / Retrieve a pointer to the storage of the value.
331
+ OpaqueValue *getStoragePtr () {
332
+ // assert(valueType && "valueType must be set before accessing storage pointer.");
333
+ return reinterpret_cast <OpaqueValue *>(
334
+ reinterpret_cast <char *>(this ) + storageOffset (valueType));
335
+ }
336
+
337
+ // / Compute the offset of the storage from the base of the item.
338
+ static size_t storageOffset (const Metadata *valueType) {
339
+ size_t offset = sizeof (TaskLocalItem);
340
+ if (valueType) {
341
+ size_t alignment = valueType->vw_alignment ();
342
+ return (offset + alignment - 1 ) & ~(alignment - 1 );
343
+ } else {
344
+ return offset;
345
+ }
346
+ }
347
+
348
+ // / Determine the size of the item given a particular value type.
349
+ static size_t itemSize (const Metadata *valueType) {
350
+ size_t offset = storageOffset (valueType);
351
+ if (valueType) {
352
+ offset += valueType->vw_size ();
353
+ }
354
+ return offset;
355
+ }
356
+ };
357
+
358
+ private:
359
+ // / Single-linked list of task local values.
360
+ // / Once task local values within this task are traversed, the list continues
361
+ // / to the "next parent that contributes task local values," or if no such
362
+ // / parent exists it terminates with null.
363
+ // /
364
+ // / If the TaskLocalValuesFragment was allocated, it is expected that this
365
+ // / value should be NOT null; it either has own values, or at least one
366
+ // / parent that has values. If this task does not have any values, the head
367
+ // / pointer MAY immediately point at this task's parent task which has values.
368
+ // /
369
+ // / NOTE: Check the higher bits to know if this is a self or parent value.
370
+ TaskLocalItem *head = nullptr ;
371
+
372
+ public:
373
+ TaskLocalValuesFragment () {}
374
+
375
+ void destroy ();
376
+
377
+ // / If the parent task has task local values defined, point to in
378
+ // / the task local values chain.
379
+ void initializeLinkParent (AsyncTask* task, AsyncTask* parent);
380
+
381
+ void pushValue (AsyncTask *task, const Metadata *keyType,
382
+ /* +1 */ OpaqueValue *value, const Metadata *valueType);
383
+
384
+ void popValue (AsyncTask *task);
385
+
386
+ OpaqueValue* get (const Metadata *keyType);
387
+ };
388
+
389
+ bool hasTaskLocalValues () const {
390
+ return Flags.task_hasLocalValues ();
391
+ }
392
+
393
+ TaskLocalValuesFragment *localValuesFragment () {
394
+ assert (hasTaskLocalValues ());
395
+
396
+ auto offset = reinterpret_cast <char *>(this );
397
+ offset += sizeof (AsyncTask);
398
+
399
+ if (hasChildFragment ()) {
400
+ offset += sizeof (ChildFragment);
401
+ }
402
+
403
+ return reinterpret_cast <TaskLocalValuesFragment*>(offset);
404
+ }
405
+
406
+ OpaqueValue* localValueGet (const Metadata *keyType) {
407
+ if (hasTaskLocalValues ()) {
408
+ return localValuesFragment ()->get (keyType);
409
+ } else {
410
+ // We are guaranteed to have a task-local fragment even if this task has
411
+ // no bindings, but its parent tasks do. Thus, if no fragment, we can
412
+ // immediately return null.
413
+ return nullptr ;
414
+ }
415
+ }
416
+
216
417
// ==== TaskGroup ------------------------------------------------------------
217
418
218
419
class GroupFragment {
@@ -516,12 +717,18 @@ class AsyncTask : public HeapObject, public Job {
516
717
GroupFragment *groupFragment () {
517
718
assert (isTaskGroup ());
518
719
720
+ auto offset = reinterpret_cast <char *>(this );
721
+ offset += sizeof (AsyncTask);
722
+
519
723
if (hasChildFragment ()) {
520
- return reinterpret_cast <GroupFragment *>(
521
- reinterpret_cast <ChildFragment*>(this + 1 ) + 1 );
724
+ offset += sizeof (ChildFragment);
522
725
}
523
726
524
- return reinterpret_cast <GroupFragment *>(this + 1 );
727
+ if (hasTaskLocalValues ()) {
728
+ offset += sizeof (TaskLocalValuesFragment);
729
+ }
730
+
731
+ return reinterpret_cast <GroupFragment *>(offset);
525
732
}
526
733
527
734
// / Offer result of a task into this channel.
@@ -647,13 +854,17 @@ class AsyncTask : public HeapObject, public Job {
647
854
FutureFragment *futureFragment () {
648
855
assert (isFuture ());
649
856
650
- auto offset = reinterpret_cast <uintptr_t >(this ); // TODO: char* instead?
857
+ auto offset = reinterpret_cast <char * >(this );
651
858
offset += sizeof (AsyncTask);
652
859
653
860
if (hasChildFragment ()) {
654
861
offset += sizeof (ChildFragment);
655
862
}
656
863
864
+ if (hasTaskLocalValues ()) {
865
+ offset += sizeof (TaskLocalValuesFragment);
866
+ }
867
+
657
868
if (isTaskGroup ()) {
658
869
offset += sizeof (GroupFragment);
659
870
}
0 commit comments