POOL(2)                                                   POOL(2)

     NAME
          poolalloc, poolfree, poolmsize, poolrealloc, poolcompact,
          poolcheck, poolblockcheck, pooldump - general memory
          management routines

     SYNOPSIS
          #include <u.h>

          #include <libc.h>

          #include <pool.h>

          void*     poolalloc(Pool* pool, ulong size)

          void poolfree(Pool* pool, void* ptr)

          ulong     poolmsize(Pool* pool, void* ptr)

          void*     poolrealloc(Pool* pool, void* ptr, ulong size)

          void poolcompact(Pool* pool)

          void poolcheck(Pool *pool)

          void poolblockcheck(Pool *pool, void *ptr)

          void pooldump(Pool *pool);

     DESCRIPTION
          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 multipro-
          cess programs.

          Poolalloc attempts to allocate a block of size size; it
          returns a pointer to the block when successful and nil oth-
          erwise.  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.  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 sat-
          isfied, poolrealloc returns nil, and the old pointer remains

     Page 1                       Plan 9             (printed 4/23/24)

     POOL(2)                                                   POOL(2)

          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 inter-
          face.

               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,
               };

          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.

     Page 2                       Plan 9             (printed 4/23/24)

     POOL(2)                                                   POOL(2)

          When a new arena is allocated, the pool routines try to
          merge it with the surrounding arenas, in an attempt to com-
          bat fragmentation.  If merge is non-nil, it is called with
          the addresses of two blocks from alloc that the pool rou-
          tines 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 can-
          not, 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 log-
          ging 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 allo-
          cated 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

     Page 3                       Plan 9             (printed 4/23/24)

     POOL(2)                                                   POOL(2)

          POOL_ANTAGONISM is set and poolalloc returns a block start-
          ing 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 mag-
          nitude 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
          debugmalloc(2) for one such mechanism.)

          The pool routines are strict about the amount of space call-
          ers 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 allo-
          cate space for the terminating NUL on strings is such a com-
          mon 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.

     EXAMPLE
          A complete example follows.

               static void*    sbrkalloc(ulong);
               static int  sbrkmerge(void*, void*);
               static void plock(Pool*);
               static void punlock(Pool*);
               static void pprint(Pool*, char*, ...);
               static void ppanic(Pool*, char*, ...);
               typedef struct Private  Private;
               struct Private {
                   Lock;
                   int fd;
               };
               static Private sbrkpriv;
               Pool sbrkmem = {
                   .name=  "sbrkmem",
                   .maxsize=   2*1024*1024*1024,
                   .minarena=  4*1024,
                   .quantum=   32,
                   .alloc= sbrkalloc,

     Page 4                       Plan 9             (printed 4/23/24)

     POOL(2)                                                   POOL(2)

                   .merge= sbrkmerge,
                   .flags= 0,

                   .lock=  plock,
                   .unlock=    punlock,
                   .print= pprint,
                   .panic= ppanic,
                   .private=   &sbrkpriv,
               };

               /* sbrk with minimal bookkeeping so we can handle merge calls */
               static void*
               sbrkalloc(ulong n)
               {
                   long *x;

                   n += 8; /* two longs for us */
                   x = sbrk(n);
                   if((int)x == -1)
                       x = nil;
                   x[0] = (n+7)&~7;    /* sbrk rounds size up to mult. of 8 */
                   x[1] = 0xDeadBeef;
                   return x+2;
               }

               static int
               sbrkmerge(void *x, void *y)
               {
                   long *lx, *ly;

                   lx = x;
                   if(lx[-1] != 0xDeadBeef)
                       abort();

                   if((uchar*)lx+lx[-2] == (uchar*)y) {
                       ly = y;
                       lx[-2] += ly[-2];
                       return 1;
                   }
                   return 0;
               }

               static void
               plock(Pool *p)
               {
                   Private *priv = p->private;
                   lock(priv);
               }

               static void
               punlock(Pool *p)
               {

     Page 5                       Plan 9             (printed 4/23/24)

     POOL(2)                                                   POOL(2)

                   Private *priv = p->private;
                   unlock(priv);
               }

               static void
               pprint(Pool *p, char *fmt, ...)
               {
                   Private *priv = p->private;
                   int n;
                   va_list v;
                   char buf[128];

                   va_start(v, fmt);
                   n = doprint(buf, buf+sizeof buf, fmt, v)-buf;
                   va_end(v);
                   write(priv->fd, buf, n);
               }

               static void
               ppanic(Pool*, char *fmt, ...)
               {
                   va_list v;
                   int n;
                   char buf[128];

                   va_start(v, fmt);
                   n = doprint(buf, buf+sizeof buf, fmt, v)-buf;
                   va_end(v);
                   write(2, "panic: ", 7);
                   write(2, buf, n);
                   write(2, "0, 1);
                   abort();
               }

               void*
               malloc(ulong size)
               {
                   return poolalloc(&sbrkmem, size);
               }

     SOURCE
          /sys/src/libc/port/pool.c

     SEE ALSO
          debugmalloc(2), malloc(2), brk(2)

     BUGS
          The implementation of POOL_TOLERANCE is slightly incomplete
          - there exists one case in which the pool is still not tol-
          erant of NUL bytes.

          Errstr is not always set.

     Page 6                       Plan 9             (printed 4/23/24)

     POOL(2)                                                   POOL(2)

          The example is too long.

     Page 7                       Plan 9             (printed 4/23/24)