@ -94,6 +94,14 @@
# define FTB_CLEAR(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0)
# define FTB_CLEAR(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0)
# endif
# endif
# if MICROPY_PY_THREAD
# define GC_ENTER() mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1)
# define GC_EXIT() mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex))
# else
# define GC_ENTER()
# define GC_EXIT()
# endif
// TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool
// TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool
void gc_init ( void * start , void * end ) {
void gc_init ( void * start , void * end ) {
// align end pointer on block boundary
// align end pointer on block boundary
@ -144,6 +152,10 @@ void gc_init(void *start, void *end) {
// allow auto collection
// allow auto collection
MP_STATE_MEM ( gc_auto_collect_enabled ) = 1 ;
MP_STATE_MEM ( gc_auto_collect_enabled ) = 1 ;
# if MICROPY_PY_THREAD
mp_thread_mutex_init ( & MP_STATE_MEM ( gc_mutex ) ) ;
# endif
DEBUG_printf ( " GC layout: \n " ) ;
DEBUG_printf ( " GC layout: \n " ) ;
DEBUG_printf ( " alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks \n " , MP_STATE_MEM ( gc_alloc_table_start ) , MP_STATE_MEM ( gc_alloc_table_byte_len ) , MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ) ;
DEBUG_printf ( " alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks \n " , MP_STATE_MEM ( gc_alloc_table_start ) , MP_STATE_MEM ( gc_alloc_table_byte_len ) , MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ) ;
# if MICROPY_ENABLE_FINALISER
# if MICROPY_ENABLE_FINALISER
@ -153,11 +165,15 @@ void gc_init(void *start, void *end) {
}
}
void gc_lock ( void ) {
void gc_lock ( void ) {
GC_ENTER ( ) ;
MP_STATE_MEM ( gc_lock_depth ) + + ;
MP_STATE_MEM ( gc_lock_depth ) + + ;
GC_EXIT ( ) ;
}
}
void gc_unlock ( void ) {
void gc_unlock ( void ) {
GC_ENTER ( ) ;
MP_STATE_MEM ( gc_lock_depth ) - - ;
MP_STATE_MEM ( gc_lock_depth ) - - ;
GC_EXIT ( ) ;
}
}
bool gc_is_locked ( void ) {
bool gc_is_locked ( void ) {
@ -236,6 +252,10 @@ STATIC void gc_sweep(void) {
case AT_HEAD :
case AT_HEAD :
# if MICROPY_ENABLE_FINALISER
# if MICROPY_ENABLE_FINALISER
if ( FTB_GET ( block ) ) {
if ( FTB_GET ( block ) ) {
# if MICROPY_PY_THREAD
// TODO need to think about reentrancy with finaliser code
assert ( ! " finaliser with threading not implemented " ) ;
# endif
mp_obj_base_t * obj = ( mp_obj_base_t * ) PTR_FROM_BLOCK ( block ) ;
mp_obj_base_t * obj = ( mp_obj_base_t * ) PTR_FROM_BLOCK ( block ) ;
if ( obj - > type ! = NULL ) {
if ( obj - > type ! = NULL ) {
// if the object has a type then see if it has a __del__ method
// if the object has a type then see if it has a __del__ method
@ -272,7 +292,8 @@ STATIC void gc_sweep(void) {
}
}
void gc_collect_start ( void ) {
void gc_collect_start ( void ) {
gc_lock ( ) ;
GC_ENTER ( ) ;
MP_STATE_MEM ( gc_lock_depth ) + + ;
MP_STATE_MEM ( gc_stack_overflow ) = 0 ;
MP_STATE_MEM ( gc_stack_overflow ) = 0 ;
MP_STATE_MEM ( gc_sp ) = MP_STATE_MEM ( gc_stack ) ;
MP_STATE_MEM ( gc_sp ) = MP_STATE_MEM ( gc_stack ) ;
// Trace root pointers. This relies on the root pointers being organised
// Trace root pointers. This relies on the root pointers being organised
@ -294,10 +315,12 @@ void gc_collect_end(void) {
gc_deal_with_stack_overflow ( ) ;
gc_deal_with_stack_overflow ( ) ;
gc_sweep ( ) ;
gc_sweep ( ) ;
MP_STATE_MEM ( gc_last_free_atb_index ) = 0 ;
MP_STATE_MEM ( gc_last_free_atb_index ) = 0 ;
gc_unlock ( ) ;
MP_STATE_MEM ( gc_lock_depth ) - - ;
GC_EXIT ( ) ;
}
}
void gc_info ( gc_info_t * info ) {
void gc_info ( gc_info_t * info ) {
GC_ENTER ( ) ;
info - > total = MP_STATE_MEM ( gc_pool_end ) - MP_STATE_MEM ( gc_pool_start ) ;
info - > total = MP_STATE_MEM ( gc_pool_end ) - MP_STATE_MEM ( gc_pool_start ) ;
info - > used = 0 ;
info - > used = 0 ;
info - > free = 0 ;
info - > free = 0 ;
@ -340,19 +363,23 @@ void gc_info(gc_info_t *info) {
info - > used * = BYTES_PER_BLOCK ;
info - > used * = BYTES_PER_BLOCK ;
info - > free * = BYTES_PER_BLOCK ;
info - > free * = BYTES_PER_BLOCK ;
GC_EXIT ( ) ;
}
}
void * gc_alloc ( size_t n_bytes , bool has_finaliser ) {
void * gc_alloc ( size_t n_bytes , bool has_finaliser ) {
size_t n_blocks = ( ( n_bytes + BYTES_PER_BLOCK - 1 ) & ( ~ ( BYTES_PER_BLOCK - 1 ) ) ) / BYTES_PER_BLOCK ;
size_t n_blocks = ( ( n_bytes + BYTES_PER_BLOCK - 1 ) & ( ~ ( BYTES_PER_BLOCK - 1 ) ) ) / BYTES_PER_BLOCK ;
DEBUG_printf ( " gc_alloc( " UINT_FMT " bytes -> " UINT_FMT " blocks) \n " , n_bytes , n_blocks ) ;
DEBUG_printf ( " gc_alloc( " UINT_FMT " bytes -> " UINT_FMT " blocks) \n " , n_bytes , n_blocks ) ;
// check if GC is locked
// check for 0 allocation
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
if ( n_blocks = = 0 ) {
return NULL ;
return NULL ;
}
}
// check for 0 allocation
GC_ENTER ( ) ;
if ( n_blocks = = 0 ) {
// check if GC is locked
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
GC_EXIT ( ) ;
return NULL ;
return NULL ;
}
}
@ -372,6 +399,7 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser) {
if ( ATB_3_IS_FREE ( a ) ) { if ( + + n_free > = n_blocks ) { i = i * BLOCKS_PER_ATB + 3 ; goto found ; } } else { n_free = 0 ; }
if ( ATB_3_IS_FREE ( a ) ) { if ( + + n_free > = n_blocks ) { i = i * BLOCKS_PER_ATB + 3 ; goto found ; } } else { n_free = 0 ; }
}
}
GC_EXIT ( ) ;
// nothing found!
// nothing found!
if ( collected ) {
if ( collected ) {
return NULL ;
return NULL ;
@ -379,6 +407,7 @@ void *gc_alloc(size_t n_bytes, bool has_finaliser) {
DEBUG_printf ( " gc_alloc( " UINT_FMT " ): no free mem, triggering GC \n " , n_bytes ) ;
DEBUG_printf ( " gc_alloc( " UINT_FMT " ): no free mem, triggering GC \n " , n_bytes ) ;
gc_collect ( ) ;
gc_collect ( ) ;
collected = 1 ;
collected = 1 ;
GC_ENTER ( ) ;
}
}
// found, ending at block i inclusive
// found, ending at block i inclusive
@ -405,6 +434,8 @@ found:
ATB_FREE_TO_TAIL ( bl ) ;
ATB_FREE_TO_TAIL ( bl ) ;
}
}
GC_EXIT ( ) ;
// get pointer to first block
// get pointer to first block
void * ret_ptr = ( void * ) ( MP_STATE_MEM ( gc_pool_start ) + start_block * BYTES_PER_BLOCK ) ;
void * ret_ptr = ( void * ) ( MP_STATE_MEM ( gc_pool_start ) + start_block * BYTES_PER_BLOCK ) ;
DEBUG_printf ( " gc_alloc(%p) \n " , ret_ptr ) ;
DEBUG_printf ( " gc_alloc(%p) \n " , ret_ptr ) ;
@ -421,7 +452,9 @@ found:
// clear type pointer in case it is never set
// clear type pointer in case it is never set
( ( mp_obj_base_t * ) ret_ptr ) - > type = NULL ;
( ( mp_obj_base_t * ) ret_ptr ) - > type = NULL ;
// set mp_obj flag only if it has a finaliser
// set mp_obj flag only if it has a finaliser
GC_ENTER ( ) ;
FTB_SET ( start_block ) ;
FTB_SET ( start_block ) ;
GC_EXIT ( ) ;
}
}
# else
# else
( void ) has_finaliser ;
( void ) has_finaliser ;
@ -447,8 +480,10 @@ void *gc_alloc_with_finaliser(mp_uint_t n_bytes) {
// force the freeing of a piece of memory
// force the freeing of a piece of memory
// TODO: freeing here does not call finaliser
// TODO: freeing here does not call finaliser
void gc_free ( void * ptr ) {
void gc_free ( void * ptr ) {
GC_ENTER ( ) ;
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
// TODO how to deal with this error?
// TODO how to deal with this error?
GC_EXIT ( ) ;
return ;
return ;
}
}
@ -471,18 +506,25 @@ void gc_free(void *ptr) {
block + = 1 ;
block + = 1 ;
} while ( ATB_GET_KIND ( block ) = = AT_TAIL ) ;
} while ( ATB_GET_KIND ( block ) = = AT_TAIL ) ;
GC_EXIT ( ) ;
# if EXTENSIVE_HEAP_PROFILING
# if EXTENSIVE_HEAP_PROFILING
gc_dump_alloc_table ( ) ;
gc_dump_alloc_table ( ) ;
# endif
# endif
} else {
} else {
GC_EXIT ( ) ;
assert ( ! " bad free " ) ;
assert ( ! " bad free " ) ;
}
}
} else if ( ptr ! = NULL ) {
} else if ( ptr ! = NULL ) {
GC_EXIT ( ) ;
assert ( ! " bad free " ) ;
assert ( ! " bad free " ) ;
} else {
GC_EXIT ( ) ;
}
}
}
}
size_t gc_nbytes ( const void * ptr ) {
size_t gc_nbytes ( const void * ptr ) {
GC_ENTER ( ) ;
if ( VERIFY_PTR ( ptr ) ) {
if ( VERIFY_PTR ( ptr ) ) {
size_t block = BLOCK_FROM_PTR ( ptr ) ;
size_t block = BLOCK_FROM_PTR ( ptr ) ;
if ( ATB_GET_KIND ( block ) = = AT_HEAD ) {
if ( ATB_GET_KIND ( block ) = = AT_HEAD ) {
@ -491,11 +533,13 @@ size_t gc_nbytes(const void *ptr) {
do {
do {
n_blocks + = 1 ;
n_blocks + = 1 ;
} while ( ATB_GET_KIND ( block + n_blocks ) = = AT_TAIL ) ;
} while ( ATB_GET_KIND ( block + n_blocks ) = = AT_TAIL ) ;
GC_EXIT ( ) ;
return n_blocks * BYTES_PER_BLOCK ;
return n_blocks * BYTES_PER_BLOCK ;
}
}
}
}
// invalid pointer
// invalid pointer
GC_EXIT ( ) ;
return 0 ;
return 0 ;
}
}
@ -529,10 +573,6 @@ void *gc_realloc(void *ptr, mp_uint_t n_bytes) {
# else // Alternative gc_realloc impl
# else // Alternative gc_realloc impl
void * gc_realloc ( void * ptr_in , size_t n_bytes , bool allow_move ) {
void * gc_realloc ( void * ptr_in , size_t n_bytes , bool allow_move ) {
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
return NULL ;
}
// check for pure allocation
// check for pure allocation
if ( ptr_in = = NULL ) {
if ( ptr_in = = NULL ) {
return gc_alloc ( n_bytes , false ) ;
return gc_alloc ( n_bytes , false ) ;
@ -559,6 +599,13 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
return NULL ;
return NULL ;
}
}
GC_ENTER ( ) ;
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
GC_EXIT ( ) ;
return NULL ;
}
// compute number of new blocks that are requested
// compute number of new blocks that are requested
size_t new_blocks = ( n_bytes + BYTES_PER_BLOCK - 1 ) / BYTES_PER_BLOCK ;
size_t new_blocks = ( n_bytes + BYTES_PER_BLOCK - 1 ) / BYTES_PER_BLOCK ;
@ -590,6 +637,7 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
// return original ptr if it already has the requested number of blocks
// return original ptr if it already has the requested number of blocks
if ( new_blocks = = n_blocks ) {
if ( new_blocks = = n_blocks ) {
GC_EXIT ( ) ;
return ptr_in ;
return ptr_in ;
}
}
@ -605,6 +653,8 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
MP_STATE_MEM ( gc_last_free_atb_index ) = ( block + new_blocks ) / BLOCKS_PER_ATB ;
MP_STATE_MEM ( gc_last_free_atb_index ) = ( block + new_blocks ) / BLOCKS_PER_ATB ;
}
}
GC_EXIT ( ) ;
# if EXTENSIVE_HEAP_PROFILING
# if EXTENSIVE_HEAP_PROFILING
gc_dump_alloc_table ( ) ;
gc_dump_alloc_table ( ) ;
# endif
# endif
@ -620,6 +670,8 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
ATB_FREE_TO_TAIL ( bl ) ;
ATB_FREE_TO_TAIL ( bl ) ;
}
}
GC_EXIT ( ) ;
// zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc)
// zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc)
memset ( ( byte * ) ptr_in + n_bytes , 0 , new_blocks * BYTES_PER_BLOCK - n_bytes ) ;
memset ( ( byte * ) ptr_in + n_bytes , 0 , new_blocks * BYTES_PER_BLOCK - n_bytes ) ;
@ -630,6 +682,8 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
return ptr_in ;
return ptr_in ;
}
}
GC_EXIT ( ) ;
if ( ! allow_move ) {
if ( ! allow_move ) {
// not allowed to move memory block so return failure
// not allowed to move memory block so return failure
return NULL ;
return NULL ;
@ -666,6 +720,7 @@ void gc_dump_info(void) {
}
}
void gc_dump_alloc_table ( void ) {
void gc_dump_alloc_table ( void ) {
GC_ENTER ( ) ;
static const size_t DUMP_BYTES_PER_LINE = 64 ;
static const size_t DUMP_BYTES_PER_LINE = 64 ;
# if !EXTENSIVE_HEAP_PROFILING
# if !EXTENSIVE_HEAP_PROFILING
// When comparing heap output we don't want to print the starting
// When comparing heap output we don't want to print the starting
@ -771,6 +826,7 @@ void gc_dump_alloc_table(void) {
mp_printf ( & mp_plat_print , " %c " , c ) ;
mp_printf ( & mp_plat_print , " %c " , c ) ;
}
}
mp_print_str ( & mp_plat_print , " \n " ) ;
mp_print_str ( & mp_plat_print , " \n " ) ;
GC_EXIT ( ) ;
}
}
# if DEBUG_PRINT
# if DEBUG_PRINT