lib/os/heap: fix realloc issues
If the new size amounts to the same number of chunks then: - If right-chunk is used then we needlessly allocate new memory and copy data over. - If right-chunk is free then we attempt to split it with a zero size which corrupts the prev/next list. Make sure this case is properly handled and add a test for it. While at it, let's simplify the code somewhat as well. Signed-off-by: Nicolas Pitre <npitre@baylibre.com>
This commit is contained in:
parent
bb18fa0b35
commit
aeda6ccdd8
2 changed files with 23 additions and 11 deletions
|
@ -311,7 +311,10 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
|
|||
chunkid_t rc = right_chunk(h, c);
|
||||
size_t chunks_need = bytes_to_chunksz(h, bytes);
|
||||
|
||||
if (chunk_size(h, c) > chunks_need) {
|
||||
if (chunk_size(h, c) == chunks_need) {
|
||||
/* We're good already */
|
||||
return ptr;
|
||||
} else if (chunk_size(h, c) > chunks_need) {
|
||||
/* Shrink in place, split off and free unused suffix */
|
||||
split_chunks(h, c, c + chunks_need);
|
||||
set_chunk_used(h, c, true);
|
||||
|
@ -323,20 +326,15 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
|
|||
chunkid_t split_size = chunks_need - chunk_size(h, c);
|
||||
|
||||
free_list_remove(h, rc);
|
||||
|
||||
if (split_size < chunk_size(h, rc)) {
|
||||
split_chunks(h, rc, rc + split_size);
|
||||
free_list_add(h, rc + split_size);
|
||||
}
|
||||
|
||||
chunkid_t newsz = chunk_size(h, c) + split_size;
|
||||
|
||||
set_chunk_size(h, c, newsz);
|
||||
merge_chunks(h, c, rc);
|
||||
set_chunk_used(h, c, true);
|
||||
set_left_chunk_size(h, c + newsz, newsz);
|
||||
|
||||
CHECK(chunk_used(h, c));
|
||||
|
||||
return chunk_mem(h, c);
|
||||
return ptr;
|
||||
} else {
|
||||
/* Reallocate and copy */
|
||||
void *ptr2 = sys_heap_alloc(heap, bytes);
|
||||
|
@ -345,8 +343,7 @@ void *sys_heap_realloc(struct sys_heap *heap, void *ptr, size_t bytes)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(ptr2, ptr,
|
||||
chunk_size(h, c) * CHUNK_UNIT - chunk_header_bytes(h));
|
||||
memcpy(ptr2, ptr, bytes);
|
||||
sys_heap_free(heap, ptr);
|
||||
return ptr2;
|
||||
}
|
||||
|
|
|
@ -275,6 +275,21 @@ static void test_realloc(void)
|
|||
"Realloc should have shrunk in place %p -> %p",
|
||||
p1, p2);
|
||||
zassert_true(realloc_check_block(p2, p1, 64), "data changed");
|
||||
|
||||
/* Allocate two blocks, then expand the first within a chunk.
|
||||
* validate that it doesn't move. We assume CHUNK_UNIT == 8.
|
||||
*/
|
||||
p1 = sys_heap_alloc(&heap, 61);
|
||||
realloc_fill_block(p1, 61);
|
||||
p2 = sys_heap_alloc(&heap, 80);
|
||||
realloc_fill_block(p2, 80);
|
||||
p3 = sys_heap_realloc(&heap, p1, 64);
|
||||
|
||||
zassert_true(sys_heap_validate(&heap), "invalid heap");
|
||||
zassert_true(p1 == p3,
|
||||
"Realloc should have expanded in place %p -> %p",
|
||||
p1, p3);
|
||||
zassert_true(realloc_check_block(p3, p1, 61), "data changed");
|
||||
}
|
||||
|
||||
void test_main(void)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue