Skip to content

Commit fcb1c01

Browse files
committed
[TaskLocal] Use the task-local stack discipline allocator
1 parent 5c71ba9 commit fcb1c01

File tree

10 files changed

+52
-49
lines changed

10 files changed

+52
-49
lines changed

include/swift/ABI/Task.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,6 @@ class AsyncTask : public HeapObject, public Job {
201201

202202
// ==== Task Local Values ----------------------------------------------------
203203

204-
void localValueInitializeLinkParent(AsyncTask *parent) {
205-
Local.initializeLinkParent(this, parent);
206-
}
207-
208204
void localValuePush(const Metadata *keyType,
209205
/* +1 */ OpaqueValue *value, const Metadata *valueType) {
210206
Local.pushValue(this, keyType, value, valueType);

include/swift/ABI/TaskLocal.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ class TaskLocal {
9393
// uninitialized or contain an instance of \c valueType.
9494

9595
private:
96+
explicit Item()
97+
: next(0),
98+
keyType(nullptr),
99+
valueType(nullptr) {}
100+
96101
explicit Item(const Metadata *keyType, const Metadata *valueType)
97102
: next(0),
98103
keyType(keyType),
@@ -114,11 +119,7 @@ class TaskLocal {
114119
const Metadata *keyType,
115120
const Metadata *valueType);
116121

117-
void destroy(AsyncTask *task) {
118-
if (valueType) {
119-
valueType->vw_destroy(getStoragePtr());
120-
}
121-
}
122+
void destroy(AsyncTask *task);
122123

123124
Item *getNext() {
124125
return reinterpret_cast<Item *>(next & ~statusMask);
@@ -210,12 +211,13 @@ class TaskLocal {
210211

211212
void popValue(AsyncTask *task);
212213

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

217-
TaskLocal::Storage* swift_task_localValueStorage(AsyncTask *task);
218-
219221
} // end namespace swift
220222

221223
#endif

stdlib/public/Concurrency/Task.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ static void completeTask(AsyncTask *task, ExecutorRef executor,
190190
// Set that there's no longer a running task in the current thread.
191191
_swift_task_clearCurrent();
192192

193+
// Destroy and deallocate any remaining task local items.
194+
// We need to do this before we destroy the task local deallocator.
195+
task->Local.destroy(task);
196+
193197
// Tear down the task-local allocator immediately;
194198
// there's no need to wait for the object to be destroyed.
195199
_swift_task_alloc_destroy(task);
@@ -288,9 +292,6 @@ AsyncTaskAndContext swift::swift_task_create_group_future_f(
288292
new(allocation) AsyncTask(&taskHeapMetadata, flags,
289293
function, initialContext);
290294

291-
// Initialize task locals fragment.
292-
task->Local.initializeLinkParent(task, parent);
293-
294295
// Initialize the child fragment if applicable.
295296
if (parent) {
296297
auto childFragment = task->childFragment();
@@ -343,6 +344,13 @@ AsyncTaskAndContext swift::swift_task_create_group_future_f(
343344
// TODO: consider providing an initial pre-allocated first slab to the allocator.
344345
_swift_task_alloc_initialize(task);
345346

347+
// TODO: if the allocator would be prepared earlier we could do this in some
348+
// other existing if-parent if rather than adding another one here.
349+
if (parent) {
350+
// Initialize task locals with a link to the parent task.
351+
task->Local.initializeLinkParent(task, parent);
352+
}
353+
346354
return {task, initialContext};
347355
}
348356

stdlib/public/Concurrency/TaskLocal.cpp

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@ using namespace swift;
2020
// =============================================================================
2121
// ==== ABI --------------------------------------------------------------------
2222

23-
TaskLocal::Storage* swift::swift_task_localValueStorage(AsyncTask *task) {
24-
return &task->Local;
25-
}
26-
2723
void swift::swift_task_localValuePush(AsyncTask *task,
2824
const Metadata *keyType,
2925
/* +1 */ OpaqueValue *value,
@@ -46,25 +42,19 @@ void swift::swift_task_localValuePop(AsyncTask *task) {
4642

4743
void TaskLocal::Storage::initializeLinkParent(AsyncTask* task,
4844
AsyncTask* parent) {
49-
assert(!head && "task local storage was already initialized with parent");
50-
if (parent) {
51-
head = TaskLocal::Item::createParentLink(task, parent);
52-
}
45+
assert(!head && "initial task local storage was already initialized");
46+
assert(parent && "parent must be provided to link to it");
47+
head = TaskLocal::Item::createParentLink(task, parent);
5348
}
5449

5550
TaskLocal::Item*
5651
TaskLocal::Item::createParentLink(AsyncTask *task, AsyncTask *parent) {
57-
assert(parent);
5852
size_t amountToAllocate = Item::itemSize(/*valueType*/nullptr);
5953
// assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
60-
void *allocation = malloc(amountToAllocate); // TODO: use task-local allocator
54+
void *allocation = swift_task_alloc(task, amountToAllocate);
55+
Item *item = new(allocation) Item();
6156

62-
Item *item =
63-
new(allocation) Item(nullptr, nullptr);
64-
65-
auto parentHead = parent->Local.head;
66-
// auto parentLocalStorage = swift_task_localValueStorage(parent);
67-
// auto parentHead = parentLocalStorage->head;
57+
auto parentHead = parent->Local.head;
6858
if (parentHead) {
6959
if (parentHead->isEmpty()) {
7060
switch (parentHead->getNextLinkType()) {
@@ -105,11 +95,9 @@ TaskLocal::Item::createLink(AsyncTask *task,
10595
assert(task);
10696
size_t amountToAllocate = Item::itemSize(valueType);
10797
// assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
108-
void *allocation = malloc(amountToAllocate); // TODO: use task-local allocator rdar://74218679
109-
Item *item =
110-
new(allocation) Item(keyType, valueType);
98+
void *allocation = swift_task_alloc(task, amountToAllocate);
99+
Item *item = new(allocation) Item(keyType, valueType);
111100

112-
auto nextStorage = swift_task_localValueStorage(task);
113101
auto next = task->Local.head;
114102
auto nextLinkType = next ? NextLinkType::IsNext
115103
: NextLinkType::IsTerminal;
@@ -122,6 +110,14 @@ TaskLocal::Item::createLink(AsyncTask *task,
122110
// =============================================================================
123111
// ==== destroy ----------------------------------------------------------------
124112

113+
void TaskLocal::Item::destroy(AsyncTask *task) {
114+
if (valueType) {
115+
valueType->vw_destroy(getStoragePtr());
116+
}
117+
118+
swift_task_dealloc(task, this);
119+
}
120+
125121
void TaskLocal::Storage::destroy(AsyncTask *task) {
126122
auto item = head;
127123
head = nullptr;
@@ -131,13 +127,14 @@ void TaskLocal::Storage::destroy(AsyncTask *task) {
131127
case TaskLocal::NextLinkType::IsNext:
132128
next = item->getNext();
133129
item->destroy(task);
134-
free(item);
135130
item = next;
136131
break;
137132

138133
case TaskLocal::NextLinkType::IsParent:
139134
case TaskLocal::NextLinkType::IsTerminal:
140-
// we're done here, we must not destroy values owned by the parent task.
135+
// we're done here; as we must not proceed into the parent owned values.
136+
// we do have to destroy the item pointing at the parent/edge itself though.
137+
item->destroy(task);
141138
return;
142139
}
143140
}
@@ -159,8 +156,9 @@ void TaskLocal::Storage::pushValue(AsyncTask *task,
159156

160157
void TaskLocal::Storage::popValue(AsyncTask *task) {
161158
assert(head && "attempted to pop value off empty task-local stack");
162-
head->destroy(task);
159+
auto old = head;
163160
head = head->getNext();
161+
old->destroy(task);
164162
}
165163

166164
OpaqueValue* TaskLocal::Storage::getValue(AsyncTask *task,

stdlib/public/Concurrency/TaskLocal.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,12 @@ extension Task {
7171
/// bound in the current (or any parent) tasks.
7272
public static func local<Key>(_ keyPath: KeyPath<TaskLocalValues, Key>)
7373
-> Key.Value where Key: TaskLocalKey {
74-
guard let _task = Task.unsafeCurrent?._task else {
75-
fatalError("No async task!")
74+
guard let unsafeTask = Task.unsafeCurrent else {
7675
return Key.defaultValue
7776
}
7877

7978
let value = _taskLocalValueGet(
80-
_task, keyType: Key.self, inheritance: Key.inherit.rawValue)
79+
unsafeTask._task, keyType: Key.self, inheritance: Key.inherit.rawValue)
8180
guard let rawValue = value else {
8281
return Key.defaultValue
8382
}

stdlib/public/Concurrency/TaskPrivate.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ void donateThreadToGlobalExecutorUntil(bool (*condition)(void*),
5757
#endif
5858

5959
// ==== ------------------------------------------------------------------------
60-
// TODO: this was moved from Task.cpp to share it with TaskGroup.cpp, where else better place to put it?
6160

6261
namespace {
6362

test/Concurrency/Runtime/async_task_locals_async_let.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ func async_let_nested() async {
4646
func test() async {
4747
printTaskLocal(\.number) // CHECK: NumberKey: 2 {{.*}}
4848
async let x31 = printTaskLocal(\.number) // CHECK: NumberKey: 2 {{.*}}
49-
_ = try! await x31
49+
_ = await x31
5050
}
5151
async let x3: () = test()
5252

53-
_ = try! await x2
53+
_ = await x2
5454
await x3
5555
}
5656

@@ -64,8 +64,9 @@ func async_let_nested_skip_optimization() async {
6464
async let x3: Int? = { () async -> Int? in
6565
async let x4: Int? = { () async -> Int? in
6666
async let x5: Int? = { () async -> Int? in
67+
assert(Task.local(\.number) == 2)
6768
async let xx = printTaskLocal(\.number) // CHECK: NumberKey: 2 {{.*}}
68-
return try! await xx
69+
return await xx
6970
}()
7071
return await x5
7172
}()

test/Concurrency/Runtime/async_task_locals_basic.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func printTaskLocal<Key>(
5353
_ expected: Key.Value? = nil,
5454
file: String = #file, line: UInt = #line, function: String = #function
5555
) where Key: TaskLocalKey {
56-
let value = Task.local(key, file: file, line: line, function: function)
56+
let value = Task.local(key)
5757
print("\(Key.self): \(value) at \(file):\(line)")
5858
if let expected = expected {
5959
assert("\(expected)" == "\(value)",

test/Concurrency/Runtime/async_task_locals_groups.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ func printTaskLocal<Key>(
2525
_ key: KeyPath<TaskLocalValues, Key>,
2626
_ expected: Key.Value? = nil,
2727
file: String = #file, line: UInt = #line
28-
) async throws where Key: TaskLocalKey {
28+
) where Key: TaskLocalKey {
2929
let value = Task.local(key)
3030
print("\(Key.self): \(value) at \(file):\(line)")
3131
if let expected = expected {
@@ -68,8 +68,8 @@ func groups() async {
6868
await group.add {
6969
printTaskLocal(\.number) // CHECK: NumberKey: 2 {{.*}}
7070

71-
async let childInsideGroupChild = printTaskLocal(\.number)
72-
try! await childInsideGroupChild // CHECK: NumberKey: 2 {{.*}}
71+
async let childInsideGroupChild: () = printTaskLocal(\.number)
72+
await childInsideGroupChild // CHECK: NumberKey: 2 {{.*}}
7373

7474
return Task.local(\.number)
7575
}

test/Concurrency/Runtime/async_task_locals_inherit_never.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func printTaskLocal<Key>(
1717
_ key: KeyPath<TaskLocalValues, Key>,
1818
_ expected: Key.Value? = nil,
1919
file: String = #file, line: UInt = #line
20-
) async where Key: TaskLocalKey {
20+
) where Key: TaskLocalKey {
2121
let value = Task.local(key)
2222
print("\(Key.self): \(value) at \(file):\(line)")
2323
if let expected = expected {

0 commit comments

Comments
 (0)