source: rtems/cpukit/score/src/heapresizeblock.c @ 80f2885b

4.104.114.84.95
Last change on this file since 80f2885b was 80f2885b, checked in by Joel Sherrill <joel.sherrill@…>, on 05/20/05 at 19:15:41

2005-05-14 Sergei Organov <osv@…>

PR 746/rtems
Optimize realloc(). The problem is that realloc() can neither grow
nor shrink efficiently the current memory region without support
from underlying heap/region modules. The patch introduces one new
routine for each of heap and region modules, _Heap_Resize_block(),
and rtems_region_resize_segment(), respectively, and uses the
latter to optimize realloc().

The implementation of _Heap_Resize_block() lead to changing of the
heap allocation strategy: now the heap manager, when splits larger
free block into used and new free parts, makes the first part of
the block used, not the last one as it was before. Due to this new
strategy, _Heap_Resize_block() never needs to change the user
pointer.

Caveat: unlike previous heap implementation, first few bytes of
the contents of the memory allocated from the heap are now almost
never all zero. This can trigger bugs in client code that have not
been visible before this patch.

  • libcsupport/src/malloc.c (realloc): try to resize segment in place using new rtems_region_resize_segment() routine before falling back to the malloc()/free() method.
  • score/src/heap.c: (_Heap_Initialize): change initial heap layout to reflect new allocation strategy of using of the lower part of a previously free block when splitting it for the purpose of allocation. (_Heap_Block_allocate): when split, make the lower part used, and leave the upper part free. Return type changed from Heap_Block* to uint32_t.
  • score/include/rtems/score/heap.h: (Heap_Statistics): added 'resizes' field. (Heap_Resize_status): new enum. (_Heap_Resize_block): new routine. (_Heap_Block_allocate): return type changed from Heap_Block* to uint32_t.
  • score/src/heapwalk.c: reflect new heap layout in checks.
  • score/src/heapsizeofuserarea.c: more assertions added.
  • score/src/heapresizeblock.c: new file. (_Heap_Resize_block): new routine.
  • score/src/heapfree.c: reverse the checks _Heap_Is_block_in() and _Heap_Is_prev_used() on entry to be in this order.
  • score/src/heapallocate.c, score/src/heapallocatealigned.c: ignore return value of _Heap_Block_allocate().
  • score/Makefile.am (HEAP_C_FILES): added src/heapresizeblock.c.
  • rtems/include/rtems/rtems/region.h: (rtems_region_resize_segment): new interface routine. (_Region_Process_queue): new internal routine called from rtems_region_resize_segment() and rtems_region_return_segment().
  • rtems/src/regionreturnsegment.c: move queue management code into the new internal routine _Region_Process_queue() and call it.
  • rtems/src/regionresizesegment.c: new file. (rtems_region_resize_segment): new interface routine.
  • rtems/src/regionprocessqueue.c: new file. (_Region_Process_queue): new internal routine containing queue management code factored out from 'regionreturnsegment.c'.
  • rtems/Makefile.am (REGION_C_FILES): Added src/regionresizesegment.c, and src/regionprocessqueue.c.
  • ada/rtems.adb, ada/rtems.ads: Added Region_Resize_Segment.
  • Property mode set to 100644
