|
These routines provide a general memory management facility. Memory
is retrieved from a coarser allocator (e.g. sbrk or the kernel's
xalloc) and then allocated to callers. The routines are locked
and thus may safely be used in multiprocess programs.
Poolalloc attempts to allocate a block of size size; it returns
a pointer to the block when successful and nil otherwise. The
call poolalloc(0) returns a non–nil pointer. Poolfree returns an
allocated block to the pool. It is an error to free a block more
than once or to free a pointer not returned by poolalloc. The
call
poolfree(nil) is legal and is a no–op.
Poolallocalign attempts to allocate a block of size size with
the given alignment constraints. If align is non–zero, the returned
pointer is aligned to be equal to offset modulo align. If span
is non–zero, the n byte block allocated will not span a span–byte
boundary.
Poolrealloc attempts to resize to nsize bytes the block associated
with ptr, which must have been previously returned by poolalloc
or poolrealloc. If the block's size can be adjusted, a (possibly
different) pointer to the new block is returned. The contents
up to the lesser of the old and new sizes are unchanged. After
a successful call to poolrealloc, the return value should be used
rather than ptr to access the block. If the request cannot be
satisfied, poolrealloc returns nil, and the old pointer remains
valid.
When blocks are allocated, there is often some extra space left
at the end that would usually go unused. Poolmsize grows the block
to encompass this extra space and returns the new size.
The poolblockcheck and poolcheck routines validate a single allocated
block or the entire pool, respectively. They call panic (see below)
if corruption is detected. Pooldump prints a summary line for
every block in the pool, using the print function (see below).
The Pool structure itself provides much of the setup interface.
| |
typedef struct Pool Pool;
struct Pool {
| |
char* name;
ulong maxsize; /* of entire Pool */
ulong cursize; /* of Pool */
ulong curfree; /* total free bytes in Pool */
ulong curalloc; /* total allocated bytes in Pool */
ulong minarena; /* smallest size of new arena */
ulong quantum; /* allocated blocks should be multiple of */
ulong minblock; /* smallest newly allocated block */
int flags;
int nfree; /* number of calls to free */
int lastcompact; /* nfree at time of last poolcompact */
void* (*alloc)(ulong);
int (*merge)(void*, void*);
void (*move)(void* from, void* to);
void (*lock)(Pool*);
void (*unlock)(Pool*);
void (*print)(Pool*, char*, ...);
void (*panic)(Pool*, char*, ...);
void (*logstack)(Pool*);
void* private;
|
};
enum { /* flags */
| |
POOL_ANTAGONISM = 1<<0,
POOL_PARANOIA = 1<<1,
POOL_VERBOSITY = 1<<2,
POOL_DEBUGGING = 1<<3,
POOL_LOGGING = 1<<4,
POOL_TOLERANCE = 1<<5,
POOL_NOREUSE = 1<<6,
|
};
|
The pool obtains arenas of memory to manage by calling the the
given alloc routine. The total number of requested bytes will
not exceed maxsize. Each allocation request will be for at least
minarena bytes.
When a new arena is allocated, the pool routines try to merge
it with the surrounding arenas, in an attempt to combat fragmentation.
If merge is non–nil, it is called with the addresses of two blocks
from alloc that the pool routines suspect might be adjacent. If
they are not mergeable, merge must return zero. If
they are mergeable, merge should merge them into one block in
its own bookkeeping and return non–zero.
To ease fragmentation and make block reuse easier, the sizes requested
of the pool routines are rounded up to a multiple of quantum before
the carrying out requests. If, after rounding, the block size
is still less than minblock bytes, minblock will be used as the
block size.
Poolcompact defragments the pool, moving blocks in order to aggregate
the free space. Each time it moves a block, it notifies the move
routine that the contents have moved. At the time that move is
called, the contents have already moved, so from should never
be dereferenced. If no move routine is supplied (i.e.
it is nil), then calling poolcompact is a no–op.
When the pool routines need to allocate a new arena but cannot,
either because alloc has returned nil or because doing so would
use more than maxsize bytes, poolcompact is called once to defragment
the memory and the request is retried.
Pools are protected by the pool routines calling lock (when non–nil)
before modifying the pool, and calling unlock when finished.
When internal corruption is detected, panic is called with a print(2)
style argument that specifies what happened. It is assumed that
panic never returns. When the pool routines wish to convey a message
to the caller (usually because logging is turned on; see below),
print is called, also with a print(2) style
argument.
Flags is a bit vector that tweaks the behavior of the pool routines
in various ways. Most are useful for debugging in one way or another.
When POOL_ANTAGONISM is set, poolalloc fills blocks with non–zero
garbage before releasing them to the user, and poolfree fills
the blocks on receipt. This tickles both user
programs and the innards of the allocator. Specifically, each
32–bit word of the memory is marked with a pointer value exclusive–or'ed
with a constant. The pointer value is the pointer to the beginning
of the allocated block and the constant varies in order to distinguish
different markings. Freed blocks use the
constant 0xF7000000, newly allocated blocks 0xF9000000, and newly
created unallocated blocks 0xF1000000. For example, if POOL_ANTAGONISM
is set and poolalloc returns a block starting at 0x00012345, each
word of the block will contain the value 0xF90012345. Recognizing
these numbers in
memory–related crashes can help diagnose things like double–frees
or dangling pointers.
Setting POOL_PARANOIA causes the allocator to walk the entire
pool whenever locking or unlocking itself, looking for corruption.
This slows runtime by a few orders of magnitude when many blocks
are in use. If POOL_VERBOSITY is set, the entire pool structure
is printed (via print) each time the pool is locked
or unlocked. POOL_DEBUGGING enables internal debugging output,
whose format is unspecified and volatile. It should not be used
by most programs. When POOL_LOGGING is set, a single line is printed
via print at the beginning and end of each pool call. If logstack
is not nil, it will be called as well. This
provides a mechanism for external programs to search for leaks.
(See leak(1) for one such program.)
The pool routines are strict about the amount of space callers
use. If even a single byte is written past the end of the allotted
space of a block, they will notice when that block is next used
in a call to poolrealloc or free (or at the next entry into the
allocator, when POOL_PARANOIA is set), and panic will be called.
Since forgetting to allocate space for the terminating NUL on
strings is such a common error, if POOL_TOLERANCE is set and a
single NUL is found written past the end of a block, print will
be called with a notification, but panic will not be.
When POOL_NOREUSE is set, poolfree fills the passed block with
garbage rather than return it to the free pool.
|