Memory.xcc

Benoit Dupont de Dinechin (Benoit.Dupont-de-Dinechin@st.com)

Copyright 2007 STMicroelectronics. Copyright 1995 - 1998 Commissariat a l'Energie Atomique.

This program is free software; you can redistribute it and/or modify it under the terms of either (at your option): the GNU General Public License (GPL) version 2; the GNU Lesser General Public License (LGPL) version 2.1; any later version of these licences as published by the Free Software Foundation.

This module implements the memory allocator used by all the CDT containers and data structures. It basically provides the "reap" interface proposed by "Reconsidering Custom Memory Allocation" by E. D. Berger, B. G. Zorn, K. S. McKinley. This is done on top of the system malloc().

The "reap" interface provides a 'freeAll' method to free all the memory of a Memory allocator at once, and also supports parent-child relationships between Memory allocators so that deleting one recursively deletes its children.

In addition to the "reap" interface, we enable memory pooling of small-sized allocation requests. Pooling is controlled by a threshold supplied to Memory_new. Currently pooled memory is only release when freeAll or Memory_delete is called.

MemoryUsage— State to track memory usage.
struct MemoryUsage_ {
  //@args
  uint32_t INUSE;               // Current memory space in use.
  uint32_t MAXUSE;              // Maximum memory space used.
  uint32_t OPENED;              // Current number of allocations in all size ranges.
  uint32_t ALLOCS;              // Total number of allocations in all size ranges.
  MemoryUsageRecord_ SMALLRECORDS[MemoryUsage_SMALLCOUNT];
  MemoryUsageRecord_ LARGERECORDS[MemoryUsage_LARGECOUNT];
};
MemoryUsage_pretty— Pretty-print this MemoryUsage.
bool
MemoryUsage_pretty(const_MemoryUsage this, FILE *file);
MemoryUsage_alloc— Trace usage of malloc.
void
MemoryUsage_alloc(MemoryUsage this, uint32_t size, bool isPool);
MemoryUsage_free— Trace usage of free.
void
MemoryUsage_free(MemoryUsage this, uint32_t size, bool isPool);
MemoryListItem— Fields needed by the items of a MemoryList.
struct MemoryListItem_ {
  //@args
  struct MemoryListItem_ *NEXT;
  struct MemoryListItem_ *PREV;
};
MemoryList— Manage a list of items derived from MemoryListItem.
struct MemoryList_ {
  //@args
  struct MemoryListItem_ *FIRST;
  struct MemoryListItem_ *LAST;
};

MemoryList_first -- For use by MemoryList_FOREACH_MemoryListItem.

static inline MemoryListItem
MemoryList_first(MemoryList this)
{
  return MemoryList_FIRST(this);
}
MemoryList_FOREACH_MemoryListItem— Iterator.
#define MemoryList_FOREACH_MemoryListItem(this, item) { \
  MemoryListItem MemoryList_NEXT = NULL; \
  MemoryListItem item = MemoryList_first(this); \
  for (; item != NULL; item = MemoryList_NEXT) { \
    MemoryList_NEXT = MemoryListItem_NEXT(item);
#define MemoryList_ENDEACH_MemoryListItem \
  } \
}
MemoryList_append— Append item to this MemoryList.
void
MemoryList_append(MemoryList this, MemoryListItem item);
MemoryList_remove— Remove item from this MemoryList.
void
MemoryList_remove(MemoryList this, MemoryListItem item);
MemoryTableEntry— Entry in a MemoryTable.
struct MemoryTableEntry_ {
  //@args       void *start, size_t useSize, int level,
  //@args       struct MemoryTableEntry_ *leaf
  size_t USESIZE;                       // Size of memory available for use.
  uint16_t LEVEL;                       // Level to keep the binary search tree balanced.
  uint16_t REFC;                        // Reference counter of this MemoryTableEntry_.
  struct MemoryTableEntry_ *LEFT;       // The left kid in the binary search tree.
  struct MemoryTableEntry_ *RIGHT;      // The right kid in the binary search tree.
  char *START;                          // The start memory address.
  //@access PAST        (MemoryTableEntry_START(this) + MemoryTableEntry_USESIZE(this))
  struct MemoryBlock_ *NEXTBLOCK;       // Next MemoryBlock in the Memory_RECYCLE list.
  //@access BLOCK       (MemoryBlock)((MemoryListItem)(this) - 1)
};
MemoryTable— Maps addresses to MemoryBlock(s).

