Skip to content

Commit 7babc10

Browse files
generatedunixname89002005232357meta-codesync[bot]
authored andcommitted
Revert D130865756
Summary: This diff reverts D130865756 The original diff was mistakenly reverted after the issue has been fixed. Depends on D130865756 Reviewed By: vitaut Differential Revision: D130936823 fbshipit-source-id: 217e3ddc7088b614e7fb077ae0f3beec1f724497
1 parent a77c7bc commit 7babc10

6 files changed

Lines changed: 139 additions & 58 deletions

File tree

‎folly/memory/Arena-inl.h‎

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,26 @@ template <class Alloc>
2828
void* Arena<Alloc>::allocateSlow(size_t size) {
2929
char* start;
3030

31+
const bool large = size > minBlockSize();
3132
size_t allocSize;
3233
if (!checked_add(
33-
&allocSize, std::max(size, minBlockSize()), roundUp(sizeof(Block)))) {
34+
&allocSize,
35+
std::max(size, minBlockSize()),
36+
large ? headerSize<LargeBlock>() : headerSize<Block>())) {
3437
throw_exception<std::bad_alloc>();
3538
}
3639
if (sizeLimit_ != kNoSizeLimit &&
3740
allocSize > sizeLimit_ - totalAllocatedSize_) {
3841
throw_exception<std::bad_alloc>();
3942
}
4043

