NASD Programmer's Documentation
Shutdown Lists

One common problem many programmers experience when building complex systems is cleaning up resources when shutting down. Modifying a system that is already in-place requires not only initializing and allocating resources at the correct time, but being sure they are deallocated after no one is using them anymore.

To simplify this sort of chore, NASD provides shutdown lists, which are basically lists of memory to deallocate and tasks to perform to cleanup. These lists are processed in LIFO order. This allows components to register shutdown operations on a list at the time that resources are allocated and initialized. Since such resources must be created before they are used, processing the list in the reverse order that things are added ensures that users of a resource are themselves stopped and cleaned up before a resource is destroyed. The NASD drive, for instance, maintains a master shutdown list (nasd_odc_shutdown) which all drive components use to register cleanup activities when they initialize. This allows those who are modifying the drive to simply insert a call to an initialization function for their new subsystem in an appropriate place. Then all initialization and relevant shutdown registry may be performed in this function, providing a modular programming environment and more readable code.

To initialize the shutdown module, call nasd_shutdown_sys_init(), which takes no arguments and returns nasd_status_t. To release a reference on the shutdown module, call nasd_shutdown_cleanup().

Basic shutdown operations are:

nasd_status_t nasd_shutdown_list_init(nasd_shutdown_list_t **slp);

nasd_status_t nasd_shutdown_proc(nasd_shutdown_list_t *sl,
  void (*proc)(void *), void *arg);

nasd_status_t nasd_shutdown_mem(nasd_shutdown_list_t *sl,
  void *addr, int size);

nasd_status_t nasd_shutdown_list_shutdown(nasd_shutdown_list_t *sl);

Shutdown lists are of type nasd_shutdown_list_t. To create a new shutdown list, call nasd_shutdown_list_init() with an indirect pointer to a nasd_shutdown_list_t. This will yield a pointer to an empty shutdown list.

To register that a particular action should be taken, use nasd_shutdown_proc(). This will enter a notation in the shutdown list that a callback should occur at shutdown time. nasd_shutdown_proc() takes the shutdown list to enter the notation on as its first argument, and the callback and an argument for this callback as its second and third arguments. When the shutdown list is traversed, the callback procedure will be called with the third argument of nasd_shutdown_proc() as its sole argument.

To indicate that memory allocated with NASD_Malloc() should be deallocated, call nasd_shutdown_mem() with the shutdown list to add this notation to, the address of the memory to free, and its size. This address and size correspond directly to the arguments which will be passed to NASD_Free(). For more information on these memory operations, see the section on memory allocation and deallocation.

To trigger the events registered with a shutdown list, call nasd_shutdown_list_shutdown() with the shutdown list as its sole argument. This will traverse the list, freeing memory or calling callbacks in the reverse order to which they were added to the list. All resources associated with the shutdown list will be destroyed by nasd_shutdown_list_shutdown(). (That is, the passed-in shutdown list will also be deallocated, and that pointer should not be reused.)

For convenience, several common shutdown procedures are provided. Each of these is a valid proc argument to nasd_shutdown_proc().

ProcedureArgumentPurpose
nasd_shutdown_mutex()mutexdestroy the mutex, printing a warning message if problems are encountered
nasd_shutdown_cond()condition variabledestroy the condition variable, printing a warning message if problems are encountered
nasd_shutdown_rwlock()readers/writers lockdestroy the lock, printing a warning message if problems are encountered
nasd_shutdown_page()
(Digital Unix kernel only)
vm_page_tFree the page

It is common for applications using shutdown lists to pass them as arguments to initialization functions. This is handy for functions which initialize many items, because it allows on-the-fly registry to handle cleaning up already-initialized items. For example, such a function might look like:

NASD_DECLARE_MUTEX(nasd_foo_lock)
NASD_DECLARE_COND(nasd_foo_cond)
typedef struct nasd_foo_s nasd_foo_t;
struct nasd_foo_s {
  /* actual data for nasd_foo_t here */
  nasd_foo_t  *another_foo;
};

nasd_freelist_t *nasd_foo_freelist; #define NASD_MAX_FREE_FOO 1024 #define NASD_FOO_INC 64 #define NASD_FOO_INITIAL 32

void
nasd_shutdown_foo_freelist(
  void  *ignored)
{
  NASD_FREELIST_DESTROY(nasd_foo_freelist,next,(nasd_foo_t *));
}
nasd_status_t
nasd_init_foo(
  nasd_shutdown_list_t  *shutdown_list)
{
  nasd_status_t rc;

  NASD_FREELIST_CREATE(nasd_foo_freelist, NASD_MAX_FREE_FOO,
    NASD_FOO_INC, sizeof(nasd_foo_t));
  if (nasd_foo_freelist == NULL) {
    return(NASD_NO_MEM);
  }

  rc = nasd_shutdown_proc(shutdown_list, nasd_shutdown_foo_freelist, NULL);
  if (rc) {
    nasd_shutdown_foo_freelist(NULL);
    return(rc);
  }

  NASD_FREELIST_PRIME(nasd_foo_freelist, NASD_FOO_INITIAL,next,
    (nasd_foo_t *));

  rc = nasd_mutex_init(&nasd_foo_lock);
  if (rc)
    return(rc);
  rc = nasd_shutdown_proc(shutdown_list, nasd_shutdown_mutex, &nasd_foo_lock);
  if (rc) {
    nasd_shutdown_mutex(&nasd_foo_lock);
    return(rc);
  }

  rc = nasd_cond_init(&nasd_foo_cond);
  if (rc)
    return(rc);
  rc = nasd_shutdown_proc(shutdown_list, nasd_shutdown_cond, &nasd_foo_cond);
  if (rc) {
    nasd_shutdown_cond(&nasd_foo_cond);
    return(rc);
  }

  return(NASD_SUCCESS);
}
In this example, each initialization step is performed separately. As far as the caller of nasd_init_foo() is concerned, if it returns NASD_SUCCESS, the foo system has initialized correctly, and will deallocate its resources when nasd_shutdown_list_shutdown() is called on shutdown_list. Likewise, if at any time nasd_init_foo() returns failure, any partially-initialized state of the foo system will be cleaned up when nasd_shutdown_list_shutdown() is called on shutdown_list.
<--- ---> ^<br>|<br>|
Timeouts Other system wrappers NASD Programmer's Documentation