Pool Allocator

The following diagrams show the internal state of the pool-based allocator. The arrows represent pointers.

Pool Creation

After mpool_create has been called, the state looks like this:

Initialization State

Observe:

  1. alloc_list points to an empty doubly-linked list (dbll).
  2. free_list points to a dbll containing a single llnode.
  3. The user_data of this llnode points to an alloc_info object that contains offset 0 and size equal to the pool size (X). Essentially this indicates that a free block of at most size X is available for allocation.

NOTE: Not shown in this figure (and all other figures that follow) is that start points to a memory block obtained by malloc that is X bytes in size.

Pool Full

Now, consider the situation when the memory in the pool is exhausted. Assume two allocations were made, of size a and b. If a + b = X (i.e. pool size), then the pool is full. The internal state can be represented as below:

Full Pool

Observe:

  1. free_list is now an empty list.
  2. alloc_list contains two nodes, one for allocation of size a and another for size b
  3. The alloc_info nodes point out that the first block is at offset 0 (so its memory address is start + 0, while the second block has address start + a.

Recall that pointers, by themselves, do not contain size information of the allocated memory block. That is why the memory allocator needs to track sizes using structures like alloc_info.

Note that alignment constraints might lead to a situation when a block of size a was allocated for a request of size less than a. However, this is one design choice. It is possible to leave the padding between allocations in the free list as tiny blocks.

Allocating from the pool

Assume the pool has just been created, and you need to allocate a block of size a:

Initialization State

To allocate a block of size a, you scan the free_list for a free block that can larger than or equal to a while respecting alignment constraints. Once you have found it, you create a node for the allocated block and place it in alloc_list, and reduce the size of the free block. Note, to handle alignment restrictions, you may need to even split the free block. In our case, the first free block has size X, and offset 0 which can satisfy any alignment. That allows us to just reduce the size of the free block assuming a is strictly less than X.

Initialization State

Freeing from the pool

Consider, now, the steps to free the block of size a in the previous example (i.e. memory at address start + 0):

Initialization State

First, you will scan the alloc_list to look for the block starting at offset 0. Once you have found that block, you will remove it from the dbll.

The next step is to add this block back to the free list. If the most recently freed block is not immediately adjacent to any existing free block, you can just reinsert it back into the free list. However, the common case (which is true in this case as well) is that there could be blocks on the free list that can be combined (or coalesced) with the most recently freed block.

In our example, the recently freed block starting at offset 0 (and of size a) can be coalesced with the free block starting at offset a. This results in the following state (which is identical to our initialization state):

Initialization State

Other questions?

If you have other questions, please ask them on Blackboard, but make sure you provide enough detail.