From 5bdb8c45dd2700941ab75661b773680bdd46aaf1 Mon Sep 17 00:00:00 2001 From: Christian Walther Date: Tue, 29 Sep 2020 22:21:29 +0200 Subject: [PATCH] Allow allocate_memory() to reuse holes when matching exactly. This requires recovering the pointer of the allocation, which could be done by adding up neighbor lengths, but the simpler way is to stop NULLing it out in the first place and instead mark an allocation as freed by the client by setting the lowest bit of the length (which is always zero in a valid length). --- supervisor/shared/memory.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/supervisor/shared/memory.c b/supervisor/shared/memory.c index 56c617b9cf..fdb4e06fbb 100755 --- a/supervisor/shared/memory.c +++ b/supervisor/shared/memory.c @@ -37,6 +37,10 @@ // impossible to support zero-length allocations). #define FREE 0 +// The lowest two bits of a valid length are always zero, so we can use them to mark an allocation +// as freed by the client but not yet reclaimed into the FREE middle. +#define HOLE 1 + static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT]; // We use uint32_t* to ensure word (4 byte) alignment. uint32_t* low_address; @@ -67,9 +71,10 @@ void free_memory(supervisor_allocation* allocation) { high_address += allocation->length / 4; allocation->length = FREE; for (index++; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) { - if (allocations[index].ptr != NULL) { + if (!(allocations[index].length & HOLE)) { break; } + // Division automatically shifts out the HOLE bit. high_address += allocations[index].length / 4; allocations[index].length = FREE; } @@ -77,7 +82,7 @@ void free_memory(supervisor_allocation* allocation) { low_address = allocation->ptr; allocation->length = FREE; for (index--; index >= 0; index--) { - if (allocations[index].ptr != NULL) { + if (!(allocations[index].length & HOLE)) { break; } low_address -= allocations[index].length / 4; @@ -85,9 +90,10 @@ void free_memory(supervisor_allocation* allocation) { } } else { // Freed memory isn't in the middle so skip updating bounds. The memory will be added to the - // middle when the memory to the inside is freed. + // middle when the memory to the inside is freed. We still need its length, but setting + // only the lowest bit is nondestructive. + allocation->length |= HOLE; } - allocation->ptr = NULL; } supervisor_allocation* allocation_from_ptr(void *ptr) { @@ -107,7 +113,7 @@ supervisor_allocation* allocate_remaining_memory(void) { } supervisor_allocation* allocate_memory(uint32_t length, bool high) { - if (length == 0 || (high_address - low_address) * 4 < (int32_t) length || length % 4 != 0) { + if (length == 0 || length % 4 != 0) { return NULL; } uint8_t index = 0; @@ -116,15 +122,21 @@ supervisor_allocation* allocate_memory(uint32_t length, bool high) { index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1; direction = -1; } + supervisor_allocation* alloc; for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index += direction) { - if (allocations[index].length == FREE) { + alloc = &allocations[index]; + if (alloc->length == FREE) { break; } + // If a hole matches in length exactly, we can reuse it. + if (alloc->length == (length | HOLE)) { + alloc->length = length; + return alloc; + } } - if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT) { + if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT || (high_address - low_address) * 4 < (int32_t) length) { return NULL; } - supervisor_allocation* alloc = &allocations[index]; if (high) { high_address -= length / 4; alloc->ptr = high_address;