Currently the MemoryTable only allows search and insert operations.

Uses a balanced search tree as described in "Balanced Search Trees Made Simple" by Arne Andersson (http://user.it.uu.se/~arnea/research.html).

struct MemoryTable_ {
  //@args       void *memory
  MemoryTableEntry_ LEAF_;
  //@access LEAF        MemoryTable__LEAF_(this)
  MemoryTableEntry ROOT;
};
MemoryTable_search— Search this MemoryTable for the MemoryBlock containing address.
struct MemoryBlock_ *
MemoryTable_search(const_MemoryTable this, const void *address);
MemoryBlock— Block of memory.
struct MemoryBlock_ {
  //@args       size_t useSize, MemoryTableEntry leaf
  MemoryListItem_ ITEM_;
  //@access ITEM        MemoryBlock__ITEM_(this)
  //@access NEXT        (MemoryBlock)MemoryListItem_NEXT(MemoryBlock_ITEM(this))
  //@access PREV        (MemoryBlock)MemoryListItem_PREV(MemoryBlock_ITEM(this))
  MemoryTableEntry_ ENTRY_;
  //@access ENTRY       MemoryBlock__ENTRY_(this)
  //@access NEXTBLOCK   MemoryTableEntry_NEXTBLOCK(MemoryBlock_ENTRY(this))
  //@mutate NEXTBLOCK   MemoryTableEntry__NEXTBLOCK(MemoryBlock_ENTRY(this))
  //@access REALSIZE    (sizeof(MemoryBlock_) + MemoryBlock_USESIZE(this))
  //@access USESIZE     MemoryTableEntry_USESIZE(MemoryBlock_ENTRY(this))
  //@mutate USESIZE     MemoryTableEntry__USESIZE(MemoryBlock_ENTRY(this))
  //@access START       MemoryTableEntry_START(MemoryBlock_ENTRY(this))
  //@mutate START       MemoryTableEntry__START(MemoryBlock_ENTRY(this))
  //@access PAST        MemoryTableEntry_PAST(MemoryBlock_ENTRY(this))
  //@access REFC        MemoryTableEntry_REFC(MemoryBlock_ENTRY(this))
  //@mutate REFC        MemoryTableEntry__REFC(MemoryBlock_ENTRY(this))
};

Memory -- Memory allocator.

struct Memory_ {
  //@args       Memory parent, size_t pooled
  MemoryListItem_ ITEM_;        // Used to embed this Memory in a MemoryList.
  //@access ITEM        Memory__ITEM_(this)
  //@access NEXT        (Memory)MemoryListItem_NEXT(Memory_ITEM(this))
  //@access PREV        (Memory)MemoryListItem_PREV(Memory_ITEM(this))
  struct Memory_ *PARENT;       // Parent in the Memory parent-child relationships.
  //@access isRoot      (Memory_PARENT(this) == NULL)
  size_t POOLED;                // Maximum alloc size for request to be pooled.
  size_t TOTALSIZE;             // Total allocated size (for statistics).
  size_t TOTALCOUNT;            // Total allocated count (for statistics).
  MemoryBlock POOLBLOCK;        // Currently active pooling block.
  char *POOLFIRST;              // First available address in pooling block.
  char *POOLPAST;               // Past available address in pooling block.
  MemoryBlock RECYCLE;          // Start of list of MemoryBlock(s) to recycle.
  MemoryList_ CHILDREN_;        // Children of this Memory allocator.
  //@access CHILDREN    Memory__CHILDREN_(this)
  MemoryList_ BLOCKS_;          // MemoryBlock(s) managed by this Memory allocator.
  //@access BLOCKS      Memory__BLOCKS_(this)
  //@access TABLE       (MemoryTable)(this + 1)
};
Memory_new— Make a new Memory allocator.
parent
The parent memory allocator.
pooled
The maximum allocation size pooled by this Memory allocator.

Memory allocators are organized in tree thanks to the parent parameter. When a Memory allocator is deleted or does freeAll, it recursively deletes all its children.

The pooled parameter is the threshold on allocation size beyond which the Memory_alloc and Memory_free directly forward to malloc and free. For allocations sizes no greater than pooled, the requests are pooled by the Memory allocator.

Memory
Memory_new(Memory parent, size_t pooled);
Memory_delete— Delete this Memory allocator.
Memory
Memory_delete(Memory this);
Memory_children— For use by Memory_CHILDREN_FOREACH_Memory.
static inline MemoryList
Memory_children(Memory this)
{
  return Memory_CHILDREN(this);
}
Memory_CHILDREN_FOREACH_Memory— Iterator for Memory_CHILDREN.
#define Memory_CHILDREN_FOREACH_Memory(this, child) { \
  MemoryList Memory_CHILDREN = Memory_children(this); \
  MemoryList_FOREACH_MemoryListItem(Memory_CHILDREN, item) { \
    Memory child = (Memory)item;
#define Memory_CHILDREN_ENDEACH_Memory \
  } MemoryList_ENDEACH_MemoryListItem; \
}
Memory_blocks— For use by Memory_FOREACH_MemoryBlock.
static inline MemoryList
Memory_blocks(Memory this)
{
  return Memory_BLOCKS(this);
}
Memory_FOREACH_MemoryBlock— Iterator for Memory_BLOCKS.
#define Memory_FOREACH_MemoryBlock(this, block) { \
  MemoryList Memory_BLOCKS = Memory_blocks(this); \
  MemoryList_FOREACH_MemoryListItem(Memory_BLOCKS, item) { \
    MemoryBlock block = (MemoryBlock)item;
#define Memory_ENDEACH_MemoryBlock \
  } MemoryList_ENDEACH_MemoryListItem; \
}

