Skip to content

Commit 6398d89

Browse files
committed
Add pool coalescing capability to TLSF allocator
This commit implements tlsf_append_pool() to support appending adjacent memory regions to existing pools. This feature reduces fragmentation by allowing memory expansion without creating separate pools.
1 parent 883e9c0 commit 6398d89

File tree

3 files changed

+167
-0
lines changed

3 files changed

+167
-0
lines changed

test.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,59 @@ static void large_size_test(tlsf_t *t)
162162
}
163163
}
164164

165+
static void append_pool_test(tlsf_t *t)
166+
{
167+
printf("Pool append functionality test\n");
168+
169+
/* Simple test: Initial allocation */
170+
void *ptr1 = tlsf_malloc(t, 1000);
171+
assert(ptr1);
172+
173+
size_t initial_size = t->size;
174+
175+
/* Try to append adjacent memory */
176+
void *append_addr = (char *) start_addr + initial_size;
177+
size_t appended = tlsf_append_pool(t, append_addr, 4096);
178+
179+
if (appended > 0) {
180+
printf("Pool append successful: %zu bytes added\n", appended);
181+
182+
/* Test large allocation from expanded pool */
183+
void *large_ptr = tlsf_malloc(t, 3000);
184+
if (large_ptr) {
185+
printf("Large allocation from expanded pool successful\n");
186+
tlsf_free(t, large_ptr);
187+
}
188+
} else {
189+
printf("Pool append not possible (non-adjacent memory)\n");
190+
}
191+
192+
/* Test non-adjacent append (should fail) */
193+
char separate_memory[2048];
194+
size_t non_adjacent =
195+
tlsf_append_pool(t, separate_memory, sizeof(separate_memory));
196+
assert(non_adjacent == 0);
197+
printf("Non-adjacent append correctly rejected\n");
198+
199+
tlsf_free(t, ptr1);
200+
tlsf_check(t);
201+
printf("Pool append test completed\n");
202+
}
203+
165204
int main(void)
166205
{
167206
PAGE = (size_t) sysconf(_SC_PAGESIZE);
168207
MAX_PAGES = 20 * TLSF_MAX_SIZE / PAGE;
169208
tlsf_t t = TLSF_INIT;
170209
srand((unsigned int) time(0));
210+
211+
/* Run existing tests */
171212
large_size_test(&t);
172213
random_sizes_test(&t);
214+
215+
/* Run pool append test */
216+
append_pool_test(&t);
217+
173218
puts("OK!");
174219
return 0;
175220
}

tlsf.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,108 @@ static bool arena_grow(tlsf_t *t, size_t size)
458458
return true;
459459
}
460460

