Skip to content

Commit 11ef588

Browse files
committed
Add sbrk_aligned() to avoid pessimistic sbrk()ing.
1 parent b432152 commit 11ef588

File tree

2 files changed

+23
-33
lines changed

2 files changed

+23
-33
lines changed

system/lib/emmalloc.c

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -478,25 +478,20 @@ int emmalloc_validate_memory_regions()
478478
return memoryError;
479479
}
480480

481-
static bool claim_more_memory(size_t numBytes)
481+
void *sbrk_aligned(uintptr_t alignment, intptr_t increment);
482+
483+
static bool claim_more_memory(size_t alignment, size_t numBytes)
482484
{
483485
#ifdef EMMALLOC_VERBOSE
484-
MAIN_THREAD_ASYNC_EM_ASM(out('claim_more_memory(numBytes='+Number($0)+ ')'), numBytes);
486+
MAIN_THREAD_ASYNC_EM_ASM(out('claim_more_memory(alignment='+Number($0)+', numBytes='+Number($1)+ ')'), alignment, numBytes);
485487
#endif
486488

487489
#ifdef EMMALLOC_MEMVALIDATE
488490
validate_memory_regions();
489491
#endif
490492

491-
// Make sure we always send sbrk requests with the same alignment that sbrk()
492-
// allocates memory at. Otherwise we will not properly interpret returned memory
493-
// to form a seamlessly contiguous region with earlier root regions, which would
494-
// lead to inefficiently treating the sbrk()ed region to be a new disjoint root
495-
// region.
496-
numBytes = (size_t)ALIGN_UP(numBytes, MALLOC_ALIGNMENT);
497-
498493
// Claim memory via sbrk
499-
uint8_t *startPtr = (uint8_t*)sbrk(numBytes);
494+
uint8_t *startPtr = (uint8_t*)sbrk_aligned(alignment, numBytes);
500495
if ((intptr_t)startPtr == -1)
501496
{
502497
#ifdef EMMALLOC_VERBOSE
@@ -508,7 +503,8 @@ static bool claim_more_memory(size_t numBytes)
508503
MAIN_THREAD_ASYNC_EM_ASM(out('claim_more_memory: claimed ' + ptrToString($0) + ' - ' + ptrToString($1) + ' (' + Number($2) + ' bytes) via sbrk()'), startPtr, startPtr + numBytes, numBytes);
509504
#endif
510505
assert(HAS_ALIGNMENT(startPtr, alignof(size_t)));
511-
uint8_t *endPtr = startPtr + numBytes;
506+
uint8_t *endPtr = ALIGN_UP(startPtr, alignment) + numBytes;
507+
assert((uintptr_t)sbrk(0) >= (uintptr_t)endPtr);
512508

513509
// Create a sentinel region at the end of the new heap block
514510
Region *endSentinelRegion = (Region*)(endPtr - sizeof(Region));
@@ -585,7 +581,7 @@ static void initialize_emmalloc_heap()
585581
#endif
586582

587583
// Start with a tiny dynamic region.
588-
claim_more_memory(3*sizeof(Region));
584+
claim_more_memory(MALLOC_ALIGNMENT, 3*sizeof(Region));
589585
}
590586

591587
void emmalloc_blank_slate_from_orbit()
@@ -810,24 +806,8 @@ static void *allocate_memory(size_t alignment, size_t size)
810806

811807
// We were unable to find a free memory region. Must sbrk() in more memory!
812808
size_t numBytesToClaim = size+sizeof(Region)*3;
813-
// Take into account the alignment as well. For typical alignment we don't
814-
// need to add anything here (so we do nothing if the alignment is equal to
815-
// MALLOC_ALIGNMENT), but it can matter if the alignment is very high. In that
816-
// case, not adding the alignment can lead to this sbrk not giving us enough
817-
// (in which case, the next attempt fails and will sbrk the same amount again,
818-
// potentially allocating a lot more memory than necessary).
819-
//
820-
// Note that this is not necessarily optimal, as the extra allocation size for
821-
// the alignment might not be needed (if we are lucky and already aligned),
822-
// and even if it helps us allocate it will not immediately be ready for reuse
823-
// (as it will be added to the currently-in-use region before us, so it is not
824-
// in a free list). As a compromise however it seems reasonable in practice as
825-
// a way to handle large aligned regions to avoid even worse waste.
826-
if (alignment > MALLOC_ALIGNMENT) {
827-
numBytesToClaim += alignment;
828-
}
829809
assert(numBytesToClaim > size); // 32-bit wraparound should not happen here, allocation size has been validated above!
830-
bool success = claim_more_memory(numBytesToClaim);
810+
bool success = claim_more_memory(alignment, numBytesToClaim);
831811
if (success)
832812
return allocate_memory(alignment, size); // Recurse back to itself to try again
833813

system/lib/libc/sbrk.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,17 @@ uintptr_t* emscripten_get_sbrk_ptr() {
5656
// Enforce preserving a minimal alignof(maxalign_t) alignment for sbrk.
5757
#define SBRK_ALIGNMENT (__alignof__(max_align_t))
5858

59-
void *sbrk(intptr_t increment_) {
59+
#define ALIGN_UP(ptr, alignment) ((uintptr_t)((((uintptr_t)(ptr)) + ((alignment)-1)) & ~((alignment)-1)))
60+
61+
// First bumps sbrk() up to the given alignment, and then by the given increment.
62+
// Returns old brk pointer. Note that the old brk pointer might not be aligned
63+
// to the requested alignment, but the caller is guaranteed that they can round
64+
// up the returned pointer to the alignment they requested, and the given
65+
// increment will fit. That is, ROUND_UP(old_brk, alignment) + increment == new_brk.
66+
void *sbrk_aligned(uintptr_t alignment, intptr_t increment_) {
6067
uintptr_t old_size;
6168
uintptr_t increment = (uintptr_t)increment_;
62-
increment = (increment + (SBRK_ALIGNMENT-1)) & ~(SBRK_ALIGNMENT-1);
69+
uintptr_t* sbrk_ptr = emscripten_get_sbrk_ptr();
6370
#ifdef __EMSCRIPTEN_SHARED_MEMORY__
6471
// Our default dlmalloc uses locks around each malloc/free, so no additional
6572
// work is necessary to keep things threadsafe, but we also make sure sbrk
@@ -68,13 +75,12 @@ void *sbrk(intptr_t increment_) {
6875
uintptr_t expected;
6976
while (1) {
7077
#endif // __EMSCRIPTEN_SHARED_MEMORY__
71-
uintptr_t* sbrk_ptr = emscripten_get_sbrk_ptr();
7278
#ifdef __EMSCRIPTEN_SHARED_MEMORY__
7379
uintptr_t old_brk = __c11_atomic_load((_Atomic(uintptr_t)*)sbrk_ptr, __ATOMIC_SEQ_CST);
7480
#else
7581
uintptr_t old_brk = *sbrk_ptr;
7682
#endif
77-
uintptr_t new_brk = old_brk + increment;
83+
uintptr_t new_brk = ALIGN_UP(old_brk, alignment) + increment;
7884
// Check for an overflow, which would indicate that we are trying to
7985
// allocate over maximum addressable memory.
8086
if (increment > 0 && new_brk <= old_brk) {
@@ -117,6 +123,10 @@ void *sbrk(intptr_t increment_) {
117123
return (void*)-1;
118124
}
119125

126+
void *sbrk(intptr_t increment_) {
127+
return sbrk_aligned(SBRK_ALIGNMENT, ALIGN_UP(increment_, SBRK_ALIGNMENT));
128+
}
129+
120130
int brk(void* ptr) {
121131
#ifdef __EMSCRIPTEN_SHARED_MEMORY__
122132
// FIXME

0 commit comments

Comments
 (0)