kopia lustrzana https://github.com/micropython/micropython
gc: "new" gc_realloc: Rewrite in plain C, fixing bunch of bugs.
There were typos, various rounding errors trying to do concurrent counting in bytes vs blocks, complex conditional paths, superfluous variables, etc., etc., all leading to obscure segfaults.pull/511/merge
rodzic
ed162b5ef2
commit
c86889dafb
63
py/gc.c
63
py/gc.c
|
@ -1,3 +1,4 @@
|
||||||
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
@ -459,16 +460,18 @@ void *gc_realloc(void *ptr_in, machine_uint_t n_bytes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void *ptr_out = NULL;
|
void *ptr_out = NULL;
|
||||||
machine_uint_t block = 0;
|
|
||||||
machine_uint_t ptr = (machine_uint_t)ptr_in;
|
machine_uint_t ptr = (machine_uint_t)ptr_in;
|
||||||
|
|
||||||
if (ptr_in == NULL) {
|
if (ptr_in == NULL) {
|
||||||
return gc_alloc(n_bytes, false);
|
return gc_alloc(n_bytes, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (VERIFY_PTR(ptr) // verify pointer
|
machine_uint_t new_blocks = (n_bytes + BYTES_PER_BLOCK) / BYTES_PER_BLOCK;
|
||||||
&& (block = BLOCK_FROM_PTR(ptr)) // get first block
|
// get first block
|
||||||
&& ATB_GET_KIND(block) == AT_HEAD) { // make sure it's a HEAD block
|
machine_uint_t block = BLOCK_FROM_PTR(ptr);
|
||||||
|
|
||||||
|
// Sabity checks
|
||||||
|
if (VERIFY_PTR(ptr) && ATB_GET_KIND(block) == AT_HEAD) {
|
||||||
|
|
||||||
byte block_type;
|
byte block_type;
|
||||||
machine_uint_t n_free = 0;
|
machine_uint_t n_free = 0;
|
||||||
|
@ -478,42 +481,39 @@ void *gc_realloc(void *ptr_in, machine_uint_t n_bytes) {
|
||||||
// get the number of consecutive tail blocks and
|
// get the number of consecutive tail blocks and
|
||||||
// the number of free blocks after last tail block
|
// the number of free blocks after last tail block
|
||||||
// stop if we reach (or are at) end of heap
|
// stop if we reach (or are at) end of heap
|
||||||
while ((block + n_blocks + n_free) < max_block
|
while (block + n_blocks + n_free < max_block) {
|
||||||
|
if (n_blocks + n_free >= new_blocks) {
|
||||||
// stop as soon as we find enough blocks for n_bytes
|
// stop as soon as we find enough blocks for n_bytes
|
||||||
&& (n_bytes > ((n_blocks+n_free) * BYTES_PER_BLOCK))
|
break;
|
||||||
// stop if block is HEAD
|
|
||||||
&& (block_type = ATB_GET_KIND(block + n_blocks + n_free)) != AT_HEAD) {
|
|
||||||
switch (block_type) {
|
|
||||||
case AT_FREE: n_free++; break;
|
|
||||||
case AT_TAIL: n_blocks++; break;
|
|
||||||
default: break;
|
|
||||||
}
|
}
|
||||||
|
block_type = ATB_GET_KIND(block + n_blocks + n_free);
|
||||||
|
switch (block_type) {
|
||||||
|
case AT_FREE: n_free++; continue;
|
||||||
|
case AT_TAIL: n_blocks++; continue;
|
||||||
|
case AT_MARK: assert(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// number of allocated bytes
|
|
||||||
machine_uint_t n_existing = n_blocks * BYTES_PER_BLOCK;
|
|
||||||
|
|
||||||
// check if realloc'ing to a smaller size
|
if (new_blocks == n_blocks) {
|
||||||
if (n_bytes <= n_existing) {
|
return ptr_in;
|
||||||
ptr_out = ptr_in;
|
}
|
||||||
|
|
||||||
|
if (new_blocks < n_blocks) {
|
||||||
// free unneeded tail blocks
|
// free unneeded tail blocks
|
||||||
for (machine_uint_t bl = block + n_blocks; ATB_GET_KIND(bl) == AT_TAIL; bl++) {
|
for (machine_uint_t bl = block + new_blocks; ATB_GET_KIND(bl) == AT_TAIL; bl++) {
|
||||||
ATB_ANY_TO_FREE(bl);
|
ATB_ANY_TO_FREE(bl);
|
||||||
}
|
}
|
||||||
|
return ptr_in;
|
||||||
|
|
||||||
// check if we can expand in place
|
// check if we can expand in place
|
||||||
} else if (n_bytes <= (n_existing + (n_free * BYTES_PER_BLOCK))) {
|
} else if (new_blocks <= n_blocks + n_free) {
|
||||||
// number of blocks needed to expand +1 if there's a remainder
|
// mark few more blocks as used tail
|
||||||
machine_uint_t n_diff = ( n_bytes - n_existing)/BYTES_PER_BLOCK+
|
for (machine_uint_t bl = block + n_blocks; bl < block + new_blocks; bl++) {
|
||||||
((n_bytes - n_existing)%BYTES_PER_BLOCK!=0);
|
assert(ATB_GET_KIND(bl) == AT_FREE);
|
||||||
|
|
||||||
DEBUG_printf("gc_realloc: expanding " UINT_FMT " blocks (" UINT_FMT " bytes) to " UINT_FMT " blocks (" UINT_FMT " bytes)\n",
|
|
||||||
n_existing/BYTES_PER_BLOCK, n_existing, n_existing/BYTES_PER_BLOCK+n_diff, n_existing + n_diff*BYTES_PER_BLOCK);
|
|
||||||
|
|
||||||
// mark rest of blocks as used tail
|
|
||||||
for (machine_uint_t bl = block + n_blocks; bl < (block + n_blocks + n_diff); bl++) {
|
|
||||||
ATB_FREE_TO_TAIL(bl);
|
ATB_FREE_TO_TAIL(bl);
|
||||||
}
|
}
|
||||||
ptr_out = ptr_in;
|
return ptr_in;
|
||||||
|
|
||||||
// try to find a new contiguous chain
|
// try to find a new contiguous chain
|
||||||
} else if ((ptr_out = gc_alloc(n_bytes,
|
} else if ((ptr_out = gc_alloc(n_bytes,
|
||||||
|
@ -524,12 +524,13 @@ void *gc_realloc(void *ptr_in, machine_uint_t n_bytes) {
|
||||||
#endif
|
#endif
|
||||||
)) != NULL) {
|
)) != NULL) {
|
||||||
DEBUG_printf("gc_realloc: allocating new block\n");
|
DEBUG_printf("gc_realloc: allocating new block\n");
|
||||||
memcpy(ptr_out, ptr_in, n_existing);
|
memcpy(ptr_out, ptr_in, n_blocks * BYTES_PER_BLOCK);
|
||||||
gc_free(ptr_in);
|
gc_free(ptr_in);
|
||||||
|
return ptr_out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr_out;
|
return NULL;
|
||||||
}
|
}
|
||||||
#endif // Alternative gc_realloc impl
|
#endif // Alternative gc_realloc impl
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue