Skip to content

Commit 3d96d05

Browse files
committed
[TaskLocals] property wrapper keys
1 parent 22c0887 commit 3d96d05

19 files changed

+547
-176
lines changed

include/swift/ABI/MetadataKind.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ METADATAKIND(Task,
9393
METADATAKIND(Job,
9494
3 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
9595

96+
/// A heap-allocated task-local value `TaskLocal::Item`.
97+
METADATAKIND(TaskLocalHeapItem,
98+
4 | MetadataKindIsNonType | MetadataKindIsRuntimePrivate)
99+
96100
// getEnumeratedMetadataKind assumes that all the enumerated values here
97101
// will be <= LastEnumeratedMetadataKind.
98102

include/swift/ABI/Task.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class alignas(2 * alignof(void*)) Job :
5050
NextWaitingTaskIndex = 0,
5151

5252
// The Dispatch object header is one pointer and two ints, which is
53-
// equvialent to three pointers on 32-bit and two pointers 64-bit. Set the
53+
// equivalent to three pointers on 32-bit and two pointers 64-bit. Set the
5454
// indexes accordingly so that DispatchLinkageIndex points to where Dispatch
5555
// expects.
5656
DispatchHasLongObjectHeader = sizeof(void *) == sizeof(int),
@@ -243,14 +243,14 @@ class AsyncTask : public Job {
243243

244244
// ==== Task Local Values ----------------------------------------------------
245245

246-
void localValuePush(const Metadata *keyType,
246+
void localValuePush(const HeapObject *key,
247247
/* +1 */ OpaqueValue *value, const Metadata *valueType) {
248-
Local.pushValue(this, keyType, value, valueType);
248+
Local.pushValue(this, key, value, valueType);
249249
}
250250

251-
OpaqueValue* localValueGet(const Metadata *keyType,
251+
OpaqueValue* localValueGet(const HeapObject *key,
252252
TaskLocal::TaskLocalInheritance inherit) {
253-
return Local.getValue(this, keyType, inherit);
253+
return Local.getValue(this, key, inherit);
254254
}
255255

256256
void localValuePop() {

include/swift/ABI/TaskLocal.h

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,27 @@ struct SwiftError;
2828
class TaskStatusRecord;
2929
class TaskGroup;
3030

31+
extern FullMetadata<HeapMetadata> taskLocalHeapItemHeapMetadata;
32+
3133
// ==== Task Locals Values ---------------------------------------------------
3234

3335
class TaskLocal {
3436
public:
3537
/// Type of the pointed at `next` task local item.
3638
enum class NextLinkType : uintptr_t {
37-
/// This task is known to be a "terminal" node in the lookup of task locals.
38-
/// In other words, even if it had a parent, the parent (and its parents)
39-
/// are known to not contain any any more task locals, and thus any further
40-
/// search beyond this task.
41-
IsTerminal = 0b00,
4239
/// The storage pointer points at the next TaskLocal::Item in this task.
43-
IsNext = 0b01,
40+
IsNext = 0b00,
4441
/// The storage pointer points at a item stored by another AsyncTask.
4542
///
4643
/// Note that this may not necessarily be the same as the task's parent
4744
/// task -- we may point to a super-parent if we know / that the parent
4845
/// does not "contribute" any task local values. This is to speed up
4946
/// lookups by skipping empty parent tasks during get(), and explained
5047
/// in depth in `createParentLink`.
51-
IsParent = 0b11
48+
IsParent = 0b01,
49+
50+
/// The next item is "unstructured" and must be ref-counted.
51+
ThisItemIsHeapItem = 0b10
5252
};
5353

5454
/// Values must match `TaskLocalInheritance` declared in `TaskLocal.swift`.
@@ -70,37 +70,41 @@ class TaskLocal {
7070
/// - specifically from the current task itself
7171
///
7272
/// Such values are *not* accessible from child tasks or detached tasks.
73-
Never = 1
73+
Never = 1
7474
};
7575

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

81-
/// Pointer to the next task local item; be it in this task or in a parent.
82-
/// Low bits encode `NextLinkType`.
83-
/// Item *next = nullptr;
81+
/// Pointer to one of the following:
82+
/// - next task local item as OpaqueValue* if it is task-local allocated
83+
/// - next task local item as HeapObject* if it is heap allocated "heavy"
84+
/// - the parent task's TaskLocal::Storage
85+
///
86+
/// Low bits encode `NextLinkType`, based on which the type of the pointer
87+
/// is determined.
8488
uintptr_t next;
8589

8690
public:
8791
/// The type of the key with which this value is associated.
88-
const Metadata *keyType;
92+
const HeapObject *key;
8993
/// The type of the value stored by this item.
9094
const Metadata *valueType;
9195

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

95-
private:
99+
protected:
96100
explicit Item()
97101
: next(0),
98-
keyType(nullptr),
102+
key(nullptr),
99103
valueType(nullptr) {}
100104

101-
explicit Item(const Metadata *keyType, const Metadata *valueType)
105+
explicit Item(const HeapObject *key, const Metadata *valueType)
102106
: next(0),
103-
keyType(keyType),
107+
key(key),
104108
valueType(valueType) {}
105109

106110
public:
@@ -116,7 +120,7 @@ class TaskLocal {
116120
static Item *createParentLink(AsyncTask *task, AsyncTask *parent);
117121

118122
static Item *createLink(AsyncTask *task,
119-
const Metadata *keyType,
123+
const HeapObject *key,
120124
const Metadata *valueType);
121125

122126
void destroy(AsyncTask *task);
@@ -125,25 +129,32 @@ class TaskLocal {
125129
return reinterpret_cast<Item *>(next & ~statusMask);
126130
}
127131

128-
NextLinkType getNextLinkType() {
132+
NextLinkType getNextLinkType() const {
129133
return static_cast<NextLinkType>(next & statusMask);
130134
}
131135

136+
bool isHeapItem() const {
137+
return getNextLinkType() == NextLinkType::ThisItemIsHeapItem;
138+
}
139+
132140
/// Item does not contain any actual value, and is only used to point at
133141
/// a specific parent item.
134-
bool isEmpty() {
142+
bool isEmpty() const {
135143
return !valueType;
136144
}
137145

138146
/// Retrieve a pointer to the storage of the value.
139147
OpaqueValue *getStoragePtr() {
148+
fprintf(stderr, "[%s:%d] (%s) GET STORAGE\n", __FILE__, __LINE__, __FUNCTION__);
140149
return reinterpret_cast<OpaqueValue *>(
141-
reinterpret_cast<char *>(this) + storageOffset(valueType));
150+
reinterpret_cast<char *>(this) + storageOffset(isHeapItem(), valueType));
142151
}
143152

144153
/// Compute the offset of the storage from the base of the item.
145-
static size_t storageOffset(const Metadata *valueType) {
154+
static size_t storageOffset(bool isHeapItem, const Metadata *valueType) {
146155
size_t offset = sizeof(Item);
156+
157+
147158
if (valueType) {
148159
size_t alignment = valueType->vw_alignment();
149160
return (offset + alignment - 1) & ~(alignment - 1);
@@ -153,15 +164,53 @@ class TaskLocal {
153164
}
154165

155166
/// Determine the size of the item given a particular value type.
156-
static size_t itemSize(const Metadata *valueType) {
157-
size_t offset = storageOffset(valueType);
167+
static size_t itemSize(bool isHeapItem, const Metadata *valueType) {
168+
size_t offset = storageOffset(isHeapItem, valueType);
169+
size_t headerSize = isHeapItem ? sizeof(HeapObject) : 0;
170+
offset += headerSize;
158171
if (valueType) {
159172
offset += valueType->vw_size();
160173
}
161174
return offset;
162175
}
163176
};
164177

178+
/// Heap allocated version of TaskLocal::Item.
179+
/// It is used in limited un-structured concurrency cases, where the task
180+
/// local must be stored on the heap for the time being.
181+
///
182+
/// MUST HAVE THE SAME LAYOUT AS TaskLocal::Item.
183+
/// FIXME: how to actually make this work...
184+
/// get the address of "after heap object" and cast that to Item?
185+
class HeapItem : public HeapObject, public Item {
186+
public:
187+
188+
explicit HeapItem(const HeapMetadata *metadata = &taskLocalHeapItemHeapMetadata)
189+
: HeapObject(metadata),
190+
Item() {}
191+
192+
explicit HeapItem(const HeapObject *key, const Metadata *valueType)
193+
: HeapItem(&taskLocalHeapItemHeapMetadata, key, valueType) {}
194+
195+
explicit HeapItem(const HeapMetadata *metadata,
196+
const HeapObject *key, const Metadata *valueType)
197+
: HeapObject(metadata),
198+
Item(key, valueType) {}
199+
200+
201+
/// Retrieve a pointer to the `Item` that is part of this object.
202+
TaskLocal::Item *getItem() {
203+
fprintf(stderr, "[%s:%d] (%s) GET ITEM PTR, heapItem:%p\n", __FILE__, __LINE__, __FUNCTION__, this);
204+
return reinterpret_cast<TaskLocal::Item *>(
205+
reinterpret_cast<char *>(this) + itemOffset());
206+
}
207+
208+
/// Compute the offset of `Item` part of this object.
209+
static size_t itemOffset() {
210+
size_t offset = sizeof(HeapObject);
211+
return offset;
212+
}
213+
};
165214

166215
class Storage {
167216
friend class TaskLocal::Item;
@@ -202,11 +251,11 @@ class TaskLocal {
202251
void initializeLinkParent(AsyncTask *task, AsyncTask *parent);
203252

204253
void pushValue(AsyncTask *task,
205-
const Metadata *keyType,
254+
const HeapObject *key,
206255
/* +1 */ OpaqueValue *value, const Metadata *valueType);
207256

208257
OpaqueValue* getValue(AsyncTask *task,
209-
const Metadata *keyType,
258+
const HeapObject *key,
210259
TaskLocalInheritance inheritance);
211260

212261
void popValue(AsyncTask *task);

include/swift/AST/KnownSDKTypes.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ KNOWN_SDK_TYPE_DECL(ObjectiveC, ObjCBool, StructDecl, 0)
4040
KNOWN_SDK_TYPE_DECL(Concurrency, UnsafeContinuation, NominalTypeDecl, 2)
4141
KNOWN_SDK_TYPE_DECL(Concurrency, MainActor, NominalTypeDecl, 0)
4242

43+
KNOWN_SDK_TYPE_DECL(Concurrency, TaskLocal, ClassDecl, 1)
44+
4345
#undef KNOWN_SDK_TYPE_DECL

include/swift/Runtime/Concurrency.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,11 +362,20 @@ bool swift_task_tryAddStatusRecord(TaskStatusRecord *record);
362362
///
363363
/// The given record need not be the last record added to
364364
/// the task, but the operation may be less efficient if not.
365-
///s
365+
///
366366
/// Returns false if the task has been cancelled.
367367
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
368368
bool swift_task_removeStatusRecord(TaskStatusRecord *record);
369369

370+
/// Signifies whether the current task is in the middle of executing the
371+
/// operation block of a `with(Throwing)TaskGroup(...) { <operation> }`.
372+
///
373+
/// Task local values must use un-structured allocation for values bound in this
374+
/// scope, as they may be referred to by `group.spawn`-ed tasks and therefore
375+
/// out-life the scope of a task-local value binding.
376+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
377+
bool swift_task_hasTaskGroupStatusRecord();
378+
370379
/// Attach a child task to its parent task and return the newly created
371380
/// `ChildTaskStatusRecord`.
372381
///
@@ -409,7 +418,7 @@ void swift_task_removeCancellationHandler(
409418
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
410419
OpaqueValue*
411420
swift_task_localValueGet(AsyncTask* task,
412-
const Metadata *keyType,
421+
const HeapObject *key,
413422
TaskLocal::TaskLocalInheritance inheritance);
414423

415424
/// Add a task local value to the passed in task.
@@ -427,7 +436,7 @@ swift_task_localValueGet(AsyncTask* task,
427436
/// \endcode
428437
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
429438
void swift_task_localValuePush(AsyncTask* task,
430-
const Metadata *keyType,
439+
const HeapObject *key,
431440
/* +1 */ OpaqueValue *value,
432441
const Metadata *valueType);
433442

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,16 @@ OVERRIDE_TASK_GROUP(taskGroup_addPending, bool,
221221
OVERRIDE_TASK_LOCAL(task_localValuePush, void,
222222
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
223223
swift::,
224-
(AsyncTask *task, const Metadata *keyType,
224+
(AsyncTask *task, const HeapObject *key,
225225
OpaqueValue *value, const Metadata *valueType),
226-
(task, keyType, value, valueType))
226+
(task, key, value, valueType))
227227

228228
OVERRIDE_TASK_LOCAL(task_localValueGet, OpaqueValue *,
229229
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
230230
swift::,
231-
(AsyncTask *task, const Metadata *keyType,
231+
(AsyncTask *task, const HeapObject *key,
232232
TaskLocal::TaskLocalInheritance inheritance),
233-
(task, keyType, inheritance))
233+
(task, key, inheritance))
234234

235235
OVERRIDE_TASK_LOCAL(task_localValuePop, void,
236236
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
@@ -248,6 +248,10 @@ OVERRIDE_TASK_STATUS(task_removeStatusRecord, bool,
248248
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
249249
swift::, (TaskStatusRecord *record), (record))
250250

251+
OVERRIDE_TASK_STATUS(task_hasTaskGroupStatusRecord, bool,
252+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
253+
swift::, , )
254+
251255
OVERRIDE_TASK_STATUS(task_attachChild, ChildTaskStatusRecord *,
252256
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
253257
swift::, (AsyncTask *child), (child))

stdlib/public/Concurrency/TaskGroup.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
215215
priority: Task.Priority = .unspecified,
216216
operation: __owned @Sendable @escaping () async -> ChildTaskResult
217217
) async -> Bool {
218-
return try self.spawnUnlessCancelled(priority: priority) {
218+
return self.spawnUnlessCancelled(priority: priority) {
219219
await operation()
220220
}
221221
}
@@ -467,7 +467,7 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
467467
priority: Task.Priority = .unspecified,
468468
operation: __owned @Sendable @escaping () async throws -> ChildTaskResult
469469
) async -> Bool {
470-
return try self.spawnUnlessCancelled(priority: priority) {
470+
return self.spawnUnlessCancelled(priority: priority) {
471471
try await operation()
472472
}
473473
}

0 commit comments

Comments
 (0)