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
|