Skip to content

Commit ec1490d

Browse files
committed
[concurrency] Implement the Task allocator as bump-pointer allocator.
Use the StackAllocator as task allocator. TODO: we could pass an initial pre-allocated first slab to the allocator, which is allocated on the stack or with the parent task's allocator. rdar://problem/71157018
1 parent e7b5843 commit ec1490d

File tree

3 files changed

+41
-40
lines changed

3 files changed

+41
-40
lines changed

stdlib/public/Concurrency/Task.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,8 @@ AsyncTaskAndContext swift::swift_task_create_future_f(
311311
initialContext->Flags.setShouldNotDeallocateInCallee(true);
312312

313313
// Initialize the task-local allocator.
314+
// TODO: consider providing an initial pre-allocated first slab to the
315+
// allocator.
314316
_swift_task_alloc_initialize(task);
315317

316318
return {task, initialContext};

stdlib/public/Concurrency/TaskAlloc.cpp

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,40 +19,27 @@
1919

2020
#include "TaskPrivate.h"
2121
#include "swift/Runtime/Concurrency.h"
22-
#include "swift/Runtime/Debug.h"
22+
#include "../runtime/StackAllocator.h"
2323
#include <stdlib.h>
24-
#include <vector>
2524

2625
using namespace swift;
2726

2827
namespace {
2928

30-
class TaskAllocator {
31-
// Just keep track of all allocations in a vector so that we can
32-
// verify stack discipline. We should make sure the allocator
33-
// implementation strictly verifies allocation order at least
34-
// until we've stabilized the compiler implementation.
35-
std::vector<void*> Allocations;
29+
/// The size of an allocator slab.
30+
///
31+
/// TODO: find the optimal value by experiment.
32+
static constexpr size_t SlabCapacity = 1024;
3633

37-
public:
38-
void *alloc(size_t size) {
39-
void *ptr = malloc(size);
40-
Allocations.push_back(ptr);
41-
return ptr;
42-
}
34+
using TaskAllocator = StackAllocator<SlabCapacity>;
4335

44-
void dealloc(void *ptr) {
45-
if (Allocations.empty() || Allocations.back() != ptr)
46-
fatalError(0, "pointer was not the last allocation on this task");
36+
struct GlobalAllocator {
37+
TaskAllocator allocator;
38+
void *spaceForFirstSlab[64];
4739

48-
Allocations.pop_back();
49-
free(ptr);
50-
}
40+
GlobalAllocator() : allocator(spaceForFirstSlab, sizeof(spaceForFirstSlab)) {}
5141
};
5242

53-
static_assert(sizeof(TaskAllocator) <= sizeof(AsyncTask::AllocatorPrivate),
54-
"task allocator must fit in allocator-private slot");
55-
5643
static_assert(alignof(TaskAllocator) <= alignof(decltype(AsyncTask::AllocatorPrivate)),
5744
"task allocator must not be more aligned than "
5845
"allocator-private slot");
@@ -70,8 +57,8 @@ static TaskAllocator &allocator(AsyncTask *task) {
7057
// FIXME: this fall-back shouldn't be necessary, but it's useful
7158
// for now, since the current execution tests aren't setting up a task
7259
// properly.
73-
static TaskAllocator global;
74-
return global;
60+
static GlobalAllocator global;
61+
return global.allocator;
7562
}
7663

7764
void swift::_swift_task_alloc_destroy(AsyncTask *task) {

stdlib/public/runtime/StackAllocator.h

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ class StackAllocator {
6060
/// True if the first slab is pre-allocated.
6161
bool firstSlabIsPreallocated;
6262

63+
/// The minimal alignment of allocated memory.
64+
static constexpr size_t alignment = alignof(std::max_align_t);
65+
6366
/// If set to true, memory allocations are checked for buffer overflows and
6467
/// use-after-free, similar to guard-malloc.
6568
static constexpr bool guardAllocations =
@@ -90,11 +93,21 @@ class StackAllocator {
9093
assert((size_t)capacity == newCapacity && "capacity overflow");
9194
}
9295

96+
/// The size of the slab header.
97+
static size_t headerSize() {
98+
return llvm::alignTo(sizeof(Slab), llvm::Align(alignment));
99+
}
100+
101+
/// Return \p size with the added overhead of the slab header.
102+
static size_t includingHeader(size_t size) {
103+
return headerSize() + size;
104+
}
105+
93106
/// Return the payload buffer address at \p atOffset.
94107
///
95108
/// Note: it's valid to call this function on a not-yet-constructed slab.
96109
char *getAddr(size_t atOffset) {
97-
return (char *)(this + 1) + atOffset;
110+
return (char *)this + headerSize() + atOffset;
98111
}
99112

100113
/// Return true if this slab can fit an allocation of \p size.
@@ -162,23 +175,20 @@ class StackAllocator {
162175
previous(previous), slab(slab) {}
163176

164177
void *getAllocatedMemory() {
165-
return (void *)(this + 1);
178+
return (char *)this + headerSize();
179+
}
180+
181+
/// The size of the allocation header.
182+
static size_t headerSize() {
183+
return llvm::alignTo(sizeof(Allocation), llvm::Align(alignment));
166184
}
167185

168186
/// Return \p size with the added overhead of the allocation header.
169187
static size_t includingHeader(size_t size) {
170-
return size + sizeof(Allocation);
188+
return headerSize() + size;
171189
}
172190
};
173191

174-
static constexpr size_t alignment = alignof(std::max_align_t);
175-
176-
static_assert(sizeof(Slab) % StackAllocator::alignment == 0,
177-
"Slab size must be a multiple of the max allocation alignment");
178-
179-
static_assert(sizeof(Allocation) % StackAllocator::alignment == 0,
180-
"Allocation size must be a multiple of the max allocation alignment");
181-
182192
// Return a slab which is suitable to allocate \p size memory.
183193
Slab *getSlabForAllocation(size_t size) {
184194
Slab *slab = (lastAllocation ? lastAllocation->slab : firstSlab);
@@ -204,7 +214,7 @@ class StackAllocator {
204214
}
205215
size_t capacity = std::max(SlabCapacity,
206216
Allocation::includingHeader(size));
207-
void *slabBuffer = malloc(sizeof(Slab) + capacity);
217+
void *slabBuffer = malloc(Slab::includingHeader(capacity));
208218
Slab *newSlab = new (slabBuffer) Slab(capacity);
209219
if (slab)
210220
slab->next = newSlab;
@@ -235,10 +245,12 @@ class StackAllocator {
235245

236246
/// Construct a StackAllocator with a pre-allocated first slab.
237247
StackAllocator(void *firstSlabBuffer, size_t bufferCapacity) {
238-
char *start = (char *)llvm::alignAddr(firstSlabBuffer, llvm::Align(alignment));
248+
char *start = (char *)llvm::alignAddr(firstSlabBuffer,
249+
llvm::Align(alignment));
239250
char *end = (char *)firstSlabBuffer + bufferCapacity;
240-
assert(start + sizeof(Slab) <= end && "buffer for first slab too small");
241-
firstSlab = new (start) Slab(end - start - sizeof(Slab));
251+
assert(start + Slab::headerSize() <= end &&
252+
"buffer for first slab too small");
253+
firstSlab = new (start) Slab(end - start - Slab::headerSize());
242254
firstSlabIsPreallocated = true;
243255
}
244256

0 commit comments

Comments
 (0)