File: TaskLocal.h

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (224 lines) | stat: -rw-r--r-- 8,407 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//===--- TaskLocal.h - ABI of task local values -----------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Swift ABI describing task locals.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_ABI_TASKLOCAL_BACKDEPLOY56_H
#define SWIFT_ABI_TASKLOCAL_BACKDEPLOY56_H

#include "swift/ABI/HeapObject.h"
#include "swift/ABI/Metadata.h"
#include "swift/ABI/MetadataValues.h"

namespace swift {
class AsyncTask;
struct OpaqueValue;
struct SwiftError;
class TaskStatusRecord;
class TaskGroup;

// ==== Task Locals Values ---------------------------------------------------

class TaskLocal {
public:
  /// Type of the pointed at `next` task local item.
  enum class NextLinkType : uintptr_t {
    /// The storage pointer points at the next TaskLocal::Item in this task.
    IsNext = 0b00,
    /// The storage pointer points at a item stored by another AsyncTask.
    ///
    /// Note that this may not necessarily be the same as the task's parent
    /// task -- we may point to a super-parent if we know / that the parent
    /// does not "contribute" any task local values. This is to speed up
    /// lookups by skipping empty parent tasks during get(), and explained
    /// in depth in `createParentLink`.
    IsParent = 0b01,
  };

  class Item {
  private:
    /// Mask used for the low status bits in a task local chain item.
    static const uintptr_t statusMask = 0x03;

    /// Pointer to one of the following:
    /// - next task local item as OpaqueValue* if it is task-local allocated
    /// - next task local item as HeapObject* if it is heap allocated "heavy"
    /// - the parent task's TaskLocal::Storage
    ///
    /// Low bits encode `NextLinkType`, based on which the type of the pointer
    /// is determined.
    uintptr_t next;

  public:
    /// The type of the key with which this value is associated.
    const HeapObject *key;
    /// The type of the value stored by this item.
    const Metadata *valueType;

    // Trailing storage for the value itself. The storage will be
    // uninitialized or contain an instance of \c valueType.

    /// Returns true if this item is a 'parent pointer'.
    ///
    /// A parent pointer is special kind of `Item` is created when pointing at
    /// the parent storage, forming a chain of task local items spanning multiple
    /// tasks.
    bool isParentPointer() const {
      return !valueType;
    }

  protected:
    explicit Item()
      : next(0),
        key(nullptr),
        valueType(nullptr) {}

    explicit Item(const HeapObject *key, const Metadata *valueType)
      : next(0),
        key(key),
        valueType(valueType) {}

  public:
    /// Item which does not by itself store any value, but only points
    /// to the nearest task-local-value containing parent's first task item.
    ///
    /// This item type is used to link to the appropriate parent task's item,
    /// when the current task itself does not have any task local values itself.
    ///
    /// When a task actually has its own task locals, it should rather point
    /// to the parent's *first* task-local item in its *last* item, extending
    /// the Item linked list into the appropriate parent.
    static Item *createParentLink(AsyncTask *task, AsyncTask *parent);

    static Item *createLink(AsyncTask *task,
                            const HeapObject *key,
                            const Metadata *valueType);

    void destroy(AsyncTask *task);

    Item *getNext() {
      return reinterpret_cast<Item *>(next & ~statusMask);
    }

    NextLinkType getNextLinkType() const {
      return static_cast<NextLinkType>(next & statusMask);
    }

    /// Item does not contain any actual value, and is only used to point at
    /// a specific parent item.
    bool isEmpty() const {
      return !valueType;
    }

    /// Retrieve a pointer to the storage of the value.
    OpaqueValue *getStoragePtr() {
      return reinterpret_cast<OpaqueValue *>(
        reinterpret_cast<char *>(this) + storageOffset(valueType));
    }

    void copyTo(AsyncTask *task);

    /// Compute the offset of the storage from the base of the item.
    static size_t storageOffset(const Metadata *valueType) {
      size_t offset = sizeof(Item);

      if (valueType) {
        size_t alignment = valueType->vw_alignment();
        return (offset + alignment - 1) & ~(alignment - 1);
      } else {
        return offset;
      }
    }

    /// Determine the size of the item given a particular value type.
    static size_t itemSize(const Metadata *valueType) {
      size_t offset = storageOffset(valueType);
      if (valueType) {
        offset += valueType->vw_size();
      }
      return offset;
    }
  };

  class Storage {
    friend class TaskLocal::Item;
  private:
    /// A stack (single-linked list) of task local values.
    ///
    /// Once task local values within this task are traversed, the list continues
    /// to the "next parent that contributes task local values," or if no such
    /// parent exists it terminates with null.
    ///
    /// If the TaskLocalValuesFragment was allocated, it is expected that this
    /// value should be NOT null; it either has own values, or at least one
    /// parent that has values. If this task does not have any values, the head
    /// pointer MAY immediately point at this task's parent task which has values.
    ///
    /// ### Concurrency
    /// Access to the head is only performed from the task itself, when it
    /// creates child tasks, the child during creation will inspect its parent's
    /// task local value stack head, and point to it. This is done on the calling
    /// task, and thus needs not to be synchronized. Subsequent traversal is
    /// performed by child tasks concurrently, however they use their own
    /// pointers/stack and can never mutate the parent's stack.
    ///
    /// The stack is only pushed/popped by the owning task, at the beginning and
    /// end a `body` block of `withLocal(_:boundTo:body:)` respectively.
    ///
    /// Correctness of the stack strongly relies on the guarantee that tasks
    /// never outline a scope in which they are created. Thanks to this, if
    /// tasks are created inside the `body` of `withLocal(_:,boundTo:body:)`
    /// all tasks created inside the `withLocal` body must complete before it
    /// returns, as such, any child tasks potentially accessing the value stack
    /// are guaranteed to be completed by the time we pop values off the stack
    /// (after the body has completed).
    TaskLocal::Item *head = nullptr;

  public:

    void initializeLinkParent(AsyncTask *task, AsyncTask *parent);

    void pushValue(AsyncTask *task,
                   const HeapObject *key,
                   /* +1 */ OpaqueValue *value, const Metadata *valueType);

    OpaqueValue* getValue(AsyncTask *task, const HeapObject *key);

    /// Returns `true` of more bindings remain in this storage,
    /// and `false` if the just popped value was the last one and the storage
    /// can be safely disposed of.
    bool popValue(AsyncTask *task);

    /// Copy all task-local bindings to the target task.
    ///
    /// The new bindings allocate their own items and can out-live the current task.
    ///
    /// ### Optimizations
    /// Only the most recent binding of a value is copied over, i.e. given
    /// a key bound to `A` and then `B`, only the `B` binding will be copied.
    /// This is safe and correct because the new task would never have a chance
    /// to observe the `A` value, because it semantically will never observe a
    /// "pop" of the `B` value - it was spawned from a scope where only B was observable.
    void copyTo(AsyncTask *target);

    /// Destroy and deallocate all items stored by this specific task.
    ///
    /// Items owned by a parent task are left untouched, since we do not own them.
    void destroy(AsyncTask *task);
  };
};

} // end namespace swift

#endif // SWIFT_ABI_TASKLOCAL_BACKDEPLOY56_H