41-
if (size > minBlockSize()) {
44+
if (large) {
4245
// Allocate a large block for this chunk only; don't change ptr_ and end_,
4346
// let them point into a normal block (or none, if they're null)
44-
allocSize = roundUp(sizeof(LargeBlock)) + size;
4547
void* mem = AllocTraits::allocate(alloc(), allocSize);
4648
auto blk = new (mem) LargeBlock(allocSize);
4749
start = align(blk->start());
48-
largeBlocks_.push_back(*blk);
50+
append(firstLargeBlock_, lastLargeBlock_, blk, blk);
4951
} else {
5052
// Allocate a normal sized block and carve out size bytes from it
5153
// Will allocate more than size bytes if convenient
@@ -55,8 +57,8 @@ void* Arena<Alloc>::allocateSlow(size_t size) {
5557
void* mem = AllocTraits::allocate(alloc(), allocSize);
5658
auto blk = new (mem) Block();
5759
start = align(blk->start());
58-
blocks_.push_back(*blk);
59-
currentBlock_ = blocks_.last();
60+
append(firstBlock_, lastBlock_, blk, blk);
61+
currentBlock_ = blk;
6062
ptr_ = start + size;
6163
end_ = static_cast<char*>(mem) + allocSize;
6264
assert(ptr_ <= end_);
@@ -71,16 +73,26 @@ void Arena<Alloc>::merge(Arena<Alloc>&& other) {
7173
FOLLY_SAFE_CHECK(
7274
blockGoodAllocSize() == other.blockGoodAllocSize(),
7375
"cannot merge arenas of different minBlockSize");
74-
const bool wasEmpty = blocks_.empty();
75-
blocks_.splice_after(blocks_.before_begin(), other.blocks_);
76-
other.blocks_.clear();
77-
if (wasEmpty) {
78-
// currentBlock_ is still before_begin(); leaving it there would let
79-
// canReuseExistingBlock() bump-allocate into a merged block.
80-
currentBlock_ = blocks_.last();
76+
// Put the other arena's blocks in front of ours, which keeps them out of
77+
// reach of canReuseExistingBlock(). Without blocks of our own, they are all
78+
// in use, so the last one becomes current.
79+
if (other.firstBlock_) {
80+
append(other.firstBlock_, other.lastBlock_, firstBlock_, lastBlock_);
81+
firstBlock_ = other.firstBlock_;
82+
lastBlock_ = other.lastBlock_;
83+
if (!currentBlock_) {
84+
currentBlock_ = lastBlock_;
85+
}
8186
}
82-
largeBlocks_.splice_after(largeBlocks_.before_begin(), other.largeBlocks_);
83-
other.largeBlocks_.clear();
87+
append(
88+
other.firstLargeBlock_,
89+
other.lastLargeBlock_,
90+
firstLargeBlock_,
91+
lastLargeBlock_);
92+
firstLargeBlock_ = other.firstLargeBlock_;
93+
lastLargeBlock_ = other.lastLargeBlock_;
94+
other.firstBlock_ = other.lastBlock_ = other.currentBlock_ = nullptr;
95+
other.firstLargeBlock_ = other.lastLargeBlock_ = nullptr;
8496
other.ptr_ = other.end_ = nullptr;
8597
totalAllocatedSize_ += other.totalAllocatedSize_;
8698
other.totalAllocatedSize_ = 0;

‎folly/memory/Arena.h‎

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
#pragma once
1818
#define FOLLY_ARENA_H_
1919

20+
#include <algorithm>
2021
#include <cassert>
2122
#include <limits>
2223
#include <stdexcept>
2324
#include <utility>
2425

25-
#include <boost/intrusive/slist.hpp>
26-
2726
#include <folly/Conv.h>
2827
#include <folly/Likely.h>
2928
#include <folly/Memory.h>
@@ -67,7 +66,6 @@ class Arena {
6766
size_t sizeLimit = kNoSizeLimit,
6867
size_t maxAlign = kDefaultMaxAlign)
6968
: allocAndSize_(alloc, minBlockSize),
70-
currentBlock_(blocks_.last()),
7169
ptr_(nullptr),
7270
end_(nullptr),
7371
totalAllocatedSize_(0),
@@ -99,7 +97,7 @@ class Arena {
9997
}
10098

10199
if (canReuseExistingBlock(size)) {
102-
currentBlock_++;
100+
currentBlock_ = currentBlock_->next;
103101
char* r = align(currentBlock_->start());
104102
ptr_ = r + size;
105103
end_ = currentBlock_->start() + blockGoodAllocSize() - sizeof(Block);
@@ -124,10 +122,10 @@ class Arena {
124122
void clear() {
125123
bytesUsed_ = 0;
126124
freeLargeBlocks(); // We don't reuse large blocks
127-
if (blocks_.empty()) {
125+
if (!firstBlock_) {
128126
return;
129127
}
130-
currentBlock_ = blocks_.begin();
128+
currentBlock_ = firstBlock_;
131129
ptr_ = align(currentBlock_->start());
132130
end_ = currentBlock_->start() + blockGoodAllocSize() - sizeof(Block);
133131
assert(ptr_ <= end_);
@@ -151,10 +149,8 @@ class Arena {
151149
private:
152150
using AllocTraits =
153151
typename std::allocator_traits<Alloc>::template rebind_traits<char>;
154-
using BlockLink = boost::intrusive::slist_member_hook<>;
155-
156152
struct alignas(max_align_v) Block {
157-
BlockLink link;
153+
Block* next = nullptr;
158154

159155
char* start() { return reinterpret_cast<char*>(this + 1); }
160156

@@ -164,11 +160,11 @@ class Arena {
164160

165161
constexpr size_t blockGoodAllocSize() {
166162
return ArenaAllocatorTraits<Alloc>::goodSize(
167-
alloc(), roundUp(sizeof(Block)) + minBlockSize());
163+
alloc(), headerSize<Block>() + minBlockSize());
168164
}
169165

170166
struct alignas(max_align_v) LargeBlock {
171-
BlockLink link;
167+
LargeBlock* next = nullptr;
172168
const size_t allocSize;
173169

174170
char* start() { return reinterpret_cast<char*>(this + 1); }
@@ -182,35 +178,39 @@ class Arena {
182178
// We don't reuse large blocks
183179
return false;
184180
}
185-
if (blocks_.empty() || currentBlock_ == blocks_.last()) {
186-
// No regular blocks to reuse
187-
return false;
188-
}
189-
return true;
181+
// No regular blocks to reuse past the current one
182+
return currentBlock_ != lastBlock_;
190183
}
191184

192185
void freeBlocks() {
193-
blocks_.clear_and_dispose([this](Block* b) {
186+
for (Block* b = firstBlock_; b;) {
187+
Block* const next = b->next;
194188
b->~Block();
195189
AllocTraits::deallocate(
196190
alloc(), reinterpret_cast<char*>(b), blockGoodAllocSize());
197-
});
191+
b = next;
192+
}
193+
firstBlock_ = lastBlock_ = currentBlock_ = nullptr;
198194
}
199195

200196
void freeLargeBlocks() {
201-
largeBlocks_.clear_and_dispose([this](LargeBlock* b) {
197+
for (LargeBlock* b = firstLargeBlock_; b;) {
198+
LargeBlock* const next = b->next;
202199
auto size = b->allocSize;
203200
totalAllocatedSize_ -= size;
204201
b->~LargeBlock();
205202
AllocTraits::deallocate(alloc(), reinterpret_cast<char*>(b), size);
206-
});
203+
b = next;
204+
}
205+
firstLargeBlock_ = lastLargeBlock_ = nullptr;
207206
}
208207

209208
public:
210209
static constexpr size_t kDefaultMinBlockSize = 4096 - sizeof(Block);
211210
static constexpr size_t kNoSizeLimit = 0;
212211
static constexpr size_t kDefaultMaxAlign = alignof(Block);
213212
static constexpr size_t kBlockOverhead = sizeof(Block);
213+
static constexpr size_t kLargeBlockOverhead = sizeof(LargeBlock);
214214

215215
private:
216216
bool isAligned(uintptr_t address) const {
@@ -232,19 +232,23 @@ class Arena {
232232

233233
char* align(char* ptr) { return align_ceil(ptr, maxAlign_); }
234234

235-
// cache_last<true> makes the list keep a pointer to the last element, so we
236-
// have push_back() and constant time splice_after()
237-
using BlockList = boost::intrusive::slist<
238-
Block,
239-
boost::intrusive::member_hook<Block, BlockLink, &Block::link>,
240-
boost::intrusive::constant_time_size<false>,
241-
boost::intrusive::cache_last<true>>;
235+
// A block header plus the most padding align() can add after it, since the
236+
// allocator only guarantees alignment for the header.
237+
template <typename Header>
238+
size_t headerSize() const {
239+
return sizeof(Header) + std::max(maxAlign_, alignof(Header)) -
240+
alignof(Header);
241+
}
242242

243-
using LargeBlockList = boost::intrusive::slist<
244-
LargeBlock,
245-
boost::intrusive::member_hook<LargeBlock, BlockLink, &LargeBlock::link>,
246-
boost::intrusive::constant_time_size<false>,
247-
boost::intrusive::cache_last<true>>;
243+
// Appends the chain [first, last], if not empty, to the chain [head, tail].
244+
template <typename B>
245+
static void append(B*& head, B*& tail, B* first, B* last) {
246+
if (!first) {
247+
return;
248+
}
249+
(tail ? tail->next : head) = first;
250+
tail = last;
251+
}
248252

249253
void* allocateSlow(size_t size);
250254

@@ -262,9 +266,14 @@ class Arena {
262266
const Alloc& alloc() const { return allocAndSize_; }
263267

264268
AllocAndSize allocAndSize_;
265-
BlockList blocks_;
266-
typename BlockList::iterator currentBlock_;
267-
LargeBlockList largeBlocks_;
269+
// Blocks are chained through their next pointers. Blocks after
270+
// currentBlock_ are free for reuse; currentBlock_ is null only when there
271+
// are no blocks.
272+
Block* firstBlock_ = nullptr;
273+
Block* lastBlock_ = nullptr;
274+
Block* currentBlock_ = nullptr;
275+
LargeBlock* firstLargeBlock_ = nullptr;
276+
LargeBlock* lastLargeBlock_ = nullptr;
268277
char* ptr_;
269278
char* end_;
270279
size_t totalAllocatedSize_;

‎folly/memory/BUCK‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ fb_dirsync_cpp_library(
2626
],
2727
use_raw_headers = True,
2828
exported_deps = [
29-
"fbsource//third-party/boost:boost",
3029
":allocator",
3130
":malloc",
3231
"//folly:conv",

‎folly/memory/CMakeLists.txt‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ folly_add_library(
3232
Arena-inl.h
3333
Arena.h
3434
EXPORTED_DEPS
35-
Boost::headers
3635
folly_conv
3736
folly_lang_align
3837
folly_lang_checked_math

‎folly/memory/test/ArenaTest.cpp‎

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <glog/logging.h>
2424

25+
#include <folly/Memory.h>
2526
#include <folly/Random.h>
2627
#include <folly/memory/Allocator.h>
2728
#include <folly/memory/MallctlHelper.h>
@@ -86,11 +87,7 @@ TEST(Arena, SizeSanity) {
8687
allocatedItems.insert(ptr);
8788
minimum_size += 10 * requestedBlockSize;
8889
maximum_size +=
89-
goodMallocSize(10 * requestedBlockSize + SysArena::kBlockOverhead);
90-
#if defined(__APPLE__) && defined(FOLLY_AARCH64)
91-
// expectation is a bit too small
92-
maximum_size += 8;
93-
#endif
90+
goodMallocSize(10 * requestedBlockSize + SysArena::kLargeBlockOverhead);
9491
EXPECT_GE(arena.totalSize(), minimum_size);
9592
EXPECT_LE(arena.totalSize(), maximum_size);
9693
VLOG(4) << minimum_size << " < " << arena.totalSize() << " < "
@@ -263,6 +260,70 @@ TEST(Arena, ExtremeSize) {
263260
EXPECT_THROW(alloc(arena, SIZE_MAX - 2), std::bad_alloc);
264261
}
265262

263+
TEST(Arena, SizeLimitCoversLargeBlockHeader) {
264+
static const size_t size = 4096;
265+
266+
for (size_t sizeLimit = size; sizeLimit < size + 64; ++sizeLimit) {
267+
SCOPED_TRACE(sizeLimit);
268+
SysArena arena(64, sizeLimit);
269+
try {
270+
void* ptr = alloc(arena, size);
271+
EXPECT_EQ(
272+
reinterpret_cast<uintptr_t>(ptr) % SysArena::kDefaultMaxAlign, 0);
273+
} catch (const std::bad_alloc&) {
274+
continue;
275+
}
276+
EXPECT_LE(arena.totalSize() - sizeof(SysArena), sizeLimit);
277+
}
278+
}
279+
280+
TEST(Arena, LargeBlockSizeOverflow) {
281+
SysArena arena;
282+
// Adding the large block header to this size overflows.
283+
EXPECT_THROW(arena.allocate(SIZE_MAX - 15), std::bad_alloc);
284+
}
285+
286+
namespace {
287+
// Returns memory at a given offset past a 64-byte boundary, followed by guard
288+
// bytes that must stay untouched.
289+
struct OffsetAllocator {
290+
using value_type = char;
291+
template <typename T>
292+
struct rebind {
293+
using other = OffsetAllocator;
294+
};
295+
static constexpr size_t kGuard = 64;
296+
size_t offset = 0;
297+
298+
char* allocate(size_t n) {
299+
auto p = static_cast<char*>(aligned_malloc(offset + n + kGuard, 64));
300+
memset(p + offset + n, 0x5a, kGuard);
301+
return p + offset;
302+
}
303+
void deallocate(char* p, size_t n) {
304+
for (size_t i = 0; i < kGuard; ++i) {
305+
EXPECT_EQ(p[n + i], 0x5a) << "overrun at " << i;
306+
}
307+
aligned_free(p - offset);
308+
}
309+
};
310+
} // namespace
311+
312+
TEST(Arena, LargeBlockPaddingForCustomAlign) {
313+
// The allocator only guarantees the alignment of the block header, so a
314+
// large block's payload may need up to 64 - that alignment bytes of padding.
315+
for (size_t offset = 0; offset < 64; offset += max_align_v) {
316+
SCOPED_TRACE(offset);
317+
Arena<OffsetAllocator> arena(
318+
OffsetAllocator{offset}, 64, Arena<OffsetAllocator>::kNoSizeLimit, 64);
319+
for (size_t size : {128, 1024}) {
320+
void* ptr = arena.allocate(size);
321+
EXPECT_EQ(reinterpret_cast<uintptr_t>(ptr) % 64, 0);
322+
memset(ptr, 0xff, size);
323+
}
324+
}
325+
}
326+
266327
TEST(Arena, MaxAlign) {
267328
// Results are still aligned with nonstandard size and allocations
268329
static const size_t blockSize = 123;
@@ -381,7 +442,7 @@ TEST(Arena, MergeIntoUntouchedArena) {
381442
auto* live = static_cast<char*>(alloc(src, 1));
382443
*live = 'x';
383444

384-
// dst has never allocated, so its currentBlock_ is still before_begin().
445+
// dst has never allocated, so it has no current block.
385446
SysArena dst;
386447
dst.merge(std::move(src));
387448

‎folly/memory/test/BUCK‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ fb_dirsync_cpp_unittest(
99
labels = ["oss-broken"],
1010
deps = [
1111
"fbsource//third-party/glog:glog",
12+
"//folly:memory",
1213
"//folly:random",
1314
"//folly/memory:allocator",
1415
"//folly/memory:arena",

0 commit comments

Comments
 (0)