summaryrefslogtreecommitdiffstats
path: root/cpukit/score/src/heapfree.c
blob: e0e2e72d858194b5c4819f47ea20750737c429b1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 *  Heap Handler
 *
 *  COPYRIGHT (c) 1989-1999.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  $Id$
 */

#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems/system.h>
#include <rtems/score/sysstate.h>
#include <rtems/score/heap.h>

/*PAGE
 *
 *  _Heap_Free
 *
 *  This kernel routine returns the memory designated by the
 *  given heap and given starting address to the memory pool.
 *
 *  Input parameters:
 *    the_heap         - pointer to heap header
 *    starting_address - starting address of the memory block to free.
 *
 *  Output parameters:
 *    TRUE  - if starting_address is valid heap address
 *    FALSE - if starting_address is invalid heap address
 */

boolean _Heap_Free(
  Heap_Control        *the_heap,
  void                *starting_address
)
{
  Heap_Block        *the_block;
  Heap_Block        *next_block;
  uint32_t         the_size;
  uint32_t         next_size;
  Heap_Statistics *const stats = &the_heap->stats;
  boolean            next_is_free;

  _Heap_Start_of_block( the_heap, starting_address, &the_block );

  if ( !_Heap_Is_block_in( the_heap, the_block ) ) {
    _HAssert(starting_address == NULL);
    _HAssert(FALSE);
    return( FALSE );
  }

  the_size = _Heap_Block_size( the_block );
  next_block = _Heap_Block_at( the_block, the_size );

  if ( !_Heap_Is_block_in( the_heap, next_block ) ) {
    _HAssert(FALSE);
    return( FALSE );
  }

  if ( !_Heap_Is_prev_used( next_block ) ) {
    _HAssert(FALSE);
    return( FALSE );
  }

  next_size = _Heap_Block_size( next_block );
  next_is_free = next_block < the_heap->final &&
    !_Heap_Is_prev_used(_Heap_Block_at(next_block, next_size));

  if ( !_Heap_Is_prev_used( the_block ) ) {
    uint32_t const prev_size = the_block->prev_size;
    Heap_Block *const prev_block = _Heap_Block_at( the_block, -prev_size );

    if ( !_Heap_Is_block_in( the_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 */
      uint32_t const size = the_size + prev_size + next_size;
      _Heap_Block_remove( next_block );
      stats->free_blocks -= 1;
      prev_block->size = size | HEAP_PREV_USED;
      next_block = _Heap_Block_at( prev_block, size );
      _HAssert(!_Heap_Is_prev_used( next_block));
      next_block->prev_size = size;
    }
    else {                      /* coalesce prev */
      uint32_t const size = the_size + prev_size;
      prev_block->size = size | HEAP_PREV_USED;
      next_block->size &= ~HEAP_PREV_USED;
      next_block->prev_size = size;
    }
  }
  else if ( next_is_free ) {    /* coalesce next */
    uint32_t const size = the_size + next_size;
    _Heap_Block_replace( next_block, the_block );
    the_block->size = size | HEAP_PREV_USED;
    next_block  = _Heap_Block_at( the_block, size );
    next_block->prev_size = size;
  }
  else {                        /* no coalesce */
    /* Add 'the_block' to the head of the free blocks list as it tends to
       produce less fragmentation than adding to the tail. */
    _Heap_Block_insert_after( _Heap_Head( the_heap), the_block );
    the_block->size = the_size | HEAP_PREV_USED;
    next_block->size &= ~HEAP_PREV_USED;
    next_block->prev_size = the_size;

    stats->free_blocks += 1;
    if ( stats->max_free_blocks < stats->free_blocks )
      stats->max_free_blocks = stats->free_blocks;
  }

  stats->used_blocks -= 1;
  stats->free_size += the_size;
  stats->frees += 1;

  return( TRUE );
}