Initializing help system before first use

Improved memory management for external types

Topics covered in this section:

For the task module we have described a very simple way of handling memory allocations in a module directly with the corresponding C functions: whenever an object of the new type needs to be created the required space is allocated and when the object is deleted this space is freed in C.

In this section we give an example of memory management by the module: the space for new complex numbers is allocated in large chunks. The module keeps track of the available space, including space that has already been used by this module and may be recycled. This proceding requires much less memory allocation operations and only a single set of deallocations. Furthermore, at the deletion of an object the possibly expensive search for the object in the entire list held by the module context is replaced by a copy of the pointer to the list of free space.

Module context

Contrary to the context of the task module that only keeps a single list, we now define a context that holds two lists:

typedef struct
    {
     s_nmlist *nmlist;
     u_freelist *freelist;
    } s_cxctx;

The first of these lists, nmlist, is all the space allocated for complex numbers, stored in chunks of size NCXL:

typedef struct Nmlist
    {
     s_complex list[NCXL];
     int nextfree;
     struct Nmlist *next;
    } s_nmlist;

The second list indicates the free entries in the list of numbers:

typedef union Freelist
    {
     s_complex cx;
     union Freelist *next;
    } u_freelist;

Service functions reset and memuse

The reset service function initializes the module context at its first call and frees all space that has been allocated by the module at the next call to it:

static void *cx_reset(XPRMcontext ctx, void *libctx, int version)
{
 s_cxctx *cxctx;
 s_nmlist *nmlist;

 if(libctx==NULL)               /* libctx==NULL => initialization */
 {
  cxctx=malloc(sizeof(s_cxctx));
  memset(cxctx, 0, sizeof(s_cxctx));
  return cxctx;
 }
 else                           /* Otherwise release the resources we use */
 {
  cxctx=libctx;
  while(cxctx->nmlist!=NULL)
  {
   nmlist=cxctx->nmlist;
   cxctx->nmlist=nmlist->next;
   free(nmlist);
  }
  free(cxctx);
  return NULL;
 }
}

The memory use (memuse) service function returns information about the total memory currently allocated by a module when invoked with value 0 for its code argument, or the memory used by individual types (code=type ID).

static size_t cx_memuse(XPRMcontext ctx, void *cxctx, void *ref, int code)
{
 switch(code)
 {
  case 0:
    {
     size_t s;
     s_nmlist *nmlist;

     s=sizeof(s_cxctx);
     nmlist=((s_cxctx*)cxctx)->nmlist;
     while(nmlist!=NULL)
     {
      s+=sizeof(s_nmlist);
      nmlist=nmlist->next;
     }
     return s;
    }
  case 1:
    return sizeof(s_complex);
  default:
    return -1;
 }
}

Type creation and deletion functions

In our example we define the type creation function printed below. As mentioned in the previous section, the space for complex numbers is not allocated one-by-one but in larger chunks and the module also keeps track of space that may be re-used. We therefore face the following choice every time a new complex number is created:

  • if possible re-use space that has been allocated earlier,
  • otherwise, if no free space remains, allocate a new block of complex numbers,
  • otherwise use the next free space.

In the case that the complex number passed into the creation function already exists we simply augment its reference counter, unless the object is shared (note that the implementation of the shared data property shown in this example is not entirely complete: it should guarantee that concurrent access to a given shared object does not corrupt the data structure, e.g. by using critical sections).

#define CX_SHARED (1<<30)       /* Marker for a shared complex number */
#define CX_CONST  (1<<29)       /* Marker for a constant complex number */

static void *cx_create(XPRMcontext ctx, void *libctx, void *todup,
                       int typnum)
{
 s_cxctx *cxctx;
 s_complex *complex;
 s_nmlist *nmlist;

 if((todup!=NULL)&&(XPRM_CREATE(typnum)==XPRM_CREATE_NEW))
 {
  /* Do not update the reference count if the object is shared */
  if((((s_complex *)todup)->refcnt&CX_SHARED)==0)
   ((s_complex *)todup)->refcnt++;
  return todup;
 }
 else
 {
  cxctx=libctx;
  if(cxctx->freelist!=NULL)         /* Re-use allocated space that was freed */
  {
   complex=&(cxctx->freelist->cx);
   cxctx->freelist=cxctx->freelist->next;
  }
  else                              /* Allocate a new block of complex numbers */
   if((cxctx->nmlist==NULL)||(cxctx->nmlist->nextfree>=NCXL))
   {
    nmlist=malloc(sizeof(s_nmlist));
    nmlist->next=cxctx->nmlist;
    cxctx->nmlist=nmlist;
    nmlist->nextfree=1;
    complex=nmlist->list;
   }
   else                             /* Use allocated and yet free space */
    complex=&(cxctx->nmlist->list[cxctx->nmlist->nextfree++]);
                                    /* Initialize the new complex number */
  if(XPRM_CREATE(typnum)==XPRM_CREATE_CST)
  {
   complex->re=((s_complex *)todup)->re;
   complex->im=((s_complex *)todup)->im;
   complex->refcnt=1|CX_CONST;
  }
  else
  {
   complex->re=complex->im=0;
   complex->refcnt=1;
   /* Tag a shared complex number to disable reference counting */
   if(XPRM_CREATE(typnum)==XPRM_CREATE_SHR)
    complex->refcnt|=CX_SHARED;
  }
  return complex;
 }
}

The deletion function does not completely deallocate the space used by a complex number. It simply moves it into the list of space that may be recycled:

static void cx_delete(XPRMcontext ctx, void *libctx, void *todel, int typnum)
{
 s_cxctx *cxctx;
 u_freelist *freelist;

 if((todel!=NULL)&&((((s_complex *)todel)->refcnt&CX_SHARED)==0)&&
    (((--((s_complex *)todel)->refcnt)&~CX_CONST)<1)
 {
  cxctx=libctx;
  freelist=todel;                    /* Delete = space to be recycled */
  freelist->next=cxctx->freelist;
  cxctx->freelist=freelist;
 }
}

© 2001-2021 Fair Isaac Corporation. All rights reserved. This documentation is the property of Fair Isaac Corporation (“FICO”). Receipt or possession of this documentation does not convey rights to disclose, reproduce, make derivative works, use, or allow others to use it except solely for internal evaluation purposes to determine whether to purchase a license to the software described in this documentation, or as otherwise set forth in a written software license agreement between you and FICO (or a FICO affiliate). Use of this documentation and the software described in it must conform strictly to the foregoing permitted uses, and no other use is permitted.