Memory_POOLFACTOR -- Multiplied by Memory_POOLED to compute pooling block use size.

#ifndef Memory_POOLFACTOR
#define Memory_POOLFACTOR 8
#endif//Memory_POOLFACTOR
Memory_alloc_— Specialized Memory_alloc for non-zero aligned alignedSize.
void *
Memory_alloc_(Memory this, size_t alignedSize);
Memory_alloc— Allocate memory in its own MemoryBlock or in a pool.
size
The requested allocation size in bytes.
Return
Allocated address that holds size bytes.
Ensure
Returned address is non-NULL.
static inline void *
Memory_alloc(Memory this, size_t size)
{
  Except_CHECK(this != NULL);
  if (size > 0) {
    size_t alignedSize = CDT_ALIGN_NEXT(size);
    void *alloc = Memory_alloc_(this, alignedSize);
    return alloc;
  }
  return NULL;
}
Memory_free_— Specialized Memory_free for non-NULL address.
void
Memory_free_(Memory this, void *address);
Memory_free— Free the memory at given address unless it is pooled.

Pooled memory is not returned to the system until this Memory is deleted. However, reference counting of pooling memory blocks is maintained so when all the allocations of such a block are freed it is recycled.

static inline void *
Memory_free(Memory this, void *address)
{
  Except_CHECK(this != NULL);
  if (address != NULL) {
    Except_CHECK(CDT_ALIGNED(address));
    Memory_free_(this, address);
  }
  return NULL;
}
Memory_freeAll— Free all the MemoryBlock(s) and children of this Memory allocator.
void
Memory_freeAll(Memory this);
Memory_realloc— Reallocate data allocated by this Memory allocator.
void *
Memory_realloc(Memory this, void* address, size_t size);