Skip to content

[TaskLocals] Remove fragment, cleanup code organization, use task-local allocator #36210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 2, 2021

Conversation

ktoso
Copy link
Contributor

@ktoso ktoso commented Mar 1, 2021

Task locals must have a pointer inside every task to implement their functionality after all, so as such, we don't need to make them a fragment.

Also proceed towards more cleanly separated out ABI and remove implementation details from Task.h entirely.

rdar://74218679

@ktoso ktoso force-pushed the wip-locals-detached branch from 0a5e544 to 11d9785 Compare March 1, 2021 13:32
@ktoso
Copy link
Contributor Author

ktoso commented Mar 1, 2021

@swift-ci please smoke test

@@ -173,12 +172,16 @@ class AsyncTask : public HeapObject, public Job {
/// Reserved for the use of the task-local stack allocator.
void *AllocatorPrivate[4];

/// Task local values storage container.
TaskLocal::Storage Local;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we future proof it a bit more just in case?

Instruments folks asked for a limited size "carry it always, but only the last 3 most recent values" which I'll be implementing next. but I wonder if it might be necessary to add resilience on this level, or we're ok because task is?

#include "swift/ABI/Task.h"
#include "swift/ABI/MetadataValues.h"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made includes more consistent; was battling include hell for quite a bit when adding new header files today here

@@ -122,10 +122,6 @@ void swift_task_cancel(AsyncTask *task);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_cancel_group_child_tasks(AsyncTask *task, TaskGroup *group);

/// Get 'active' AsyncTask, depending on platform this may use thread local storage.
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
AsyncTask* swift_task_get_active();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not used; we're using the getCurrent which does the same

// Initialize task locals fragment.
auto taskLocalsFragment = task->localValuesFragment();
new (taskLocalsFragment) AsyncTask::TaskLocalValuesFragment();
taskLocalsFragment->initializeLinkParent(task, parent);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fragment is gone, every task needs the pointer because we store locals in this task so it always has to be ready to accept someone pushing a value onto it

func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self.keyType))
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not necessary, we use the types as identity in C by raw pointers, so no need for erasure in Swift

await x3
}

_ = await x1
try! await printTaskLocal(\.number) // CHECK: NumberKey: 0 {{.*}}
printTaskLocal(\.number) // CHECK: NumberKey: 0 {{.*}}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now we were able to remove a lot of await which is awesome -- thanks to getCurrent task from sync functions

@@ -255,9 +260,6 @@ AsyncTaskAndContext swift::swift_task_create_group_future_f(
// Figure out the size of the header.
size_t headerSize = sizeof(AsyncTask);

/// Every task is able to store task local values.
headerSize += sizeof(AsyncTask::TaskLocalValuesFragment);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need for this, since locals are part of the Task now

assert(task);
size_t amountToAllocate = Item::itemSize(valueType);
// assert(amountToAllocate % MaximumAlignment == 0); // TODO: do we need this?
void *allocation = swift_task_alloc(task, amountToAllocate);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hooray, we're now using swift_task_alloc/swift_task_dealloc, and it all works excellently thanks to the only way to bind a value being withLocal 👍

@ktoso ktoso requested review from rjmccall and DougGregor and removed request for rjmccall March 1, 2021 16:00
@ktoso ktoso changed the title [TaskLocals] Cleanly separate locals impl from Task, no need for fragment [TaskLocals] Remove fragment, cleanup code organization, use task-local allocator Mar 1, 2021
@ktoso ktoso force-pushed the wip-locals-detached branch from d6b8834 to b0df9b0 Compare March 1, 2021 16:02
@ktoso ktoso force-pushed the wip-locals-detached branch from b0df9b0 to fcb1c01 Compare March 2, 2021 02:14
@ktoso
Copy link
Contributor Author

ktoso commented Mar 2, 2021

@swift-ci please smoke test

@ktoso
Copy link
Contributor Author

ktoso commented Mar 2, 2021

@swift-ci please smoke test macOS

@ktoso ktoso merged commit d26b1f0 into swiftlang:main Mar 2, 2021
@ktoso ktoso deleted the wip-locals-detached branch March 2, 2021 10:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant