Skip to content

Commit 009574d

Browse files
committed
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).
1 parent bb6e4cf commit 009574d

File tree

1 file changed

+20
-8
lines changed

1 file changed

+20
-8
lines changed

supervisor/shared/memory.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
// impossible to support zero-length allocations).
3838
#define FREE 0
3939

40+
// The lowest two bits of a valid length are always zero, so we can use them to mark an allocation
41+
// as freed by the client but not yet reclaimed into the FREE middle.
42+
#define HOLE 1
43+
4044
static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
4145
// We use uint32_t* to ensure word (4 byte) alignment.
4246
uint32_t* low_address;
@@ -67,27 +71,29 @@ void free_memory(supervisor_allocation* allocation) {
6771
high_address += allocation->length / 4;
6872
allocation->length = FREE;
6973
for (index++; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
70-
if (allocations[index].ptr != NULL) {
74+
if (!(allocations[index].length & HOLE)) {
7175
break;
7276
}
77+
// Division automatically shifts out the HOLE bit.
7378
high_address += allocations[index].length / 4;
7479
allocations[index].length = FREE;
7580
}
7681
} else if (allocation->ptr + allocation->length / 4 == low_address) {
7782
low_address = allocation->ptr;
7883
allocation->length = FREE;
7984
for (index--; index >= 0; index--) {
80-
if (allocations[index].ptr != NULL) {
85+
if (!(allocations[index].length & HOLE)) {
8186
break;
8287
}
8388
low_address -= allocations[index].length / 4;
8489
allocations[index].length = FREE;
8590
}
8691
} else {
8792
// Freed memory isn't in the middle so skip updating bounds. The memory will be added to the
88-
// middle when the memory to the inside is freed.
93+
// middle when the memory to the inside is freed. We still need its length, but setting
94+
// only the lowest bit is nondestructive.
95+
allocation->length |= HOLE;
8996
}
90-
allocation->ptr = NULL;
9197
}
9298

9399
supervisor_allocation* allocation_from_ptr(void *ptr) {
@@ -107,7 +113,7 @@ supervisor_allocation* allocate_remaining_memory(void) {
107113
}
108114

109115
supervisor_allocation* allocate_memory(uint32_t length, bool high) {
110-
if (length == 0 || (high_address - low_address) * 4 < (int32_t) length || length % 4 != 0) {
116+
if (length == 0 || length % 4 != 0) {
111117
return NULL;
112118
}
113119
uint8_t index = 0;
@@ -116,15 +122,21 @@ supervisor_allocation* allocate_memory(uint32_t length, bool high) {
116122
index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1;
117123
direction = -1;
118124
}
125+
supervisor_allocation* alloc;
119126
for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index += direction) {
120-
if (allocations[index].length == FREE) {
127+
alloc = &allocations[index];
128+
if (alloc->length == FREE) {
121129
break;
122130
}
131+
// If a hole matches in length exactly, we can reuse it.
132+
if (alloc->length == (length | HOLE)) {
133+
alloc->length = length;
134+
return alloc;
135+
}
123136
}
124-
if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT) {
137+
if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT || (high_address - low_address) * 4 < (int32_t) length) {
125138
return NULL;
126139
}
127-
supervisor_allocation* alloc = &allocations[index];
128140
if (high) {
129141
high_address -= length / 4;
130142
alloc->ptr = high_address;

0 commit comments

Comments
 (0)