Skip to content

Commit 3c05a8c

Browse files
authored
Merge pull request #3482 from cwalther/alloc
Fix inconsistent supervisor heap
2 parents f0b3731 + be8092f commit 3c05a8c

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

supervisor/shared/memory.c

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@
3333

3434
#define CIRCUITPY_SUPERVISOR_ALLOC_COUNT (12)
3535

36+
// Using a zero length to mark an unused allocation makes the code a bit shorter (but makes it
37+
// impossible to support zero-length allocations).
38+
#define FREE 0
39+
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+
3644
static supervisor_allocation allocations[CIRCUITPY_SUPERVISOR_ALLOC_COUNT];
3745
// We use uint32_t* to ensure word (4 byte) alignment.
3846
uint32_t* low_address;
@@ -61,25 +69,31 @@ void free_memory(supervisor_allocation* allocation) {
6169
}
6270
if (allocation->ptr == high_address) {
6371
high_address += allocation->length / 4;
72+
allocation->length = FREE;
6473
for (index++; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index++) {
65-
if (allocations[index].ptr != NULL) {
74+
if (!(allocations[index].length & HOLE)) {
6675
break;
6776
}
77+
// Division automatically shifts out the HOLE bit.
6878
high_address += allocations[index].length / 4;
79+
allocations[index].length = FREE;
6980
}
7081
} else if (allocation->ptr + allocation->length / 4 == low_address) {
7182
low_address = allocation->ptr;
83+
allocation->length = FREE;
7284
for (index--; index >= 0; index--) {
73-
if (allocations[index].ptr != NULL) {
85+
if (!(allocations[index].length & HOLE)) {
7486
break;
7587
}
7688
low_address -= allocations[index].length / 4;
89+
allocations[index].length = FREE;
7790
}
7891
} else {
7992
// Freed memory isn't in the middle so skip updating bounds. The memory will be added to the
80-
// 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;
8196
}
82-
allocation->ptr = NULL;
8397
}
8498

8599
supervisor_allocation* allocation_from_ptr(void *ptr) {
@@ -99,7 +113,7 @@ supervisor_allocation* allocate_remaining_memory(void) {
99113
}
100114

101115
supervisor_allocation* allocate_memory(uint32_t length, bool high) {
102-
if ((high_address - low_address) * 4 < (int32_t) length || length % 4 != 0) {
116+
if (length == 0 || length % 4 != 0) {
103117
return NULL;
104118
}
105119
uint8_t index = 0;
@@ -108,15 +122,21 @@ supervisor_allocation* allocate_memory(uint32_t length, bool high) {
108122
index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1;
109123
direction = -1;
110124
}
125+
supervisor_allocation* alloc;
111126
for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT; index += direction) {
112-
if (allocations[index].ptr == NULL) {
127+
alloc = &allocations[index];
128+
if (alloc->length == FREE && (high_address - low_address) * 4 >= (int32_t) length) {
113129
break;
114130
}
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+
}
115136
}
116137
if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT) {
117138
return NULL;
118139
}
119-
supervisor_allocation* alloc = &allocations[index];
120140
if (high) {
121141
high_address -= length / 4;
122142
alloc->ptr = high_address;

0 commit comments

Comments
 (0)