static void test_check_free_block(void) { Heap_Block *block = NULL; Heap_Block *next_block = NULL; Heap_Block *first_free_block = NULL; Heap_Block *secound_free_block = NULL; void *p1 = NULL; puts( "test the _Heap_Walk_check_free_block() function" ); puts( "\tset a previous size for the next block which is not equal to the size of the actual block" ); test_heap_init_default(); block = _Heap_Free_list_first( &TestHeap ); next_block = _Heap_Block_at( block, _Heap_Block_size( block ) ); next_block->prev_size = _Heap_Block_size( block ) - 1; test_call_heap_walk( false ); puts( "\tclear the previous_used flag of the first free block after an used block" ); test_heap_init_default(); p1 = test_allocate_block(); block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); first_free_block = _Heap_Free_list_first( &TestHeap ); first_free_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; first_free_block->prev_size = _Heap_Block_size( block ); _Heap_Free_list_insert_after( first_free_block, block ); test_call_heap_walk( false ); puts( "\ttake a free block out of the free list" ); test_heap_init_custom(); test_create_heap_with_gaps(); first_free_block = _Heap_Free_list_first( &TestHeap ); secound_free_block = first_free_block->next; _Heap_Free_list_remove( secound_free_block ); test_call_heap_walk( false ); }
static void test_heap_do_block_allocate( int variant, void *p2 ) { Heap_Block *const block = _Heap_Block_of_alloc_area( (uintptr_t) p2, test_page_size()); uintptr_t const alloc_box_begin = _Heap_Alloc_area_of_block( block ); uintptr_t const alloc_box_size = _Heap_Block_size( block ); uintptr_t const alloc_box_end = alloc_box_begin + alloc_box_size; uintptr_t alloc_begin = 0; uintptr_t alloc_size = 0; puts( "\tallocate block at the beginning"); alloc_begin = alloc_box_begin; alloc_size = 0; test_block_alloc( variant, 0, alloc_begin, alloc_size ); puts( "\tallocate block full space"); alloc_begin = alloc_box_begin; alloc_size = alloc_box_size + HEAP_ALLOC_BONUS - HEAP_BLOCK_HEADER_SIZE; test_block_alloc( variant, 1, alloc_begin, alloc_size ); puts( "\tallocate block in the middle"); alloc_begin = alloc_box_begin + TEST_DEFAULT_PAGE_SIZE; alloc_size = 0; test_block_alloc( variant, 2, alloc_begin, alloc_size ); puts( "\tallocate block at the end"); alloc_begin = alloc_box_end - TEST_DEFAULT_PAGE_SIZE; alloc_size = TEST_DEFAULT_PAGE_SIZE + HEAP_ALLOC_BONUS - HEAP_BLOCK_HEADER_SIZE; test_block_alloc( variant, 3, alloc_begin, alloc_size ); }
Heap_Resize_status _Heap_Resize_block( Heap_Control *heap, void *alloc_begin_ptr, uintptr_t new_alloc_size, uintptr_t *old_size, uintptr_t *new_size ) { uintptr_t const page_size = heap->page_size; uintptr_t const alloc_begin = (uintptr_t) alloc_begin_ptr; Heap_Block *const block = _Heap_Block_of_alloc_area( alloc_begin, page_size ); *old_size = 0; *new_size = 0; if ( _Heap_Is_block_in_heap( heap, block ) ) { _Heap_Protection_block_check( heap, block ); return _Heap_Resize_block_checked( heap, block, alloc_begin, new_alloc_size, old_size, new_size ); } return HEAP_RESIZE_FATAL_ERROR; }
static uintptr_t _Heap_Check_block( const Heap_Control *heap, const Heap_Block *block, uintptr_t alloc_size, uintptr_t alignment, uintptr_t boundary ) { uintptr_t const page_size = heap->page_size; uintptr_t const min_block_size = heap->min_block_size; uintptr_t const block_begin = (uintptr_t) block; uintptr_t const block_size = _Heap_Block_size( block ); uintptr_t const block_end = block_begin + block_size; uintptr_t const alloc_begin_floor = _Heap_Alloc_area_of_block( block ); uintptr_t const alloc_begin_ceiling = block_end - min_block_size + HEAP_BLOCK_HEADER_SIZE + page_size - 1; uintptr_t alloc_end = block_end + HEAP_ALLOC_BONUS; uintptr_t alloc_begin = alloc_end - alloc_size; alloc_begin = _Heap_Align_down( alloc_begin, alignment ); /* Ensure that the we have a valid new block at the end */ if ( alloc_begin > alloc_begin_ceiling ) { alloc_begin = _Heap_Align_down( alloc_begin_ceiling, alignment ); } alloc_end = alloc_begin + alloc_size; /* Ensure boundary constaint */ if ( boundary != 0 ) { uintptr_t const boundary_floor = alloc_begin_floor + alloc_size; uintptr_t boundary_line = _Heap_Align_down( alloc_end, boundary ); while ( alloc_begin < boundary_line && boundary_line < alloc_end ) { if ( boundary_line < boundary_floor ) { return 0; } alloc_begin = boundary_line - alloc_size; alloc_begin = _Heap_Align_down( alloc_begin, alignment ); alloc_end = alloc_begin + alloc_size; boundary_line = _Heap_Align_down( alloc_end, boundary ); } } /* Ensure that the we have a valid new block at the beginning */ if ( alloc_begin >= alloc_begin_floor ) { uintptr_t const alloc_block_begin = (uintptr_t) _Heap_Block_of_alloc_area( alloc_begin, page_size ); uintptr_t const free_size = alloc_block_begin - block_begin; if ( free_size >= min_block_size || free_size == 0 ) { return alloc_begin; } } return 0; }
bool _Heap_Size_of_alloc_area( Heap_Control *heap, void *alloc_begin_ptr, uintptr_t *alloc_size ) { uintptr_t const page_size = heap->page_size; uintptr_t const alloc_begin = (uintptr_t) alloc_begin_ptr; Heap_Block *block = _Heap_Block_of_alloc_area( alloc_begin, page_size ); Heap_Block *next_block = NULL; uintptr_t block_size = 0; if ( !_Heap_Is_block_in_heap( heap, block ) ) { return false; } block_size = _Heap_Block_size( block ); next_block = _Heap_Block_at( block, block_size ); if ( !_Heap_Is_block_in_heap( heap, next_block ) || !_Heap_Is_prev_used( next_block ) ) { return false; } *alloc_size = (uintptr_t) next_block + HEAP_BLOCK_SIZE_OFFSET - alloc_begin; return true; }
static Heap_Block *_Heap_Block_allocate_from_end( Heap_Control *heap, Heap_Block *block, Heap_Block *free_list_anchor, uintptr_t alloc_begin, uintptr_t alloc_size ) { Heap_Statistics *const stats = &heap->stats; uintptr_t block_begin = (uintptr_t) block; uintptr_t block_size = _Heap_Block_size( block ); uintptr_t block_end = block_begin + block_size; Heap_Block *const new_block = _Heap_Block_of_alloc_area( alloc_begin, heap->page_size ); uintptr_t const new_block_begin = (uintptr_t) new_block; uintptr_t const new_block_size = block_end - new_block_begin; block_end = new_block_begin; block_size = block_end - block_begin; _HAssert( block_size >= heap->min_block_size ); _HAssert( new_block_size >= heap->min_block_size ); /* Statistics */ stats->free_size += block_size; if ( _Heap_Is_prev_used( block ) ) { _Heap_Free_list_insert_after( free_list_anchor, block ); free_list_anchor = block; /* Statistics */ ++stats->free_blocks; } else { Heap_Block *const prev_block = _Heap_Prev_block( block ); uintptr_t const prev_block_size = _Heap_Block_size( prev_block ); block = prev_block; block_begin = (uintptr_t) block; block_size += prev_block_size; } block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED; new_block->prev_size = block_size; new_block->size_and_flag = new_block_size; _Heap_Block_split( heap, new_block, free_list_anchor, alloc_size ); return new_block; }
static void test_check_free_list(void) { void *p1 = NULL; Heap_Block *first_free_block = NULL; Heap_Block *secound_free_block = NULL; Heap_Block *third_free_block = NULL; Heap_Block *used_block = NULL; puts( "testing the _Heap_Walk_check_free_list() function" ); puts( "\tno free blocks" ); test_heap_init_custom(); test_fill_heap(); test_call_heap_walk( true ); puts( "\tcreate a loop in the free list" ); test_heap_init_default(); test_create_heap_with_gaps(); /* find free blocks */ first_free_block = _Heap_Free_list_first( &TestHeap ); secound_free_block = first_free_block->next; third_free_block = secound_free_block->next; /* create a loop */ third_free_block->next = secound_free_block; secound_free_block->prev = third_free_block; test_call_heap_walk( false ); puts( "\tput a block outside the heap to the free list" ); test_heap_init_default(); first_free_block = _Heap_Free_list_first( &TestHeap ); first_free_block->next = TestHeap.first_block - 1; test_call_heap_walk( false ); puts( "\tput a block on the free list, which is not page-aligned" ); test_heap_init_custom(); test_create_heap_with_gaps(); first_free_block = _Heap_Free_list_first( &TestHeap ); first_free_block->next = (Heap_Block *) ((uintptr_t) first_free_block->next + CPU_ALIGNMENT); first_free_block->next->prev = first_free_block; test_call_heap_walk( false ); puts( "\tput a used block on the free list" ); test_heap_init_custom(); test_create_heap_with_gaps(); p1 = test_allocate_block(); first_free_block = _Heap_Free_list_first( &TestHeap ); used_block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); _Heap_Free_list_insert_after( first_free_block, used_block ); test_call_heap_walk( false ); }
Heap_Block *_Heap_Greedy_allocate( Heap_Control *heap, const uintptr_t *block_sizes, size_t block_count ) { Heap_Block *const free_list_tail = _Heap_Free_list_tail( heap ); Heap_Block *allocated_blocks = NULL; Heap_Block *blocks = NULL; Heap_Block *current; size_t i; _Heap_Protection_free_all_delayed_blocks( heap ); for (i = 0; i < block_count; ++i) { void *next = _Heap_Allocate( heap, block_sizes [i] ); if ( next != NULL ) { Heap_Block *next_block = _Heap_Block_of_alloc_area( (uintptr_t) next, heap->page_size ); next_block->next = allocated_blocks; allocated_blocks = next_block; } } while ( (current = _Heap_Free_list_first( heap )) != free_list_tail ) { _Heap_Block_allocate( heap, current, _Heap_Alloc_area_of_block( current ), _Heap_Block_size( current ) - HEAP_BLOCK_HEADER_SIZE ); current->next = blocks; blocks = current; } while ( allocated_blocks != NULL ) { current = allocated_blocks; allocated_blocks = allocated_blocks->next; _Heap_Free( heap, (void *) _Heap_Alloc_area_of_block( current ) ); } return blocks; }
static void test_heap_free(void) { Heap_Control *heap = &TestHeap; void *p; Heap_Block *block; bool ok; _Heap_Initialize( heap, &TestHeapMemory[0], sizeof(TestHeapMemory), 0 ); p = _Heap_Allocate( heap, 1 ); rtems_test_assert( p != NULL ); block = _Heap_Block_of_alloc_area( (uintptr_t) p, heap->page_size ); /* * This will kick the next block outside of the heap area and the next * _Heap_Free() will detect this. */ block->size_and_flag += sizeof(TestHeapMemory); ok = _Heap_Free( heap, p ); rtems_test_assert( !ok ); }
static void test_heap_resize_block(void) { void *p1, *p2, *p3; uintptr_t new_alloc_size = 0; Heap_Block *block = NULL; puts( "run tests for _Heap_Resize_Block()" ); puts( "\tgive a block outside the heap to the function" ); test_heap_init( TEST_DEFAULT_PAGE_SIZE ); p1 = TestHeap.first_block - TEST_DEFAULT_PAGE_SIZE; new_alloc_size = 1; test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_FATAL_ERROR ); puts( "\tincrease size"); puts( "\t\tlet the next block be used alredy and try to get a size bigger than the actual block" ); test_heap_init( TEST_DEFAULT_PAGE_SIZE ); p1 = test_alloc_one_page(); rtems_test_assert( p1 ); p2 = test_alloc_one_page(); rtems_test_assert( p2 ); new_alloc_size = 3 * TEST_DEFAULT_PAGE_SIZE / 2; test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_UNSATISFIED ); puts( "\t\tnext block not used and try to set the new allocation size between the page-alignments" ); test_heap_init( TEST_DEFAULT_PAGE_SIZE ); p1 = test_alloc_one_page(); new_alloc_size = 3 * TEST_DEFAULT_PAGE_SIZE / 2; test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_SUCCESSFUL ); puts( "\t\tlet the block after the next be used and try to allocate more then one pagesize more" ); test_heap_init( TEST_DEFAULT_PAGE_SIZE ); p1 = test_alloc_one_page(); rtems_test_assert( p1 ); p2 = test_alloc_one_page(); rtems_test_assert( p2 ); p3 = test_alloc_one_page(); rtems_test_assert( p3 ); test_free( p2 ); new_alloc_size = 5 * TEST_DEFAULT_PAGE_SIZE / 2; test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_UNSATISFIED ); puts( "\ttry to resize to the same size" ); test_heap_init( TEST_DEFAULT_PAGE_SIZE ); p1 = test_alloc_one_page(); block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); new_alloc_size = _Heap_Block_size( block ); test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_SUCCESSFUL ); puts( "\tdecrease size"); puts( "\t\tdecrease a block with two pages to one page" ); test_heap_init( TEST_DEFAULT_PAGE_SIZE ); p1 = test_alloc_two_pages(); new_alloc_size = 1; test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_SUCCESSFUL ); puts( "\t\tresize the block to the size 0" ); test_heap_init( TEST_DEFAULT_PAGE_SIZE ); p1 = test_alloc_one_page(); new_alloc_size = 0; test_simple_resize_block( p1, new_alloc_size, HEAP_RESIZE_SUCCESSFUL ); }
static void test_block_alloc( int free_variant, int alloc_variant, uintptr_t alloc_begin, uintptr_t alloc_size ) { void *p1 = NULL; void *p2 = NULL; void *p3 = NULL; uintptr_t size_fresh_heap = 0; uintptr_t pages_per_default_block = 0; uint32_t exp_free_pages = 0; uint32_t exp_free_blocks = 0; uint32_t exp_used_blocks = 0; test_heap_init( TEST_DEFAULT_PAGE_SIZE ); size_fresh_heap = _Heap_Get_size( &TestHeap ); exp_free_pages = size_fresh_heap / TestHeap.page_size; p1 = test_create_used_block(); p2 = test_create_used_block(); p3 = test_create_used_block(); pages_per_default_block = _Heap_Block_size( _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ) ) / TestHeap.page_size; if (free_variant == 1) { test_free( p1 ); } else if (free_variant == 2) { test_free( p3 ); } else if (free_variant == 3) { test_free( p2 ); test_free( p3 ); } _Heap_Block_allocate( &TestHeap, _Heap_Block_of_alloc_area( (uintptr_t) p2, test_page_size()), alloc_begin, alloc_size ); test_check_alloc_simple( (void *) alloc_begin, alloc_size, 0, 0 ); /* check statistics */ switch( free_variant ) { case 1: exp_free_pages = exp_free_pages - 2 * pages_per_default_block; exp_used_blocks = 2; switch( alloc_variant ) { case 1: /* allocate block full space */ exp_free_blocks = 2; break; case 2: /* allocate block in the middle */ exp_free_pages = exp_free_pages + pages_per_default_block - 1; exp_free_blocks = 3; break; case 3: /* allocate block at the end */ exp_free_pages = exp_free_pages + pages_per_default_block - 2; exp_free_blocks = 2; break; default: /* allocate block at the beginning */ exp_free_pages = exp_free_pages + pages_per_default_block - 1; exp_free_blocks = 3; break; } break; case 2: exp_free_pages = exp_free_pages - 2 * pages_per_default_block; exp_used_blocks = 2; switch( alloc_variant ) { case 1: /* allocate block full space */ exp_free_blocks = 1; break; case 2: /* allocate block in the middle */ exp_free_pages = exp_free_pages + pages_per_default_block - 1; exp_free_blocks = 2; break; case 3: /* allocate block at the end */ exp_free_pages = exp_free_pages + pages_per_default_block - 1; exp_free_blocks = 2; break; default: /* allocate block at the beginning */ exp_free_pages = exp_free_pages + pages_per_default_block - 1; exp_free_blocks = 1; break; } break; case 3: exp_free_pages = exp_free_pages - pages_per_default_block; exp_used_blocks = 2; switch( alloc_variant ) { case 1: /* allocate block full space */ exp_free_pages = exp_free_pages - pages_per_default_block; exp_free_blocks = 1; break; case 2: /* allocate block in the middle */ exp_free_pages = exp_free_pages - 1; exp_free_blocks = 2; break; case 3: /* allocate block at the end */ exp_free_pages = exp_free_pages - 2; exp_free_blocks = 2; break; default: /* allocate block at the beginning */ exp_free_pages = exp_free_pages - 1; exp_free_blocks = 1; break; } break; default: exp_free_pages = exp_free_pages - 3 * pages_per_default_block; exp_used_blocks = 3; switch( alloc_variant ) { case 1: /* allocate block full space */ exp_free_blocks = 1; break; case 2: /* allocate block in the middle */ exp_free_blocks = 3; exp_free_pages = exp_free_pages + pages_per_default_block - 1; break; case 3: /* allocate block at the end */ exp_free_blocks = 2; exp_free_pages = exp_free_pages + pages_per_default_block - 1; break; default: /* allocate block at the beginning */ exp_free_blocks = 2; exp_free_pages = exp_free_pages + pages_per_default_block - 1; } } rtems_test_assert( TestHeap.stats.free_size == exp_free_pages * TestHeap.page_size ); rtems_test_assert( TestHeap.stats.free_blocks == exp_free_blocks ); rtems_test_assert( TestHeap.stats.used_blocks == exp_used_blocks ); }
static void test_check_alloc( void *alloc_begin_ptr, void *expected_alloc_begin_ptr, uintptr_t alloc_size, uintptr_t alignment, uintptr_t boundary ) { uintptr_t const min_block_size = TestHeap.min_block_size; uintptr_t const page_size = TestHeap.page_size; rtems_test_assert( alloc_begin_ptr == expected_alloc_begin_ptr ); if( expected_alloc_begin_ptr != NULL ) { uintptr_t const alloc_begin = (uintptr_t ) alloc_begin_ptr; uintptr_t const alloc_end = alloc_begin + alloc_size; uintptr_t const alloc_area_begin = _Heap_Align_down( alloc_begin, page_size ); uintptr_t const alloc_area_offset = alloc_begin - alloc_area_begin; #if UNUSED uintptr_t const alloc_area_size = alloc_area_offset + alloc_size; #endif Heap_Block *block = _Heap_Block_of_alloc_area( alloc_area_begin, page_size ); uintptr_t const block_begin = (uintptr_t ) block; uintptr_t const block_size = _Heap_Block_size( block ); uintptr_t const block_end = block_begin + block_size; rtems_test_assert( block_size >= min_block_size ); rtems_test_assert( block_begin < block_end ); rtems_test_assert( _Heap_Is_aligned( block_begin + HEAP_BLOCK_HEADER_SIZE, page_size ) ); rtems_test_assert( _Heap_Is_aligned( block_size, page_size ) ); rtems_test_assert( alloc_end <= block_end + HEAP_ALLOC_BONUS ); rtems_test_assert( alloc_area_begin > block_begin ); rtems_test_assert( alloc_area_offset < page_size ); rtems_test_assert( _Heap_Is_aligned( alloc_area_begin, page_size ) ); if ( alignment == 0 ) { rtems_test_assert( alloc_begin == alloc_area_begin ); } else { rtems_test_assert( _Heap_Is_aligned( alloc_begin, alignment ) ); } if ( boundary != 0 ) { uintptr_t boundary_line = _Heap_Align_down( alloc_end, boundary ); rtems_test_assert( alloc_size <= boundary ); rtems_test_assert( boundary_line <= alloc_begin || alloc_end <= boundary_line ); } } rtems_test_assert( page_size < CPU_ALIGNMENT || _Heap_Walk( &TestHeap, 0, false ) ); }
bool _Heap_Free( Heap_Control *heap, void *alloc_begin_ptr ) { Heap_Statistics *const stats = &heap->stats; uintptr_t alloc_begin = (uintptr_t) alloc_begin_ptr; Heap_Block *block = _Heap_Block_of_alloc_area( alloc_begin, heap->page_size ); Heap_Block *next_block = NULL; uintptr_t block_size = 0; uintptr_t next_block_size = 0; bool next_is_free = false; if ( !_Heap_Is_block_in_heap( heap, block ) ) { return false; } block_size = _Heap_Block_size( block ); next_block = _Heap_Block_at( block, block_size ); if ( !_Heap_Is_block_in_heap( heap, next_block ) ) { _HAssert( false ); return false; } if ( !_Heap_Is_prev_used( next_block ) ) { _HAssert( false ); return false; } next_block_size = _Heap_Block_size( next_block ); next_is_free = next_block != heap->last_block && !_Heap_Is_prev_used( _Heap_Block_at( next_block, next_block_size )); if ( !_Heap_Is_prev_used( block ) ) { uintptr_t const prev_size = block->prev_size; Heap_Block * const prev_block = _Heap_Block_at( block, -prev_size ); if ( !_Heap_Is_block_in_heap( heap, prev_block ) ) { _HAssert( false ); return( false ); } /* As we always coalesce free blocks, the block that preceedes prev_block must have been used. */ if ( !_Heap_Is_prev_used ( prev_block) ) { _HAssert( false ); return( false ); } if ( next_is_free ) { /* coalesce both */ uintptr_t const size = block_size + prev_size + next_block_size; _Heap_Free_list_remove( next_block ); stats->free_blocks -= 1; prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block = _Heap_Block_at( prev_block, size ); _HAssert(!_Heap_Is_prev_used( next_block)); next_block->prev_size = size; } else { /* coalesce prev */ uintptr_t const size = block_size + prev_size; prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; next_block->prev_size = size; } } else if ( next_is_free ) { /* coalesce next */ uintptr_t const size = block_size + next_block_size; _Heap_Free_list_replace( next_block, block ); block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block = _Heap_Block_at( block, size ); next_block->prev_size = size; } else { /* no coalesce */ /* Add 'block' to the head of the free blocks list as it tends to produce less fragmentation than adding to the tail. */ _Heap_Free_list_insert_after( _Heap_Free_list_head( heap), block ); block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; next_block->prev_size = block_size; /* Statistics */ ++stats->free_blocks; if ( stats->max_free_blocks < stats->free_blocks ) { stats->max_free_blocks = stats->free_blocks; } } /* Statistics */ --stats->used_blocks; ++stats->frees; stats->free_size += block_size; return( true ); }
uintptr_t _Heap_Extend( Heap_Control *heap, void *extend_area_begin_ptr, uintptr_t extend_area_size, uintptr_t unused __attribute__((unused)) ) { Heap_Statistics *const stats = &heap->stats; Heap_Block *const first_block = heap->first_block; Heap_Block *start_block = first_block; Heap_Block *merge_below_block = NULL; Heap_Block *merge_above_block = NULL; Heap_Block *link_below_block = NULL; Heap_Block *link_above_block = NULL; Heap_Block *extend_first_block = NULL; Heap_Block *extend_last_block = NULL; uintptr_t const page_size = heap->page_size; uintptr_t const min_block_size = heap->min_block_size; uintptr_t const extend_area_begin = (uintptr_t) extend_area_begin_ptr; uintptr_t const extend_area_end = extend_area_begin + extend_area_size; uintptr_t const free_size = stats->free_size; uintptr_t extend_first_block_size = 0; uintptr_t extended_size = 0; bool extend_area_ok = false; if ( extend_area_end < extend_area_begin ) { return 0; } extend_area_ok = _Heap_Get_first_and_last_block( extend_area_begin, extend_area_size, page_size, min_block_size, &extend_first_block, &extend_last_block ); if (!extend_area_ok ) { /* For simplicity we reject extend areas that are too small */ return 0; } do { uintptr_t const sub_area_begin = (start_block != first_block) ? (uintptr_t) start_block : heap->area_begin; uintptr_t const sub_area_end = start_block->prev_size; Heap_Block *const end_block = _Heap_Block_of_alloc_area( sub_area_end, page_size ); if ( sub_area_end > extend_area_begin && extend_area_end > sub_area_begin ) { return 0; } if ( extend_area_end == sub_area_begin ) { merge_below_block = start_block; } else if ( extend_area_end < sub_area_end ) { link_below_block = start_block; } if ( sub_area_end == extend_area_begin ) { start_block->prev_size = extend_area_end; merge_above_block = end_block; } else if ( sub_area_end < extend_area_begin ) { link_above_block = end_block; } start_block = _Heap_Block_at( end_block, _Heap_Block_size( end_block ) ); } while ( start_block != first_block ); if ( extend_area_begin < heap->area_begin ) { heap->area_begin = extend_area_begin; } else if ( heap->area_end < extend_area_end ) { heap->area_end = extend_area_end; } extend_first_block_size = (uintptr_t) extend_last_block - (uintptr_t) extend_first_block; extend_first_block->prev_size = extend_area_end; extend_first_block->size_and_flag = extend_first_block_size | HEAP_PREV_BLOCK_USED; _Heap_Protection_block_initialize( heap, extend_first_block ); extend_last_block->prev_size = extend_first_block_size; extend_last_block->size_and_flag = 0; _Heap_Protection_block_initialize( heap, extend_last_block ); if ( (uintptr_t) extend_first_block < (uintptr_t) heap->first_block ) { heap->first_block = extend_first_block; } else if ( (uintptr_t) extend_last_block > (uintptr_t) heap->last_block ) { heap->last_block = extend_last_block; } if ( merge_below_block != NULL ) { _Heap_Merge_below( heap, extend_area_begin, merge_below_block ); } else if ( link_below_block != NULL ) { _Heap_Link_below( link_below_block, extend_last_block ); } if ( merge_above_block != NULL ) { _Heap_Merge_above( heap, merge_above_block, extend_area_end ); } else if ( link_above_block != NULL ) { _Heap_Link_above( link_above_block, extend_first_block, extend_last_block ); } if ( merge_below_block == NULL && merge_above_block == NULL ) { _Heap_Free_block( heap, extend_first_block ); } _Heap_Set_last_block_size( heap ); extended_size = stats->free_size - free_size; /* Statistics */ stats->size += extended_size; return extended_size; }
static void test_main_loop(void) { void *p1 = NULL; void *p2 = NULL; Heap_Block *block = NULL; Heap_Block *next_block = NULL; puts( "Test the main loop" ); puts( "\tset the blocksize so, that the next block is outside the heap" ); test_heap_init_custom(); /* use all blocks */ p1 = test_fill_heap(); block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); block->size_and_flag = ( 2 * _Heap_Block_size( block ) ) | HEAP_PREV_BLOCK_USED; test_call_heap_walk( false ); puts( "\twalk a heap with blocks with different states of the previous-used flag" ); test_heap_init_custom(); test_create_heap_with_gaps(); test_allocate_block(); /* fill one gap */ test_call_heap_walk( true ); puts( "\tcreate a block with a not page aligned size" ); test_heap_init_custom(); p1 = test_allocate_block(); p2 = test_allocate_block(); _Heap_Free( &TestHeap, p1 ); block = _Heap_Block_of_alloc_area( (uintptr_t) p2, TestHeap.page_size ); block->size_and_flag = (3 * TestHeap.page_size / 2) & ~HEAP_PREV_BLOCK_USED; test_call_heap_walk( false ); puts( "\tcreate a block with a size smaller than the min_block_size" ); test_heap_init_default(); p1 = test_allocate_block(); p2 = test_allocate_block(); _Heap_Free( &TestHeap, p1 ); block = _Heap_Block_of_alloc_area( (uintptr_t) p2, TestHeap.page_size ); block->size_and_flag = 0; test_call_heap_walk( false ); puts( "\tmake a block with a size, so that the block reaches into the next block" ); test_heap_init_default(); p1 = test_allocate_block(); p2 = test_allocate_block(); block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); block->size_and_flag = ( 3 * _Heap_Block_size( block ) / 2 ) | HEAP_PREV_BLOCK_USED; test_call_heap_walk( false ); puts( "\tcreate a block with invalid successor" ); test_heap_init_default(); test_allocate_block(); p1 = test_allocate_block(); block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); block->size_and_flag = (0 - TestHeap.page_size) | HEAP_PREV_BLOCK_USED; test_call_heap_walk( false ); puts( "\tmake a block with a size, so that it includes the next block" ); test_heap_init_default(); p1 = test_allocate_block(); p2 = test_allocate_block(); block = _Heap_Block_of_alloc_area( (uintptr_t) p1, TestHeap.page_size ); next_block = _Heap_Block_at( block, _Heap_Block_size( block ) ); block->size_and_flag = ( _Heap_Block_size( block ) + _Heap_Block_size( next_block ) ) | HEAP_PREV_BLOCK_USED; test_call_heap_walk( true ); }
bool _Heap_Free( Heap_Control *heap, void *alloc_begin_ptr ) { Heap_Statistics *const stats = &heap->stats; uintptr_t alloc_begin; Heap_Block *block; Heap_Block *next_block = NULL; uintptr_t block_size = 0; uintptr_t next_block_size = 0; bool next_is_free = false; /* * If NULL return true so a free on NULL is considered a valid release. This * is a special case that could be handled by the in heap check how-ever that * would result in false being returned which is wrong. */ if ( alloc_begin_ptr == NULL ) { return true; } alloc_begin = (uintptr_t) alloc_begin_ptr; block = _Heap_Block_of_alloc_area( alloc_begin, heap->page_size ); if ( !_Heap_Is_block_in_heap( heap, block ) ) { return false; } _Heap_Protection_block_check( heap, block ); block_size = _Heap_Block_size( block ); next_block = _Heap_Block_at( block, block_size ); if ( !_Heap_Is_block_in_heap( heap, next_block ) ) { return false; } _Heap_Protection_block_check( heap, next_block ); if ( !_Heap_Is_prev_used( next_block ) ) { _Heap_Protection_block_error( heap, block ); return false; } if ( !_Heap_Protection_determine_block_free( heap, block ) ) { return true; } next_block_size = _Heap_Block_size( next_block ); next_is_free = next_block != heap->last_block && !_Heap_Is_prev_used( _Heap_Block_at( next_block, next_block_size )); if ( !_Heap_Is_prev_used( block ) ) { uintptr_t const prev_size = block->prev_size; Heap_Block * const prev_block = _Heap_Block_at( block, -prev_size ); if ( !_Heap_Is_block_in_heap( heap, prev_block ) ) { _HAssert( false ); return( false ); } /* As we always coalesce free blocks, the block that preceedes prev_block must have been used. */ if ( !_Heap_Is_prev_used ( prev_block) ) { _HAssert( false ); return( false ); } if ( next_is_free ) { /* coalesce both */ uintptr_t const size = block_size + prev_size + next_block_size; _Heap_Free_list_remove( next_block ); stats->free_blocks -= 1; prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block = _Heap_Block_at( prev_block, size ); _HAssert(!_Heap_Is_prev_used( next_block)); next_block->prev_size = size; } else { /* coalesce prev */ uintptr_t const size = block_size + prev_size; prev_block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; next_block->prev_size = size; } } else if ( next_is_free ) { /* coalesce next */ uintptr_t const size = block_size + next_block_size; _Heap_Free_list_replace( next_block, block ); block->size_and_flag = size | HEAP_PREV_BLOCK_USED; next_block = _Heap_Block_at( block, size ); next_block->prev_size = size; } else { /* no coalesce */ /* Add 'block' to the head of the free blocks list as it tends to produce less fragmentation than adding to the tail. */ _Heap_Free_list_insert_after( _Heap_Free_list_head( heap), block ); block->size_and_flag = block_size | HEAP_PREV_BLOCK_USED; next_block->size_and_flag &= ~HEAP_PREV_BLOCK_USED; next_block->prev_size = block_size; /* Statistics */ ++stats->free_blocks; if ( stats->max_free_blocks < stats->free_blocks ) { stats->max_free_blocks = stats->free_blocks; } } /* Statistics */ --stats->used_blocks; ++stats->frees; stats->free_size += block_size; return( true ); }