2014-05-03 23:27:38 +01:00
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2013, 2014 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
2014-04-20 11:45:16 +03:00
# include <assert.h>
2013-10-21 23:45:08 +01:00
# include <stdio.h>
# include <string.h>
2015-01-01 23:30:53 +00:00
# include "py/mpstate.h"
2015-01-01 20:27:54 +00:00
# include "py/gc.h"
# include "py/obj.h"
# include "py/runtime.h"
2014-04-03 23:55:12 +02:00
2014-01-07 15:20:33 +00:00
# if MICROPY_ENABLE_GC
2014-06-18 12:29:03 +02:00
#if 0 // print debugging info
2014-02-10 21:45:54 +02:00
#define DEBUG_PRINT (1)
2014-02-16 18:11:42 +02:00
#define DEBUG_printf DEBUG_printf
2014-02-10 21:45:54 +02:00
#else // don't print debugging info
2014-11-05 21:16:41 +00:00
# define DEBUG_PRINT (0)
2014-02-26 22:40:35 +00:00
# define DEBUG_printf(...) (void)0
2014-02-10 21:45:54 +02:00
# endif
2014-08-28 23:06:38 +01:00
// make this 1 to dump the heap each time it changes
# define EXTENSIVE_HEAP_PROFILING (0)
2013-10-21 23:45:08 +01:00
# define WORDS_PER_BLOCK (4)
# define BYTES_PER_BLOCK (WORDS_PER_BLOCK * BYTES_PER_WORD)
// ATB = allocation table byte
// 0b00 = FREE -- free block
// 0b01 = HEAD -- head of a chain of blocks
// 0b10 = TAIL -- in the tail of a chain of blocks
// 0b11 = MARK -- marked head block
# define AT_FREE (0)
# define AT_HEAD (1)
# define AT_TAIL (2)
# define AT_MARK (3)
# define BLOCKS_PER_ATB (4)
# define ATB_MASK_0 (0x03)
# define ATB_MASK_1 (0x0c)
# define ATB_MASK_2 (0x30)
# define ATB_MASK_3 (0xc0)
# define ATB_0_IS_FREE(a) (((a) & ATB_MASK_0) == 0)
# define ATB_1_IS_FREE(a) (((a) & ATB_MASK_1) == 0)
# define ATB_2_IS_FREE(a) (((a) & ATB_MASK_2) == 0)
# define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0)
# define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1)))
2015-01-01 23:30:53 +00:00
# define ATB_GET_KIND(block) ((MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3)
# define ATB_ANY_TO_FREE(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0)
# define ATB_FREE_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0)
# define ATB_FREE_TO_TAIL(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0)
# define ATB_HEAD_TO_MARK(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0)
# define ATB_MARK_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0)
2013-10-21 23:45:08 +01:00
2015-01-01 23:30:53 +00:00
# define BLOCK_FROM_PTR(ptr) (((ptr) - (mp_uint_t)MP_STATE_MEM(gc_pool_start)) / BYTES_PER_BLOCK)
# define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (mp_uint_t)MP_STATE_MEM(gc_pool_start)))
2013-10-21 23:45:08 +01:00
# define ATB_FROM_BLOCK(bl) ((bl) / BLOCKS_PER_ATB)
2014-04-05 20:35:48 +01:00
# if MICROPY_ENABLE_FINALISER
// FTB = finaliser table byte
// if set, then the corresponding block may have a finaliser
# define BLOCKS_PER_FTB (8)
2015-01-01 23:30:53 +00:00
# define FTB_GET(block) ((MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1)
# define FTB_SET(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)
2014-04-05 20:35:48 +01:00
# endif
2013-10-22 21:12:29 +01:00
// 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 ) {
// align end pointer on block boundary
2014-07-03 13:25:24 +01:00
end = ( void * ) ( ( mp_uint_t ) end & ( ~ ( BYTES_PER_BLOCK - 1 ) ) ) ;
2014-06-18 10:20:41 +02:00
DEBUG_printf ( " Initializing GC heap: %p..%p = " UINT_FMT " bytes \n " , start , end , ( byte * ) end - ( byte * ) start ) ;
2014-04-05 20:35:48 +01:00
// calculate parameters for GC (T=total, A=alloc table, F=finaliser table, P=pool; all in bytes):
// T = A + F + P
// F = A * BLOCKS_PER_ATB / BLOCKS_PER_FTB
// P = A * BLOCKS_PER_ATB * BYTES_PER_BLOCK
// => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK)
2014-07-03 13:25:24 +01:00
mp_uint_t total_byte_len = ( byte * ) end - ( byte * ) start ;
2014-04-05 20:35:48 +01:00
# if MICROPY_ENABLE_FINALISER
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_alloc_table_byte_len ) = total_byte_len * BITS_PER_BYTE / ( BITS_PER_BYTE + BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK ) ;
2014-04-05 20:35:48 +01:00
# else
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_alloc_table_byte_len ) = total_byte_len / ( 1 + BITS_PER_BYTE / 2 * BYTES_PER_BLOCK ) ;
2014-04-05 20:35:48 +01:00
# endif
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_alloc_table_start ) = ( byte * ) start ;
2014-04-03 23:55:12 +02:00
2014-04-05 20:35:48 +01:00
# if MICROPY_ENABLE_FINALISER
2015-01-01 23:30:53 +00:00
mp_uint_t gc_finaliser_table_byte_len = ( MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1 ) / BLOCKS_PER_FTB ;
MP_STATE_MEM ( gc_finaliser_table_start ) = MP_STATE_MEM ( gc_alloc_table_start ) + MP_STATE_MEM ( gc_alloc_table_byte_len ) ;
2014-04-05 20:35:48 +01:00
# endif
2014-04-03 23:55:12 +02:00
2015-01-01 23:30:53 +00:00
mp_uint_t gc_pool_block_len = MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ;
MP_STATE_MEM ( gc_pool_start ) = ( mp_uint_t * ) ( ( byte * ) end - gc_pool_block_len * BYTES_PER_BLOCK ) ;
MP_STATE_MEM ( gc_pool_end ) = ( mp_uint_t * ) end ;
2013-10-22 21:12:29 +01:00
2014-08-08 12:33:49 +01:00
# if MICROPY_ENABLE_FINALISER
2015-01-01 23:30:53 +00:00
assert ( ( byte * ) MP_STATE_MEM ( gc_pool_start ) > = MP_STATE_MEM ( gc_finaliser_table_start ) + gc_finaliser_table_byte_len ) ;
2014-08-08 12:33:49 +01:00
# endif
2013-10-22 21:12:29 +01:00
// clear ATBs
2015-01-01 23:30:53 +00:00
memset ( MP_STATE_MEM ( gc_alloc_table_start ) , 0 , MP_STATE_MEM ( gc_alloc_table_byte_len ) ) ;
2013-10-22 21:12:29 +01:00
2014-04-05 20:35:48 +01:00
# if MICROPY_ENABLE_FINALISER
// clear FTBs
2015-01-01 23:30:53 +00:00
memset ( MP_STATE_MEM ( gc_finaliser_table_start ) , 0 , gc_finaliser_table_byte_len ) ;
2014-04-05 20:35:48 +01:00
# endif
2014-04-03 23:55:12 +02:00
2014-08-22 18:17:02 +01:00
// set last free ATB index to start of heap
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_last_free_atb_index ) = 0 ;
2014-08-22 18:17:02 +01:00
2014-04-05 20:35:48 +01:00
// unlock the GC
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_lock_depth ) = 0 ;
2014-04-05 20:35:48 +01:00
2014-10-31 21:30:46 +00:00
// allow auto collection
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_auto_collect_enabled ) = 1 ;
2014-10-31 21:30:46 +00:00
2014-02-10 21:45:54 +02:00
DEBUG_printf ( " GC layout: \n " ) ;
2015-01-01 23:30:53 +00:00
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 ) ;
2014-04-05 20:35:48 +01:00
# if MICROPY_ENABLE_FINALISER
2015-01-01 23:30:53 +00:00
DEBUG_printf ( " finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks \n " , MP_STATE_MEM ( gc_finaliser_table_start ) , gc_finaliser_table_byte_len , gc_finaliser_table_byte_len * BLOCKS_PER_FTB ) ;
2014-04-05 20:35:48 +01:00
# endif
2015-01-01 23:30:53 +00:00
DEBUG_printf ( " pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks \n " , MP_STATE_MEM ( gc_pool_start ) , gc_pool_block_len * BYTES_PER_BLOCK , gc_pool_block_len ) ;
2013-10-22 21:12:29 +01:00
}
2014-04-08 11:31:21 +00:00
void gc_lock ( void ) {
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_lock_depth ) + + ;
2014-04-08 11:31:21 +00:00
}
void gc_unlock ( void ) {
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_lock_depth ) - - ;
2014-04-08 11:31:21 +00:00
}
2014-06-30 22:49:21 -07:00
bool gc_is_locked ( void ) {
2015-01-01 23:30:53 +00:00
return MP_STATE_MEM ( gc_lock_depth ) ! = 0 ;
2014-06-30 22:49:21 -07:00
}
2013-10-22 20:26:36 +01:00
# define VERIFY_PTR(ptr) ( \
(ptr & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \
2015-01-01 23:30:53 +00:00
&& ptr >= (mp_uint_t)MP_STATE_MEM(gc_pool_start) /* must be above start of pool */ \
&& ptr < (mp_uint_t)MP_STATE_MEM(gc_pool_end) /* must be below end of pool */ \
2013-10-22 20:26:36 +01:00
)
2013-10-21 23:45:08 +01:00
# define VERIFY_MARK_AND_PUSH(ptr) \
do { \
2013-10-22 20:26:36 +01:00
if (VERIFY_PTR(ptr)) { \
2014-07-03 13:25:24 +01:00
mp_uint_t _block = BLOCK_FROM_PTR(ptr); \
2013-10-21 23:45:08 +01:00
if (ATB_GET_KIND(_block) == AT_HEAD) { \
/* an unmarked head, mark it, and push it on gc stack */ \
ATB_HEAD_TO_MARK(_block); \
2015-01-01 23:30:53 +00:00
if (MP_STATE_MEM(gc_sp) < &MP_STATE_MEM(gc_stack)[MICROPY_ALLOC_GC_STACK_SIZE]) { \
*MP_STATE_MEM(gc_sp)++ = _block; \
2013-10-21 23:45:08 +01:00
} else { \
2015-01-01 23:30:53 +00:00
MP_STATE_MEM(gc_stack_overflow) = 1; \
2013-10-21 23:45:08 +01:00
} \
} \
} \
} while (0)
2014-02-12 18:31:30 +02:00
STATIC void gc_drain_stack ( void ) {
2015-01-01 23:30:53 +00:00
while ( MP_STATE_MEM ( gc_sp ) > MP_STATE_MEM ( gc_stack ) ) {
2013-10-21 23:45:08 +01:00
// pop the next block off the stack
2015-01-01 23:30:53 +00:00
mp_uint_t block = * - - MP_STATE_MEM ( gc_sp ) ;
2013-10-21 23:45:08 +01:00
2013-10-22 15:25:25 +01:00
// work out number of consecutive blocks in the chain starting with this one
2014-07-03 13:25:24 +01:00
mp_uint_t n_blocks = 0 ;
2013-10-21 23:45:08 +01:00
do {
n_blocks + = 1 ;
} while ( ATB_GET_KIND ( block + n_blocks ) = = AT_TAIL ) ;
// check this block's children
2014-07-03 13:25:24 +01:00
mp_uint_t * scan = ( mp_uint_t * ) PTR_FROM_BLOCK ( block ) ;
for ( mp_uint_t i = n_blocks * WORDS_PER_BLOCK ; i > 0 ; i - - , scan + + ) {
mp_uint_t ptr2 = * scan ;
2013-10-21 23:45:08 +01:00
VERIFY_MARK_AND_PUSH ( ptr2 ) ;
}
}
}
2014-02-12 18:31:30 +02:00
STATIC void gc_deal_with_stack_overflow ( void ) {
2015-01-01 23:30:53 +00:00
while ( MP_STATE_MEM ( gc_stack_overflow ) ) {
MP_STATE_MEM ( gc_stack_overflow ) = 0 ;
MP_STATE_MEM ( gc_sp ) = MP_STATE_MEM ( gc_stack ) ;
2013-10-21 23:45:08 +01:00
// scan entire memory looking for blocks which have been marked but not their children
2015-01-01 23:30:53 +00:00
for ( mp_uint_t block = 0 ; block < MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ; block + + ) {
2013-10-21 23:45:08 +01:00
// trace (again) if mark bit set
if ( ATB_GET_KIND ( block ) = = AT_MARK ) {
2015-01-01 23:30:53 +00:00
* MP_STATE_MEM ( gc_sp ) + + = block ;
2013-10-21 23:45:08 +01:00
gc_drain_stack ( ) ;
}
}
}
}
2014-02-12 18:31:30 +02:00
STATIC void gc_sweep ( void ) {
2014-06-05 22:48:02 +03:00
# if MICROPY_PY_GC_COLLECT_RETVAL
2015-02-07 17:24:10 +00:00
MP_STATE_MEM ( gc_collected ) = 0 ;
2014-06-05 22:48:02 +03:00
# endif
2013-10-21 23:45:08 +01:00
// free unmarked heads and their tails
int free_tail = 0 ;
2015-01-01 23:30:53 +00:00
for ( mp_uint_t block = 0 ; block < MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ; block + + ) {
2013-10-21 23:45:08 +01:00
switch ( ATB_GET_KIND ( block ) ) {
case AT_HEAD :
2014-04-05 20:35:48 +01:00
# if MICROPY_ENABLE_FINALISER
if ( FTB_GET ( block ) ) {
mp_obj_t obj = ( mp_obj_t ) PTR_FROM_BLOCK ( block ) ;
if ( ( ( mp_obj_base_t * ) obj ) - > type ! = MP_OBJ_NULL ) {
// if the object has a type then see if it has a __del__ method
mp_obj_t dest [ 2 ] ;
mp_load_method_maybe ( obj , MP_QSTR___del__ , dest ) ;
if ( dest [ 0 ] ! = MP_OBJ_NULL ) {
// load_method returned a method
mp_call_method_n_kw ( 0 , 0 , dest ) ;
}
2014-04-03 23:55:12 +02:00
}
2014-04-05 20:35:48 +01:00
// clear finaliser flag
FTB_CLEAR ( block ) ;
2014-04-03 23:55:12 +02:00
}
2014-04-05 20:35:48 +01:00
# endif
2013-10-21 23:45:08 +01:00
free_tail = 1 ;
2014-06-05 22:48:02 +03:00
# if MICROPY_PY_GC_COLLECT_RETVAL
2015-02-07 17:24:10 +00:00
MP_STATE_MEM ( gc_collected ) + + ;
2014-06-05 22:48:02 +03:00
# endif
2013-10-21 23:45:08 +01:00
// fall through to free the head
case AT_TAIL :
if ( free_tail ) {
2014-06-16 10:44:29 +02:00
DEBUG_printf ( " gc_sweep(%p) \n " , PTR_FROM_BLOCK ( block ) ) ;
2013-10-21 23:45:08 +01:00
ATB_ANY_TO_FREE ( block ) ;
}
break ;
case AT_MARK :
ATB_MARK_TO_HEAD ( block ) ;
free_tail = 0 ;
break ;
}
}
}
2013-10-23 20:20:17 +01:00
void gc_collect_start ( void ) {
2014-04-08 11:31:21 +00:00
gc_lock ( ) ;
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_stack_overflow ) = 0 ;
MP_STATE_MEM ( gc_sp ) = MP_STATE_MEM ( gc_stack ) ;
// Trace root pointers. This relies on the root pointers being organised
// correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals,
// dict_globals, then the root pointer section of mp_state_vm.
void * * ptrs = ( void * * ) ( void * ) & mp_state_ctx ;
gc_collect_root ( ptrs , offsetof ( mp_state_ctx_t , vm . stack_top ) / sizeof ( mp_uint_t ) ) ;
2013-10-21 23:45:08 +01:00
}
2014-07-03 13:25:24 +01:00
void gc_collect_root ( void * * ptrs , mp_uint_t len ) {
for ( mp_uint_t i = 0 ; i < len ; i + + ) {
mp_uint_t ptr = ( mp_uint_t ) ptrs [ i ] ;
2013-10-21 23:45:08 +01:00
VERIFY_MARK_AND_PUSH ( ptr ) ;
gc_drain_stack ( ) ;
}
}
2013-10-23 20:20:17 +01:00
void gc_collect_end ( void ) {
2013-10-21 23:45:08 +01:00
gc_deal_with_stack_overflow ( ) ;
gc_sweep ( ) ;
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_last_free_atb_index ) = 0 ;
2014-04-08 11:31:21 +00:00
gc_unlock ( ) ;
2013-10-22 15:25:25 +01:00
}
2013-10-21 23:45:08 +01:00
2013-10-22 15:25:25 +01:00
void gc_info ( gc_info_t * info ) {
2015-01-01 23:30:53 +00:00
info - > total = ( MP_STATE_MEM ( gc_pool_end ) - MP_STATE_MEM ( gc_pool_start ) ) * sizeof ( mp_uint_t ) ;
2013-10-22 15:25:25 +01:00
info - > used = 0 ;
info - > free = 0 ;
info - > num_1block = 0 ;
info - > num_2block = 0 ;
info - > max_block = 0 ;
2015-01-01 23:30:53 +00:00
for ( mp_uint_t block = 0 , len = 0 ; block < MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ; block + + ) {
2014-07-03 13:25:24 +01:00
mp_uint_t kind = ATB_GET_KIND ( block ) ;
2013-10-22 15:25:25 +01:00
if ( kind = = AT_FREE | | kind = = AT_HEAD ) {
if ( len = = 1 ) {
info - > num_1block + = 1 ;
} else if ( len = = 2 ) {
info - > num_2block + = 1 ;
}
if ( len > info - > max_block ) {
info - > max_block = len ;
}
}
switch ( kind ) {
2013-10-21 23:45:08 +01:00
case AT_FREE :
2013-10-22 15:25:25 +01:00
info - > free + = 1 ;
len = 0 ;
2013-10-21 23:45:08 +01:00
break ;
case AT_HEAD :
2013-10-22 15:25:25 +01:00
info - > used + = 1 ;
len = 1 ;
break ;
2013-10-21 23:45:08 +01:00
case AT_TAIL :
2013-10-22 15:25:25 +01:00
info - > used + = 1 ;
len + = 1 ;
2013-10-21 23:45:08 +01:00
break ;
case AT_MARK :
2013-10-22 15:25:25 +01:00
// shouldn't happen
2013-10-21 23:45:08 +01:00
break ;
}
}
2013-10-22 15:25:25 +01:00
info - > used * = BYTES_PER_BLOCK ;
info - > free * = BYTES_PER_BLOCK ;
2013-10-21 23:45:08 +01:00
}
2014-07-03 13:25:24 +01:00
void * gc_alloc ( mp_uint_t n_bytes , bool has_finaliser ) {
mp_uint_t n_blocks = ( ( n_bytes + BYTES_PER_BLOCK - 1 ) & ( ~ ( BYTES_PER_BLOCK - 1 ) ) ) / BYTES_PER_BLOCK ;
2014-06-18 10:20:41 +02:00
DEBUG_printf ( " gc_alloc( " UINT_FMT " bytes -> " UINT_FMT " blocks) \n " , n_bytes , n_blocks ) ;
2013-10-21 23:45:08 +01:00
2014-04-08 11:31:21 +00:00
// check if GC is locked
2015-01-01 23:30:53 +00:00
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
2014-04-08 11:31:21 +00:00
return NULL ;
2014-04-05 20:35:48 +01:00
}
2013-10-21 23:45:08 +01:00
// check for 0 allocation
if ( n_blocks = = 0 ) {
return NULL ;
}
2014-07-03 13:25:24 +01:00
mp_uint_t i ;
mp_uint_t end_block ;
mp_uint_t start_block ;
mp_uint_t n_free = 0 ;
2015-01-01 23:30:53 +00:00
int collected = ! MP_STATE_MEM ( gc_auto_collect_enabled ) ;
2013-10-21 23:45:08 +01:00
for ( ; ; ) {
// look for a run of n_blocks available blocks
2015-01-01 23:30:53 +00:00
for ( i = MP_STATE_MEM ( gc_last_free_atb_index ) ; i < MP_STATE_MEM ( gc_alloc_table_byte_len ) ; i + + ) {
byte a = MP_STATE_MEM ( gc_alloc_table_start ) [ i ] ;
2014-08-22 18:17:02 +01:00
if ( ATB_0_IS_FREE ( a ) ) { if ( + + n_free > = n_blocks ) { i = i * BLOCKS_PER_ATB + 0 ; goto found ; } } else { n_free = 0 ; }
if ( ATB_1_IS_FREE ( a ) ) { if ( + + n_free > = n_blocks ) { i = i * BLOCKS_PER_ATB + 1 ; goto found ; } } else { n_free = 0 ; }
if ( ATB_2_IS_FREE ( a ) ) { if ( + + n_free > = n_blocks ) { i = i * BLOCKS_PER_ATB + 2 ; 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 ; }
}
2013-10-21 23:45:08 +01:00
// nothing found!
if ( collected ) {
return NULL ;
}
2014-02-11 18:01:38 +02:00
DEBUG_printf ( " gc_alloc( " UINT_FMT " ): no free mem, triggering GC \n " , n_bytes ) ;
2013-10-21 23:45:08 +01:00
gc_collect ( ) ;
collected = 1 ;
}
// found, ending at block i inclusive
found :
// get starting and end blocks, both inclusive
end_block = i ;
start_block = i - n_free + 1 ;
2014-08-28 10:18:40 +01:00
// Set last free ATB index to block after last block we found, for start of
// next scan. To reduce fragmentation, we only do this if we were looking
// for a single free block, which guarantees that there are no free blocks
2014-08-28 23:06:38 +01:00
// before this one. Also, whenever we free or shink a block we must check
// if this index needs adjusting (see gc_realloc and gc_free).
2014-08-28 10:18:40 +01:00
if ( n_free = = 1 ) {
2015-01-01 23:30:53 +00:00
MP_STATE_MEM ( gc_last_free_atb_index ) = ( i + 1 ) / BLOCKS_PER_ATB ;
2014-08-28 10:18:40 +01:00
}
2014-08-22 18:17:02 +01:00
2013-10-21 23:45:08 +01:00
// mark first block as used head
ATB_FREE_TO_HEAD ( start_block ) ;
// mark rest of blocks as used tail
// TODO for a run of many blocks can make this more efficient
2014-07-03 13:25:24 +01:00
for ( mp_uint_t bl = start_block + 1 ; bl < = end_block ; bl + + ) {
2013-10-21 23:45:08 +01:00
ATB_FREE_TO_TAIL ( bl ) ;
}
2014-04-05 20:35:48 +01:00
// get pointer to first block
2015-01-01 23:30:53 +00:00
void * ret_ptr = ( void * ) ( MP_STATE_MEM ( gc_pool_start ) + start_block * WORDS_PER_BLOCK ) ;
2014-06-18 10:20:41 +02:00
DEBUG_printf ( " gc_alloc(%p) \n " , ret_ptr ) ;
2014-04-05 15:49:03 +02:00
2014-04-26 22:23:42 +01:00
// zero out the additional bytes of the newly allocated blocks
2014-04-25 23:37:55 +01:00
// This is needed because the blocks may have previously held pointers
// to the heap and will not be set to something else if the caller
// doesn't actually use the entire block. As such they will continue
// to point to the heap and may prevent other blocks from being reclaimed.
2014-06-13 22:33:31 +01:00
memset ( ( byte * ) ret_ptr + n_bytes , 0 , ( end_block - start_block + 1 ) * BYTES_PER_BLOCK - n_bytes ) ;
2014-04-25 23:37:55 +01:00
2015-09-04 16:53:46 +01:00
# if MICROPY_ENABLE_FINALISER
2014-04-05 20:35:48 +01:00
if ( has_finaliser ) {
2014-04-26 22:23:42 +01:00
// clear type pointer in case it is never set
( ( mp_obj_base_t * ) ret_ptr ) - > type = MP_OBJ_NULL ;
2014-04-05 20:35:48 +01:00
// set mp_obj flag only if it has a finaliser
FTB_SET ( start_block ) ;
}
2015-09-04 16:53:46 +01:00
# else
( void ) has_finaliser ;
# endif
2014-04-05 20:35:48 +01:00
2014-08-28 23:06:38 +01:00
# if EXTENSIVE_HEAP_PROFILING
gc_dump_alloc_table ( ) ;
# endif
2014-04-05 20:35:48 +01:00
return ret_ptr ;
2013-10-21 23:45:08 +01:00
}
2014-04-05 20:35:48 +01:00
/*
2014-07-03 13:25:24 +01:00
void *gc_alloc(mp_uint_t n_bytes) {
2014-04-03 23:55:12 +02:00
return _gc_alloc(n_bytes, false);
}
2014-07-03 13:25:24 +01:00
void *gc_alloc_with_finaliser(mp_uint_t n_bytes) {
2014-04-03 23:55:12 +02:00
return _gc_alloc(n_bytes, true);
}
2014-04-05 20:35:48 +01:00
*/
2014-04-03 23:55:12 +02:00
2013-10-22 20:26:36 +01:00
// force the freeing of a piece of memory
void gc_free ( void * ptr_in ) {
2015-01-01 23:30:53 +00:00
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
2014-04-08 11:31:21 +00:00
// TODO how to deal with this error?
return ;
2014-04-05 20:35:48 +01:00
}
2014-07-03 13:25:24 +01:00
mp_uint_t ptr = ( mp_uint_t ) ptr_in ;
2014-06-16 10:44:29 +02:00
DEBUG_printf ( " gc_free(%p) \n " , ptr ) ;
2013-10-22 20:26:36 +01:00
if ( VERIFY_PTR ( ptr ) ) {
2014-07-03 13:25:24 +01:00
mp_uint_t block = BLOCK_FROM_PTR ( ptr ) ;
2013-10-22 20:26:36 +01:00
if ( ATB_GET_KIND ( block ) = = AT_HEAD ) {
2014-08-28 23:06:38 +01:00
// set the last_free pointer to this block if it's earlier in the heap
2015-01-01 23:30:53 +00:00
if ( block / BLOCKS_PER_ATB < MP_STATE_MEM ( gc_last_free_atb_index ) ) {
MP_STATE_MEM ( gc_last_free_atb_index ) = block / BLOCKS_PER_ATB ;
2014-08-28 23:06:38 +01:00
}
2013-10-22 20:26:36 +01:00
// free head and all of its tail blocks
do {
ATB_ANY_TO_FREE ( block ) ;
block + = 1 ;
} while ( ATB_GET_KIND ( block ) = = AT_TAIL ) ;
2014-08-28 23:06:38 +01:00
# if EXTENSIVE_HEAP_PROFILING
gc_dump_alloc_table ( ) ;
# endif
2014-10-23 14:13:05 +01:00
} else {
assert ( ! " bad free " ) ;
2013-10-22 20:26:36 +01:00
}
2014-10-23 14:13:05 +01:00
} else if ( ptr_in ! = NULL ) {
assert ( ! " bad free " ) ;
2013-10-22 20:26:36 +01:00
}
}
2014-10-24 23:12:25 +01:00
mp_uint_t gc_nbytes ( const void * ptr_in ) {
2014-07-03 13:25:24 +01:00
mp_uint_t ptr = ( mp_uint_t ) ptr_in ;
2013-10-21 23:45:08 +01:00
2013-10-22 20:26:36 +01:00
if ( VERIFY_PTR ( ptr ) ) {
2014-07-03 13:25:24 +01:00
mp_uint_t block = BLOCK_FROM_PTR ( ptr ) ;
2013-10-21 23:45:08 +01:00
if ( ATB_GET_KIND ( block ) = = AT_HEAD ) {
// work out number of consecutive blocks in the chain starting with this on
2014-07-03 13:25:24 +01:00
mp_uint_t n_blocks = 0 ;
2013-10-21 23:45:08 +01:00
do {
n_blocks + = 1 ;
} while ( ATB_GET_KIND ( block + n_blocks ) = = AT_TAIL ) ;
return n_blocks * BYTES_PER_BLOCK ;
}
}
// invalid pointer
return 0 ;
}
2014-03-12 21:00:23 +02:00
#if 0
2014-04-08 11:31:21 +00:00
// old, simple realloc that didn't expand memory in place
2014-07-03 13:25:24 +01:00
void *gc_realloc(void *ptr, mp_uint_t n_bytes) {
mp_uint_t n_existing = gc_nbytes(ptr);
2014-03-07 00:21:51 +00:00
if (n_bytes <= n_existing) {
return ptr;
} else {
2014-04-25 11:44:53 +00:00
bool has_finaliser;
if (ptr == NULL) {
has_finaliser = false;
} else {
2014-04-20 11:43:38 +03:00
#if MICROPY_ENABLE_FINALISER
2014-07-03 13:25:24 +01:00
has_finaliser = FTB_GET(BLOCK_FROM_PTR((mp_uint_t)ptr));
2014-04-20 11:43:38 +03:00
#else
2014-04-25 11:44:53 +00:00
has_finaliser = false;
2014-04-20 11:43:38 +03:00
#endif
2014-04-25 11:44:53 +00:00
}
void * ptr2 = gc_alloc ( n_bytes , has_finaliser ) ;
2014-03-07 00:21:51 +00:00
if ( ptr2 = = NULL ) {
return ptr2 ;
}
memcpy ( ptr2 , ptr , n_existing ) ;
gc_free ( ptr ) ;
return ptr2 ;
}
}
2014-04-20 11:43:38 +03:00
# else // Alternative gc_realloc impl
2014-04-08 11:31:21 +00:00
2015-06-13 21:53:22 +01:00
void * gc_realloc ( void * ptr_in , mp_uint_t n_bytes , bool allow_move ) {
2015-01-01 23:30:53 +00:00
if ( MP_STATE_MEM ( gc_lock_depth ) > 0 ) {
2014-04-08 11:31:21 +00:00
return NULL ;
2014-04-05 20:35:48 +01:00
}
2014-04-20 18:16:25 +01:00
// check for pure allocation
2014-03-05 23:23:04 +02:00
if ( ptr_in = = NULL ) {
2014-04-05 20:35:48 +01:00
return gc_alloc ( n_bytes , false ) ;
2013-10-21 23:45:08 +01:00
}
2014-03-05 23:23:04 +02:00
2014-10-23 12:02:00 +01:00
// check for pure free
if ( n_bytes = = 0 ) {
gc_free ( ptr_in ) ;
return NULL ;
}
2014-07-03 13:25:24 +01:00
mp_uint_t ptr = ( mp_uint_t ) ptr_in ;
2014-04-20 18:16:25 +01:00
// sanity check the ptr
if ( ! VERIFY_PTR ( ptr ) ) {
return NULL ;
}
2014-04-20 11:45:16 +03:00
// get first block
2014-07-03 13:25:24 +01:00
mp_uint_t block = BLOCK_FROM_PTR ( ptr ) ;
2014-04-20 11:45:16 +03:00
2014-04-20 18:16:25 +01:00
// sanity check the ptr is pointing to the head of a block
if ( ATB_GET_KIND ( block ) ! = AT_HEAD ) {
return NULL ;
2014-03-05 23:23:04 +02:00
}
2014-04-20 18:16:25 +01:00
// compute number of new blocks that are requested
2014-07-03 13:25:24 +01:00
mp_uint_t new_blocks = ( n_bytes + BYTES_PER_BLOCK - 1 ) / BYTES_PER_BLOCK ;
2014-04-20 18:16:25 +01:00
2014-10-15 18:24:47 +00:00
// Get the total number of consecutive blocks that are already allocated to
// this chunk of memory, and then count the number of free blocks following
// it. Stop if we reach the end of the heap, or if we find enough extra
// free blocks to satisfy the realloc. Note that we need to compute the
// total size of the existing memory chunk so we can correctly and
// efficiently shrink it (see below for shrinking code).
2014-07-03 13:25:24 +01:00
mp_uint_t n_free = 0 ;
mp_uint_t n_blocks = 1 ; // counting HEAD block
2015-01-01 23:30:53 +00:00
mp_uint_t max_block = MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ;
2014-10-15 18:24:47 +00:00
for ( mp_uint_t bl = block + n_blocks ; bl < max_block ; bl + + ) {
byte block_type = ATB_GET_KIND ( bl ) ;
if ( block_type = = AT_TAIL ) {
n_blocks + + ;
continue ;
2014-04-20 18:16:25 +01:00
}
2014-10-15 18:24:47 +00:00
if ( block_type = = AT_FREE ) {
n_free + + ;
if ( n_blocks + n_free > = new_blocks ) {
// stop as soon as we find enough blocks for n_bytes
break ;
}
continue ;
2014-04-20 18:16:25 +01:00
}
break ;
}
// return original ptr if it already has the requested number of blocks
if ( new_blocks = = n_blocks ) {
return ptr_in ;
}
// check if we can shrink the allocated area
if ( new_blocks < n_blocks ) {
// free unneeded tail blocks
2014-10-15 18:24:47 +00:00
for ( mp_uint_t bl = block + new_blocks , count = n_blocks - new_blocks ; count > 0 ; bl + + , count - - ) {
2014-04-20 18:16:25 +01:00
ATB_ANY_TO_FREE ( bl ) ;
}
2014-08-28 23:06:38 +01:00
// set the last_free pointer to end of this block if it's earlier in the heap
2015-01-01 23:30:53 +00:00
if ( ( block + new_blocks ) / BLOCKS_PER_ATB < MP_STATE_MEM ( gc_last_free_atb_index ) ) {
MP_STATE_MEM ( gc_last_free_atb_index ) = ( block + new_blocks ) / BLOCKS_PER_ATB ;
2014-08-28 23:06:38 +01:00
}
# if EXTENSIVE_HEAP_PROFILING
gc_dump_alloc_table ( ) ;
# endif
2014-04-20 18:16:25 +01:00
return ptr_in ;
}
// check if we can expand in place
if ( new_blocks < = n_blocks + n_free ) {
// mark few more blocks as used tail
2014-07-03 13:25:24 +01:00
for ( mp_uint_t bl = block + n_blocks ; bl < block + new_blocks ; bl + + ) {
2014-04-20 18:16:25 +01:00
assert ( ATB_GET_KIND ( bl ) = = AT_FREE ) ;
ATB_FREE_TO_TAIL ( bl ) ;
}
2014-04-25 23:37:55 +01:00
2014-04-26 22:23:42 +01:00
// zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc)
2014-06-12 17:42:20 +02:00
memset ( ( byte * ) ptr_in + n_bytes , 0 , new_blocks * BYTES_PER_BLOCK - n_bytes ) ;
2014-04-25 23:37:55 +01:00
2014-08-28 23:06:38 +01:00
# if EXTENSIVE_HEAP_PROFILING
gc_dump_alloc_table ( ) ;
# endif
2014-04-20 18:16:25 +01:00
return ptr_in ;
}
2015-06-13 21:53:22 +01:00
if ( ! allow_move ) {
// not allowed to move memory block so return failure
return NULL ;
}
2014-04-20 18:16:25 +01:00
// can't resize inplace; try to find a new contiguous chain
void * ptr_out = gc_alloc ( n_bytes ,
# if MICROPY_ENABLE_FINALISER
FTB_GET ( block )
# else
false
# endif
) ;
// check that the alloc succeeded
if ( ptr_out = = NULL ) {
return NULL ;
}
2014-06-16 10:44:29 +02:00
DEBUG_printf ( " gc_realloc(%p -> %p) \n " , ptr_in , ptr_out ) ;
2014-04-20 18:16:25 +01:00
memcpy ( ptr_out , ptr_in , n_blocks * BYTES_PER_BLOCK ) ;
gc_free ( ptr_in ) ;
return ptr_out ;
2013-10-21 23:45:08 +01:00
}
2014-04-20 11:43:38 +03:00
# endif // Alternative gc_realloc impl
2014-03-12 21:00:23 +02:00
2015-01-12 22:34:38 +00:00
void gc_dump_info ( void ) {
2014-02-11 18:01:38 +02:00
gc_info_t info ;
gc_info ( & info ) ;
2015-04-11 12:15:47 +01:00
mp_printf ( & mp_plat_print , " GC: total: " UINT_FMT " , used: " UINT_FMT " , free: " UINT_FMT " \n " ,
info . total , info . used , info . free ) ;
mp_printf ( & mp_plat_print , " No. of 1-blocks: " UINT_FMT " , 2-blocks: " UINT_FMT " , max blk sz: " UINT_FMT " \n " ,
2014-02-11 18:01:38 +02:00
info . num_1block , info . num_2block , info . max_block ) ;
}
2014-02-26 22:55:59 +00:00
void gc_dump_alloc_table ( void ) {
2014-08-28 23:06:38 +01:00
static const mp_uint_t DUMP_BYTES_PER_LINE = 64 ;
# if !EXTENSIVE_HEAP_PROFILING
// When comparing heap output we don't want to print the starting
// pointer of the heap because it changes from run to run.
2015-04-11 12:15:47 +01:00
mp_printf ( & mp_plat_print , " GC memory layout; from %p: " , MP_STATE_MEM ( gc_pool_start ) ) ;
2014-08-28 23:06:38 +01:00
# endif
2015-01-01 23:30:53 +00:00
for ( mp_uint_t bl = 0 ; bl < MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ; bl + + ) {
2014-08-28 23:06:38 +01:00
if ( bl % DUMP_BYTES_PER_LINE = = 0 ) {
// a new line of blocks
{
// check if this line contains only free blocks
2014-10-24 23:12:25 +01:00
mp_uint_t bl2 = bl ;
2015-01-01 23:30:53 +00:00
while ( bl2 < MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB & & ATB_GET_KIND ( bl2 ) = = AT_FREE ) {
2014-10-24 23:12:25 +01:00
bl2 + + ;
}
if ( bl2 - bl > = 2 * DUMP_BYTES_PER_LINE ) {
// there are at least 2 lines containing only free blocks, so abbreviate their printing
2015-04-11 12:15:47 +01:00
mp_printf ( & mp_plat_print , " \n ( " UINT_FMT " lines all free) " , ( bl2 - bl ) / DUMP_BYTES_PER_LINE ) ;
2014-10-24 23:12:25 +01:00
bl = bl2 & ( ~ ( DUMP_BYTES_PER_LINE - 1 ) ) ;
2015-01-01 23:30:53 +00:00
if ( bl > = MP_STATE_MEM ( gc_alloc_table_byte_len ) * BLOCKS_PER_ATB ) {
2014-10-24 23:12:25 +01:00
// got to end of heap
2014-08-28 23:06:38 +01:00
break ;
}
}
}
// print header for new line of blocks
2015-03-31 23:07:02 +01:00
// (the cast to uint32_t is for 16-bit ports)
2014-10-24 23:12:25 +01:00
# if EXTENSIVE_HEAP_PROFILING
2015-04-11 12:15:47 +01:00
mp_printf ( & mp_plat_print , " \n %05x: " , ( uint ) ( ( bl * BYTES_PER_BLOCK ) & ( uint32_t ) 0xfffff ) ) ;
2014-10-24 23:12:25 +01:00
# else
2015-04-11 12:15:47 +01:00
mp_printf ( & mp_plat_print , " \n %05x: " , ( uint ) ( PTR_FROM_BLOCK ( bl ) & ( uint32_t ) 0xfffff ) ) ;
2014-10-24 23:12:25 +01:00
# endif
2013-10-22 15:25:25 +01:00
}
2014-02-26 22:55:59 +00:00
int c = ' ' ;
switch ( ATB_GET_KIND ( bl ) ) {
case AT_FREE : c = ' . ' ; break ;
2014-10-17 14:12:57 +00:00
/* this prints out if the object is reachable from BSS or STACK (for unix only)
case AT_HEAD: {
c = 'h';
2015-01-08 10:32:45 +01:00
void **ptrs = (void**)(void*)&mp_state_ctx;
mp_uint_t len = offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t);
2014-10-17 14:12:57 +00:00
for (mp_uint_t i = 0; i < len; i++) {
mp_uint_t ptr = (mp_uint_t)ptrs[i];
if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) {
c = 'B';
break;
}
}
if (c == 'h') {
ptrs = (void**)&c;
2015-01-08 10:32:45 +01:00
len = ((mp_uint_t)MP_STATE_VM(stack_top) - (mp_uint_t)&c) / sizeof(mp_uint_t);
2014-10-17 14:12:57 +00:00
for (mp_uint_t i = 0; i < len; i++) {
mp_uint_t ptr = (mp_uint_t)ptrs[i];
if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) {
c = 'S';
break;
}
}
}
break;
}
*/
2014-10-24 23:12:25 +01:00
/* this prints the uPy object type of the head block */
2014-04-25 23:37:55 +01:00
case AT_HEAD : {
2015-01-01 23:30:53 +00:00
mp_uint_t * ptr = MP_STATE_MEM ( gc_pool_start ) + bl * WORDS_PER_BLOCK ;
2014-07-03 13:25:24 +01:00
if ( * ptr = = ( mp_uint_t ) & mp_type_tuple ) { c = ' T ' ; }
else if ( * ptr = = ( mp_uint_t ) & mp_type_list ) { c = ' L ' ; }
else if ( * ptr = = ( mp_uint_t ) & mp_type_dict ) { c = ' D ' ; }
2014-11-05 21:16:41 +00:00
# if MICROPY_PY_BUILTINS_FLOAT
2014-07-03 13:25:24 +01:00
else if ( * ptr = = ( mp_uint_t ) & mp_type_float ) { c = ' F ' ; }
2014-11-05 21:16:41 +00:00
# endif
2014-07-03 13:25:24 +01:00
else if ( * ptr = = ( mp_uint_t ) & mp_type_fun_bc ) { c = ' B ' ; }
2014-10-24 23:12:25 +01:00
else if ( * ptr = = ( mp_uint_t ) & mp_type_module ) { c = ' M ' ; }
2015-01-11 14:37:06 +00:00
else {
c = ' h ' ;
#if 0
// This code prints "Q" for qstr-pool data, and "q" for qstr-str
// data. It can be useful to see how qstrs are being allocated,
// but is disabled by default because it is very slow.
for (qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) {
if ((qstr_pool_t*)ptr == pool) {
c = 'Q';
break;
}
for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) {
if ((const byte*)ptr == *q) {
c = 'q';
break;
}
}
}
#endif
}
2014-04-25 23:37:55 +01:00
break ;
}
2014-02-26 22:55:59 +00:00
case AT_TAIL : c = ' t ' ; break ;
case AT_MARK : c = ' m ' ; break ;
}
2015-04-11 12:15:47 +01:00
mp_printf ( & mp_plat_print , " %c " , c ) ;
2013-10-22 15:25:25 +01:00
}
2015-04-11 12:15:47 +01:00
mp_print_str ( & mp_plat_print , " \n " ) ;
2013-10-22 15:25:25 +01:00
}
2014-02-26 22:55:59 +00:00
# if DEBUG_PRINT
2014-02-10 21:45:54 +02:00
void gc_test ( void ) {
2014-07-03 13:25:24 +01:00
mp_uint_t len = 500 ;
mp_uint_t * heap = malloc ( len ) ;
gc_init ( heap , heap + len / sizeof ( mp_uint_t ) ) ;
2013-10-21 23:45:08 +01:00
void * ptrs [ 100 ] ;
{
2014-07-03 13:25:24 +01:00
mp_uint_t * * p = gc_alloc ( 16 , false ) ;
2014-04-05 20:35:48 +01:00
p [ 0 ] = gc_alloc ( 64 , false ) ;
p [ 1 ] = gc_alloc ( 1 , false ) ;
p [ 2 ] = gc_alloc ( 1 , false ) ;
p [ 3 ] = gc_alloc ( 1 , false ) ;
2014-07-03 13:25:24 +01:00
mp_uint_t * * * p2 = gc_alloc ( 16 , false ) ;
2013-10-21 23:45:08 +01:00
p2 [ 0 ] = p ;
p2 [ 1 ] = p ;
ptrs [ 0 ] = p2 ;
}
2014-02-10 21:45:54 +02:00
for ( int i = 0 ; i < 25 ; i + = 2 ) {
2014-07-03 13:25:24 +01:00
mp_uint_t * p = gc_alloc ( i , false ) ;
2013-10-21 23:45:08 +01:00
printf ( " p=%p \n " , p ) ;
if ( i & 3 ) {
//ptrs[i] = p;
}
}
2014-02-10 21:45:54 +02:00
printf ( " Before GC: \n " ) ;
2014-02-26 22:55:59 +00:00
gc_dump_alloc_table ( ) ;
2014-02-10 21:45:54 +02:00
printf ( " Starting GC... \n " ) ;
gc_collect_start ( ) ;
gc_collect_root ( ptrs , sizeof ( ptrs ) / sizeof ( void * ) ) ;
gc_collect_end ( ) ;
printf ( " After GC: \n " ) ;
2014-02-26 22:55:59 +00:00
gc_dump_alloc_table ( ) ;
2013-10-21 23:45:08 +01:00
}
2014-02-10 21:45:54 +02:00
# endif
2014-01-07 15:20:33 +00:00
# endif // MICROPY_ENABLE_GC