Creating and Using an Expandable Heap

When using a fixed-size heap, the initial block of memory must be large enough to satisfy all allocation requests made to it. You can also, however, create a heap that can expand and contract as your program needs demand.

With the IBM C and C++ Compilers runtime heap, when not enough storage is available for your malloc request, the runtime gets additional storage from the system. Similarly, when you minimize the heap with _heapmin or when your program ends, the runtime returns the memory to the operating system.

When you create an expandable heap, you provide your own functions to do this work (we'll call them getmore_fn and release_fn, although you can name them whatever you choose). You specify pointers to these functions as the last two parameters to _ucreate (instead of the NULL pointers you used to create a fixed-size heap). For example:

Heap_t growHeap;
static char block[_HEAP_MIN_SIZE];  /* get block */

growHeap = _ucreate(block, _HEAP_MIN_SIZE,   /* starting block */
                    !_BLOCK_CLEAN,      /* memory not set to 0 */
                    _HEAP_REGULAR,      /* regular memory      */
                    getmore_fn,     /* function to expand heap */
                    release_fn);    /* function to shrink heap */

Note: You can use the same getmore_fn and release_fn for more than one heap, as long as the heaps use the same type of memory and your functions are not written specifically for one heap.

Expanding Your Heap
When you call _umalloc (or a similar function) for your heap, _umalloc tries to allocate the memory from the initial block you provided to _ucreate. If not enough memory is there, it then calls your getmore_fn. Your getmore_fn then gets more memory from the operating system and adds it to the heap. It is up to you how you do this.

Your getmore_fn must have the following prototype:

void *(*getmore_fn)(Heap_t uh, size_t *size, int *clean);

The uh is the heap to be expanded.

The size is the size of the allocation request passed by _umalloc. You probably want to return enough memory at a time to satisfy several allocations; otherwise every subsequent allocation has to call getmore_fn, reducing your program's execution speed. Make sure that you update the size parameter. if you return more than the size requested.

Your function must also set the clean parameter to either _BLOCK_CLEAN, to indicate the memory has been set to 0, or !_BLOCK_CLEAN, to indicate that the memory has not been initialized.

The following fragment shows an example of a getmore_fn:

static void *getmore_fn(Heap_t uh, size_t *length, int *clean)
{
   char *newblock;

   /* round the size up to a multiple of 64K */
   *length = (*length / 65536) * 65536 + 65536;

   *clean = _BLOCK_CLEAN;  /* mark the block as "clean" */
   return(newblock);       /* return new memory block   */
}

Be sure that your getmore_fn allocates the right type of memory (regular or shared) for the heap. There are also special considerations for shared memory, as described under Types of Memory.

You can also use _uaddmem to add blocks to your heap, as you did for the fixed heap in Creating and Using a Fixed-Size Heap. _uaddmem works exactly the same way for expandable heaps.

Shrinking Your Heap
To coalesce the heap (return all blocks in the heap that are totally free to the system), use _uheapmin. _uheapmin works like _heapmin, except that you specify the heap to use.

When you call _uheapmin to coalesce the heap or _udestroy to destroy it, these functions call your release_fn to return the memory to the system. Again, it is up to you how you implement this function.

Your release_fn must have the following prototype:

void (*release_fn)(Heap_t uh, void *block, size_t size);

Where uh identifies the heap to be shrunk. The pointer block and its size are passed to your function by _uheapmin or _udestroy. Your function must return the memory pointed to by block to the system. For example:

static void release_fn(Heap_t uh, void *block, size_t size)
{
   free(block);
   return;
}

Notes:

  1. _udestroy calls your release_fn to return all memory added to the uh heap by your getmore_fn or by _uaddmem. However, you are responsible for returning the initial block of memory that you supplied to _ucreate.
  2. Because a fixed-size heap has no release_fn, _uheapmin and _udestroy work slightly differently. Calling _uheapmin for a fixed-size heap has no effect but does not cause an error; _uheapmin simply returns 0. Calling _udestroy for a fixed-size heap marks the heap as destroyed, so no further operations can be performed on it, but returns no memory. It is up to you to return the heap's memory to the system.


Memory Management Functions
Managing Memory with Multiple Memory Heaps
Types of Memory
Debugging Memory Heaps


Creating and Using a Fixed Size Heap
Debugging Problems with Heap Memory
Changing the Default Heap Used in a Program
Example of Creating and Using a User Heap
Example of Creating and Using a Shared-Memory User Heap