Skip to content

Conversation

@mikeash
Copy link
Contributor

@mikeash mikeash commented Nov 1, 2025

Add SWIFT_DEBUG_ENABLE_TASK_SLAB_ALLOCATOR, which is on by default. When turned off, async stack allocations call through to malloc/free. This allows memory debugging tools to be used on async stack allocations.

/// SlabMetadataPtr specifies a fake metadata pointer to place at the beginning
/// of slab allocations, so analysis tools can identify them.
template <size_t SlabCapacity, Metadata *SlabMetadataPtr>
template <size_t SlabCapacity, Metadata *SlabMetadataPtr, bool (*disableSlabAllocator)()>
Copy link
Member

Choose a reason for hiding this comment

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

Why introduce the type parameter here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just to keep StackAllocator reasonably self-contained and unaware of things like debug variables.

Copy link
Member

Choose a reason for hiding this comment

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

But this encodes it right into the type, so it feels like its even more deeply aware of things like debug variables.

@mikeash mikeash force-pushed the task-allocator-disable-option branch 2 times, most recently from 6bd65ba to 3852a97 Compare November 1, 2025 00:24
@ktoso
Copy link
Contributor

ktoso commented Nov 1, 2025

Very nice, that'll be useful

Copy link
Member

@compnerd compnerd left a comment

Choose a reason for hiding this comment

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

Overall, this seems good. I still thing that this is really over-encoding the environment variable since it encodes it into the type - perhaps we should use the STL approach of _Allocator as a type parameter? That would allow you to hoist the allocator and the knowledge of the environment variable out of the stack allocator itself.

Comment on lines 339 to 342
if (SWIFT_UNLIKELY(EnableSlabAllocator())) {
free(ptr);
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if (SWIFT_UNLIKELY(EnableSlabAllocator())) {
free(ptr);
return;
}
if (SWIFT_UNLIKELY(EnableSlabAllocator()))
return free(ptr);

Enabled,
Disabled,
};
static std::atomic<EnableState> savedState = {EnableState::Uninitialized};
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be marked constinit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We're still on C++17, so unfortunately not.

savedState.store(state, std::memory_order_relaxed);
}

return SWIFT_UNLIKELY(state == EnableState::Enabled);
Copy link
Contributor

Choose a reason for hiding this comment

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

The "unlikely" seems backwards.

Does SWIFT_UNLIKELY actually do anything in a return position?

/// Allocate a memory buffer of \p size.
void *alloc(size_t size) {
if (SWIFT_UNLIKELY(EnableSlabAllocator()))
return malloc(size);
Copy link
Contributor

Choose a reason for hiding this comment

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

You have the condition reversed here and below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, I switched it from disable to enable and didn't fix up the uses properly. I rearranged various things and it should be correct now.

@rjmccall
Copy link
Contributor

rjmccall commented Nov 1, 2025

If we expect this to be uncommonly used, it'd be nice to hide it in a path that isn't normally taken. Maybe we can initialize the allocator to ignore the initial slab, and then we only have to check the condition when we'd otherwise be allocating a new slab? There might be a similar unlikely case that we can bury the check during deallocation in, like lastAllocation not being set.

@mikeash
Copy link
Contributor Author

mikeash commented Nov 3, 2025

@compnerd The _Allocator approach would involve templating it with a type that has a method that can be called to retrieve the info? Is there a concrete difference from the current approach, other than templating with a type versus a function? I'm happy to make the change, just possibly missing your point.

@compnerd
Copy link
Member

compnerd commented Nov 3, 2025

It is mostly a type vs function thing. The type based approach feels more flexible (and is a general pattern in C++). That should allow you to accomplish the same type of isolation but also opens it up for further expansion in the future without changing the type itself.

@mikeash
Copy link
Contributor Author

mikeash commented Nov 3, 2025

Gotcha. I'll make that change, then.

@mikeash mikeash force-pushed the task-allocator-disable-option branch from 3852a97 to 81107a8 Compare November 3, 2025 23:06
@mikeash
Copy link
Contributor Author

mikeash commented Nov 3, 2025

@rjmccall Nice idea about hiding this behind existing conditionals. I pushed changes to make that happen. We now ensure lastAllocation and firstSlab stay initialized to null when the slab allocator is disabled. When allocating, we only check enablement when those are null, which is the case where we'd need to allocate a new slab otherwise. When deallocating, we only check when lastAllocation is null or doesn't match the allocation being freed, which is otherwise a fatal error.

Copy link
Contributor

@rjmccall rjmccall left a comment

Choose a reason for hiding this comment

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

Thanks, a lot happier about this! I think you've got an UNLIKELY backwards, but maybe it doesn't matter for the code-generation.

Slab *getSlabForAllocation(size_t size) {
Slab *slab = (lastAllocation ? lastAllocation->slab : firstSlab);
if (slab) {
if (SWIFT_UNLIKELY(slab)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems likely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I blame daylight saving, end of. It actually makes a decent difference in the codegen. Fixing this makes the fast path quite a bit more linear. I was getting a little confused following it around before because it bounced back and forth so much, but didn't quite realize what that meant. Pushed the fix now.

@mikeash mikeash force-pushed the task-allocator-disable-option branch from 81107a8 to 7e66850 Compare November 4, 2025 02:25
Copy link
Contributor

@rjmccall rjmccall left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@mikeash
Copy link
Contributor Author

mikeash commented Nov 6, 2025

@swift-ci please test

@mikeash mikeash force-pushed the task-allocator-disable-option branch from 7e66850 to 07bbbfc Compare November 7, 2025 20:26
@mikeash mikeash requested a review from a team as a code owner November 7, 2025 20:26
@mikeash
Copy link
Contributor Author

mikeash commented Nov 7, 2025

Now with less build breakage. Needed a little #if action to work in Embedded, and Compatibility56 needed its own copy of StackAllocator.h.

@mikeash
Copy link
Contributor Author

mikeash commented Nov 7, 2025

@swift-ci please test

@mikeash mikeash force-pushed the task-allocator-disable-option branch from 07bbbfc to ecdb965 Compare November 7, 2025 23:25
@mikeash
Copy link
Contributor Author

mikeash commented Nov 7, 2025

And unittests/runtime/StackAllocator.cpp needed fixing too. Third time's the charm?

@mikeash
Copy link
Contributor Author

mikeash commented Nov 7, 2025

@swift-ci please test

… allocator.

Add SWIFT_DEBUG_ENABLE_TASK_SLAB_ALLOCATOR, which is on by default. When turned off, async stack allocations call through to malloc/free. This allows memory debugging tools to be used on async stack allocations.
@mikeash mikeash force-pushed the task-allocator-disable-option branch from ecdb965 to a62f08e Compare November 8, 2025 03:48
@mikeash
Copy link
Contributor Author

mikeash commented Nov 8, 2025

Someday I will remember to update the symbols in the ABI tests.

@mikeash
Copy link
Contributor Author

mikeash commented Nov 8, 2025

@swift-ci please test

@mikeash mikeash enabled auto-merge November 8, 2025 03:49
@mikeash mikeash merged commit f149a8b into swiftlang:main Nov 8, 2025
4 of 5 checks passed
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.

4 participants