Skip to content

Commit 8970317

Browse files
Implemented stop nodes in TaskLocal values and refactored encoding of item types
1 parent 7ea9dbd commit 8970317

File tree

7 files changed

+306
-168
lines changed

7 files changed

+306
-168
lines changed

include/swift/ABI/Task.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,14 +344,15 @@ class AsyncTask : public Job {
344344

345345
// ==== Task Local Values ----------------------------------------------------
346346

347-
void localValuePush(const HeapObject *key,
347+
void localPushValue(const HeapObject *key,
348348
/* +1 */ OpaqueValue *value,
349349
const Metadata *valueType);
350350

351-
OpaqueValue *localValueGet(const HeapObject *key);
351+
void localPushStop();
352352

353-
/// Returns true if storage has still more bindings.
354-
bool localValuePop();
353+
OpaqueValue *localGetValue(const HeapObject *key);
354+
355+
void localPop();
355356

356357
// ==== Child Fragment -------------------------------------------------------
357358

include/swift/ABI/TaskLocal.h

Lines changed: 92 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -34,62 +34,69 @@ class TaskLocal {
3434
public:
3535
class Storage;
3636

37-
/// Type of the pointed at `next` task local item.
38-
enum class NextLinkType : uintptr_t {
39-
/// The storage pointer points at the next TaskLocal::Item in this task.
40-
IsNext = 0b00,
41-
/// The storage pointer points at a item stored by another AsyncTask.
37+
/// Type of item in the task local item linked list.
38+
enum class ItemKind : intptr_t {
39+
/// Regular value item.
40+
/// Has @c valueType and @c key .
41+
/// Value is stored in the trailing storage.
42+
/// @c next pointer points to another item owned by the same task as current item.
43+
Value = -1,
44+
45+
/// Item that marks end of sequence of items owned by the current task.
46+
/// @c next pointer points to an item owned by another AsyncTask.
4247
///
4348
/// Note that this may not necessarily be the same as the task's parent
4449
/// task -- we may point to a super-parent if we know / that the parent
4550
/// does not "contribute" any task local values. This is to speed up
46-
/// lookups by skipping empty parent tasks during get(), and explained
47-
/// in depth in `createParentLink`.
48-
IsParent = 0b01,
51+
/// lookups by skipping empty parent tasks during @c get() , and explained
52+
/// in depth in @c createParentLink() .
53+
ParentLink = 0,
54+
55+
/// Stop-item that blocks further lookup.
56+
/// Inserting stop-node allows to temporary disable all inserted task-local values in O(1),
57+
/// while maintaining immutable linked list nature of the task-local values implementation.
58+
Stop = 1,
4959
};
5060

5161
class Item {
5262
private:
53-
/// Mask used for the low status bits in a task local chain item.
54-
static const uintptr_t statusMask = 0x03;
55-
56-
/// Pointer to one of the following:
57-
/// - next task local item as OpaqueValue* if it is task-local allocated
58-
/// - next task local item as HeapObject* if it is heap allocated "heavy"
59-
/// - the parent task's TaskLocal::Storage
60-
///
61-
/// Low bits encode `NextLinkType`, based on which the type of the pointer
62-
/// is determined.
63-
uintptr_t next;
64-
65-
public:
66-
/// The type of the key with which this value is associated.
67-
const HeapObject *key;
63+
/// Pointer to the next item in the chain.
64+
Item * const next;
65+
66+
union KeyOrKind {
67+
/// The type of the key with which this value is associated.
68+
/// Set if valueType is not null
69+
const HeapObject *key;
70+
71+
/// Kind of the node
72+
/// Set if valueType is null
73+
ItemKind kind;
74+
75+
KeyOrKind(const HeapObject *key) : key(key) {}
76+
KeyOrKind(ItemKind kind) : kind(kind) {}
77+
} const keyOrKind;
78+
6879
/// The type of the value stored by this item.
69-
const Metadata *valueType;
70-
71-
// Trailing storage for the value itself. The storage will be
72-
// uninitialized or contain an instance of \c valueType.
73-
74-
/// Returns true if this item is a 'parent pointer'.
75-
///
76-
/// A parent pointer is special kind of `Item` is created when pointing at
77-
/// the parent storage, forming a chain of task local items spanning multiple
78-
/// tasks.
79-
bool isParentPointer() const {
80-
return !valueType;
81-
}
80+
const Metadata * const valueType;
8281

82+
// Trailing storage for an instance of \c valueType if kind is ItemKind::Value
83+
8384
protected:
84-
explicit Item()
85-
: next(0),
86-
key(nullptr),
87-
valueType(nullptr) {}
88-
89-
explicit Item(const HeapObject *key, const Metadata *valueType)
90-
: next(0),
91-
key(key),
92-
valueType(valueType) {}
85+
explicit Item(Item *next, ItemKind kind)
86+
: next(next),
87+
keyOrKind(kind),
88+
valueType(nullptr)
89+
{}
90+
91+
explicit Item(Item *next, const HeapObject *key, const Metadata *valueType)
92+
: next(next),
93+
keyOrKind(key),
94+
valueType(valueType)
95+
{
96+
assert(valueType != nullptr);
97+
}
98+
99+
static void *allocate(size_t amountToAllocate, AsyncTask *task);
93100

94101
public:
95102
/// Item which does not by itself store any value, but only points
@@ -103,29 +110,42 @@ class TaskLocal {
103110
/// the Item linked list into the appropriate parent.
104111
static Item *createParentLink(AsyncTask *task, AsyncTask *parent);
105112

106-
static Item *createLink(Item *next, AsyncTask *task, const HeapObject *key,
113+
static Item *createValue(Item *next, AsyncTask *task, const HeapObject *key,
107114
const Metadata *valueType);
115+
116+
static Item *createStop(Item *next, AsyncTask *task);
108117

109118
/// Destroys value and frees memory using specified task for deallocation.
110119
/// If task is null, then th
111120
void destroy(AsyncTask *task);
112121

113122
Item *getNext() {
114-
return reinterpret_cast<Item *>(next & ~statusMask);
123+
return next;
115124
}
116-
117-
NextLinkType getNextLinkType() const {
118-
return static_cast<NextLinkType>(next & statusMask);
125+
126+
/// Returns kind of this item.
127+
ItemKind getKind() const {
128+
return valueType ? ItemKind::Value : keyOrKind.kind;
119129
}
120130

121-
/// Item does not contain any actual value, and is only used to point at
122-
/// a specific parent item.
123-
bool isEmpty() const {
124-
return !valueType;
131+
/// Returns key of the value item.
132+
/// Available only if @c getKind() is @c ItemKind::Value .
133+
const HeapObject *getKey() const {
134+
assert(getKind() == ItemKind::Value);
135+
return keyOrKind.key;
136+
}
137+
138+
/// Returns value type of the value item.
139+
/// Available only if @c getKind() is @c ItemKind::Value .
140+
const Metadata *getValueType() const {
141+
assert(getKind() == ItemKind::Value);
142+
return valueType;
125143
}
126144

127145
/// Retrieve a pointer to the storage of the value.
146+
/// Available only if @c getKind() is @c ItemKind::Value .
128147
OpaqueValue *getStoragePtr() {
148+
assert(getKind() == ItemKind::Value);
129149
return reinterpret_cast<OpaqueValue *>(
130150
reinterpret_cast<char *>(this) + storageOffset(valueType));
131151
}
@@ -179,10 +199,10 @@ class TaskLocal {
179199
/// The stack is only pushed/popped by the owning task, at the beginning and
180200
/// end a `body` block of `withLocal(_:boundTo:body:)` respectively.
181201
///
182-
/// Correctness of the stack strongly relies on the guarantee that tasks
183-
/// never outline a scope in which they are created. Thanks to this, if
184-
/// tasks are created inside the `body` of `withLocal(_:,boundTo:body:)`
185-
/// all tasks created inside the `withLocal` body must complete before it
202+
/// Correctness of the stack strongly relies on the guarantee that child tasks
203+
/// never outlive a scope in which they are created. Thanks to this, if
204+
/// child tasks are created inside the `body` of `withLocal(_:,boundTo:body:)`
205+
/// all child tasks created inside the `withLocal` body must complete before it
186206
/// returns, as such, any child tasks potentially accessing the value stack
187207
/// are guaranteed to be completed by the time we pop values off the stack
188208
/// (after the body has completed).
@@ -196,12 +216,14 @@ class TaskLocal {
196216
const HeapObject *key,
197217
/* +1 */ OpaqueValue *value, const Metadata *valueType);
198218

219+
void pushStop(AsyncTask *task);
220+
199221
OpaqueValue* getValue(AsyncTask *task, const HeapObject *key);
200222

201-
/// Returns `true` of more bindings remain in this storage,
223+
/// Returns `true` if more bindings remain in this storage,
202224
/// and `false` if the just popped value was the last one and the storage
203225
/// can be safely disposed of.
204-
bool popValue(AsyncTask *task);
226+
bool pop(AsyncTask *task);
205227

206228
/// Copy all task-local bindings to the target task.
207229
///
@@ -227,9 +249,9 @@ class TaskLocal {
227249
/// Copy all task locals from the current context to the target storage.
228250
/// To prevent data races, there should be no other accesses to the target
229251
/// storage, while copying. Target storage is asserted to be empty, as a proxy
230-
/// for being not in use. If task is specified, it will be used for memory
231-
/// management. If task is nil, items will be allocated using malloc(). The
232-
/// same value of task should be passed to TaskLocal::Storage::destroy().
252+
/// for being not in use. If @c task is specified, it will be used for memory
253+
/// management. If @c task is nil, items will be allocated using malloc(). The
254+
/// same value of @c task should be passed to @c TaskLocal::Storage::destroy() .
233255
static void copyTo(Storage *target, AsyncTask *task);
234256

235257
class AdHocScope {
@@ -239,6 +261,13 @@ class TaskLocal {
239261
AdHocScope(Storage *storage);
240262
~AdHocScope();
241263
};
264+
265+
class WithResetValuesScope {
266+
bool didPush;
267+
public:
268+
WithResetValuesScope();
269+
~WithResetValuesScope();
270+
};
242271
};
243272

244273
} // end namespace swift

include/swift/Runtime/Concurrency.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,34 @@ void swift_task_localValuePush(const HeapObject *key,
637637
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
638638
void swift_task_localValuePop();
639639

640+
/// Bind a task local key to a value in the context of either the current
641+
/// AsyncTask if present, or in the thread-local fallback context if no task
642+
/// available.
643+
///
644+
/// Its Swift signature is
645+
///
646+
/// \code
647+
/// public func _taskLocalStopPush() -> Bool
648+
/// \endcode
649+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
650+
bool swift_task_localStopPush();
651+
652+
/// Pop a single task local binding from the binding stack of the current task,
653+
/// or the fallback thread-local storage if no task is available.
654+
///
655+
/// This operation must be paired up with a preceding "push" operation, as otherwise
656+
/// it may attempt to "pop" off an empty value stuck which will lead to a crash.
657+
///
658+
/// The Swift surface API ensures proper pairing of push and pop operations.
659+
///
660+
/// Its Swift signature is
661+
///
662+
/// \code
663+
/// public func _taskLocalStopPop()
664+
/// \endcode
665+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
666+
void swift_task_localStopPop(bool didPush);
667+
640668
/// Copy all task locals from the current context to the target task.
641669
///
642670
/// Its Swift signature is

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,16 @@ OVERRIDE_TASK_LOCAL(task_localValuePop, void,
356356
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
357357
swift::, ,)
358358

359+
OVERRIDE_TASK_LOCAL(task_localStopPush, bool,
360+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
361+
swift::, ,)
362+
363+
OVERRIDE_TASK_LOCAL(task_localStopPop, void,
364+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
365+
swift::,
366+
(bool didPush),
367+
(didPush))
368+
359369
OVERRIDE_TASK_LOCAL(task_localsCopyTo, void,
360370
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
361371
swift::,

0 commit comments

Comments
 (0)