461+
static size_t arena_append_pool(tlsf_t *t, void *mem, size_t size)
462+
{
463+
if (!t->size || !mem || size < 2 * BLOCK_OVERHEAD)
464+
return 0;
465+
466+
/* Align memory block boundaries */
467+
char *start = align_ptr((char *) mem, ALIGN_SIZE);
468+
char *end = (char *) mem + size;
469+
size_t aligned_size = (size_t) (end - start) & ~(ALIGN_SIZE - 1);
470+
471+
if (aligned_size < 2 * BLOCK_OVERHEAD)
472+
return 0;
473+
474+
/* Get current pool information */
475+
void *current_pool_start =
476+
tlsf_resize(t, t->size); /* Ensure current pool is available */
477+
if (!current_pool_start)
478+
return 0;
479+
480+
char *current_pool_end = (char *) current_pool_start + t->size;
481+
482+
/* Only support coalescing if the new memory is immediately adjacent to the
483+
* current pool
484+
*/
485+
if (start != current_pool_end)
486+
return 0;
487+
488+
/* Update the pool size first to include the new memory */
489+
size_t old_size = t->size;
490+
size_t new_total_size = t->size + aligned_size;
491+
492+
/* Try to resize the pool to include the new memory */
493+
void *resized_pool = tlsf_resize(t, new_total_size);
494+
if (!resized_pool)
495+
return 0;
496+
497+
/* Update our pool size */
498+
t->size = new_total_size;
499+
500+
/* Find the current sentinel block */
501+
tlsf_block_t *old_sentinel =
502+
to_block((char *) resized_pool + old_size - 2 * BLOCK_OVERHEAD);
503+
check_sentinel(old_sentinel);
504+
505+
/* Check if the block before the sentinel is free */
506+
tlsf_block_t *last_block = NULL;
507+
if (block_is_prev_free(old_sentinel)) {
508+
last_block = block_prev(old_sentinel);
509+
ASSERT(last_block && block_is_free(last_block),
510+
"last block should be free");
511+
/* Remove the last free block from lists since we'll recreate it */
512+
block_remove(t, last_block);
513+
}
514+
515+
/* Calculate the new free block size */
516+
size_t new_free_size =
517+
aligned_size + BLOCK_OVERHEAD; /* Include old sentinel space */
518+
tlsf_block_t *new_free_block;
519+
520+
if (last_block) {
521+
/* Merge with the existing free block */
522+
new_free_size += block_size(last_block) + BLOCK_OVERHEAD;
523+
new_free_block = last_block;
524+
} else {
525+
/* Convert the old sentinel into the start of the new free block */
526+
new_free_block = old_sentinel;
527+
}
528+
529+
/* Set up the new free block header */
530+
new_free_block->header = new_free_size | BLOCK_BIT_FREE;
531+
532+
/* Set up proper linking for the new free block */
533+
if (!last_block && old_size > 2 * BLOCK_OVERHEAD) {
534+
/* There's a previous block, find it by scanning backwards */
535+
char *scan_start = (char *) old_sentinel - BLOCK_OVERHEAD;
536+
char *pool_start = (char *) resized_pool;
537+
538+
/* Simple backward scan to find the previous block */
539+
for (char *scan_ptr = scan_start; scan_ptr >= pool_start;
540+
scan_ptr -= ALIGN_SIZE) {
541+
tlsf_block_t *candidate = to_block(scan_ptr - BLOCK_OVERHEAD);
542+
if ((char *) candidate >= pool_start &&
543+
(char *) candidate + BLOCK_OVERHEAD + block_size(candidate) ==
544+
(char *) old_sentinel) {
545+
new_free_block->prev = candidate;
546+
block_set_prev_free(new_free_block, block_is_free(candidate));
547+
break;
548+
}
549+
}
550+
}
551+
552+
/* Insert the new free block into the appropriate list */
553+
block_insert(t, new_free_block);
554+
555+
/* Create a new sentinel at the end */
556+
tlsf_block_t *new_sentinel = block_link_next(new_free_block);
557+
new_sentinel->header = BLOCK_BIT_PREV_FREE;
558+
check_sentinel(new_sentinel);
559+
560+
return aligned_size;
561+
}
562+
461563
static void arena_shrink(tlsf_t *t, tlsf_block_t *block)
462564
{
463565
check_sentinel(block_next(block));
@@ -594,6 +696,14 @@ void *tlsf_realloc(tlsf_t *t, void *mem, size_t size)
594696
return mem;
595697
}
596698

699+
size_t tlsf_append_pool(tlsf_t *t, void *mem, size_t size)
700+
{
701+
if (UNLIKELY(!t || !mem || !size))
702+
return 0;
703+
704+
return arena_append_pool(t, mem, size);
705+
}
706+
597707
#ifdef TLSF_ENABLE_CHECK
598708
#include <stdio.h>
599709
#include <stdlib.h>

tlsf.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ typedef struct {
3232
void *tlsf_resize(tlsf_t *, size_t);
3333
void *tlsf_aalloc(tlsf_t *, size_t, size_t);
3434

35+
/**
36+
* Append a memory block to an existing pool, potentially coalescing with
37+
* the last block if it's free. Returns the number of bytes actually used
38+
* from the memory block for pool expansion.
39+
*
40+
* @param tlsf The TLSF allocator instance
41+
* @param mem Pointer to the memory block to append
42+
* @param size Size of the memory block in bytes
43+
* @return Number of bytes used from the memory block, 0 on failure
44+
*/
45+
size_t tlsf_append_pool(tlsf_t *tlsf, void *mem, size_t size);
46+
3547
/**
3648
* Allocates the requested @size bytes of memory and returns a pointer to it.
3749
* On failure, returns NULL.

0 commit comments

Comments
 (0)