File size: 5.9 KB
Line 
1/*
2 *  Heap Handler
3 *
4 *  COPYRIGHT (c) 1989-1999.
5 *  On-Line Applications Research Corporation (OAR).
6 *
7 *  The license and distribution terms for this file may be
8 *  found in the file LICENSE in this distribution or at
9 *  http://www.rtems.com/license/LICENSE.
10 *
11 *  $Id$
12 */
13
14
15#include <rtems/system.h>
16#include <rtems/score/sysstate.h>
17#include <rtems/score/heap.h>
18
19/*
20 *  _Heap_Resize_block
21 *
22 *  DESCRIPTION:
23 *
24 *  This routine tries to resize in place the block that is pointed to by the
25 *  'starting_address' to the new 'size'.
26 *
27 *  Input parameters:
28 *    the_heap         - pointer to heap header
29 *    starting_address - starting address of the memory block
30 *    size             - new size
31 *
32 *  Output parameters:
33 *    'old_mem_size'   - the size of the user memory area of the block before
34 *                       resizing.
35 *    'avail_mem_size' - the size of the user memory area of the free block
36 *                       that has been enlarged or created due to resizing,
37 *                       0 if none.
38 *    Returns
39 *      HEAP_RESIZE_SUCCESSFUL  - if success
40 *      HEAP_RESIZE_UNSATISFIED - if the block can't be resized in place
41 *      HEAP_RESIZE_FATAL_ERROR - if failure
42 */
43
44Heap_Resize_status _Heap_Resize_block(
45  Heap_Control *the_heap,
46  void         *starting_address,
47  uint32_t      size,
48  uint32_t     *old_mem_size,
49  uint32_t     *avail_mem_size
50)
51{
52  Heap_Block *the_block;
53  Heap_Block *next_block;
54  uint32_t   next_block_size;
55  boolean    next_is_used;
56  Heap_Block *next_next_block;
57  uint32_t   old_block_size;
58  uint32_t   old_user_size;
59  uint32_t   prev_used_flag;
60  Heap_Statistics *const stats = &the_heap->stats;
61  uint32_t const min_block_size = the_heap->min_block_size;
62  uint32_t const page_size = the_heap->page_size;
63
64  *old_mem_size = 0;
65  *avail_mem_size = 0;
66
67  _Heap_Start_of_block(the_heap, starting_address, &the_block);
68  _HAssert(_Heap_Is_block_in(the_heap, the_block));
69  if (!_Heap_Is_block_in(the_heap, the_block))
70    return HEAP_RESIZE_FATAL_ERROR;
71
72  prev_used_flag = the_block->size & HEAP_PREV_USED;
73  old_block_size = _Heap_Block_size(the_block);
74  next_block = _Heap_Block_at(the_block, old_block_size);
75
76  _HAssert(_Heap_Is_block_in(the_heap, next_block));
77  _HAssert(_Heap_Is_prev_used(next_block));
78  if ( !_Heap_Is_block_in(the_heap, next_block) ||
79       !_Heap_Is_prev_used(next_block))
80    return HEAP_RESIZE_FATAL_ERROR;
81
82  next_block_size = _Heap_Block_size(next_block);
83  next_next_block = _Heap_Block_at(next_block, next_block_size);
84  next_is_used    = (next_block == the_heap->final) ||
85                     _Heap_Is_prev_used(next_next_block);
86
87  /* See _Heap_Size_of_user_area() source for explanations */
88  old_user_size = _Addresses_Subtract(next_block, starting_address)
89    + HEAP_BLOCK_HEADER_OFFSET;
90
91  *old_mem_size = old_user_size;
92
93  if (size > old_user_size) {
94    /* Need to extend the block: allocate part of the next block and then
95       merge 'the_block' and allocated block together. */
96    if (next_is_used)    /* Next block is in use, -- no way to extend */
97      return HEAP_RESIZE_UNSATISFIED;
98    else {
99      uint32_t add_block_size = size - old_user_size;
100      _Heap_Align_up(&add_block_size, page_size);
101      if (add_block_size < min_block_size)
102        add_block_size = min_block_size;
103      if (add_block_size > next_block_size)
104        return HEAP_RESIZE_UNSATISFIED; /* Next block is too small or none. */
105      add_block_size =
106        _Heap_Block_allocate(the_heap, next_block, add_block_size);
107      /* Merge two subsequent blocks */
108      the_block->size = (old_block_size + add_block_size) | prev_used_flag;
109      --stats->used_blocks;
110    }
111  } else {
112
113    /* Calculate how much memory we could free */
114    uint32_t free_block_size = old_user_size - size;
115    _Heap_Align_down(&free_block_size, page_size);
116
117    if (free_block_size > 0) {
118
119      /* To free some memory the block should be shortened so that it can
120         can hold 'size' user bytes and still remain not shorter than
121         'min_block_size'. */
122
123      uint32_t new_block_size = old_block_size - free_block_size;
124
125      if (new_block_size < min_block_size) {
126        uint32_t delta = min_block_size - new_block_size;
127        _HAssert(free_block_size >= delta);
128        free_block_size -= delta;
129        if (free_block_size == 0) {
130          ++stats->resizes;
131          return HEAP_RESIZE_SUCCESSFUL;
132        }
133        new_block_size += delta;
134      }
135
136      _HAssert(new_block_size >= min_block_size);
137      _HAssert(new_block_size + free_block_size == old_block_size);
138      _HAssert(_Heap_Is_aligned(new_block_size, page_size));
139      _HAssert(_Heap_Is_aligned(free_block_size, page_size));
140
141      if (!next_is_used) {
142        /* Extend the next block to the low addresses by 'free_block_size' */
143        Heap_Block *const new_next_block =
144          _Heap_Block_at(the_block, new_block_size);
145        uint32_t const new_next_block_size =
146          next_block_size + free_block_size;
147        _HAssert(_Heap_Is_block_in(the_heap, next_next_block));
148        the_block->size = new_block_size | prev_used_flag;
149        new_next_block->size = new_next_block_size | HEAP_PREV_USED;
150        next_next_block->prev_size = new_next_block_size;
151        _Heap_Block_replace(next_block, new_next_block);
152        the_heap->stats.free_size += free_block_size;
153        *avail_mem_size = new_next_block_size - HEAP_BLOCK_USED_OVERHEAD;
154
155      } else if (free_block_size >= min_block_size) {
156        /* Split the block into 2 used  parts, then free the second one. */
157        the_block->size = new_block_size | prev_used_flag;
158        next_block = _Heap_Block_at(the_block, new_block_size);
159        next_block->size = free_block_size | HEAP_PREV_USED;
160        ++stats->used_blocks; /* We have created used block */
161        --stats->frees;       /* Don't count next call in stats */
162        _Heap_Free(the_heap, _Heap_User_area(next_block));
163        *avail_mem_size = free_block_size - HEAP_BLOCK_USED_OVERHEAD;
164      }
165    }
166  }
167
168  ++stats->resizes;
169  return HEAP_RESIZE_SUCCESSFUL;
170}
Note: See TracBrowser for help on using the repository browser.