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:
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()
.
Procedure | Argument | Purpose |
---|---|---|
nasd_shutdown_mutex() | mutex | destroy the mutex, printing a warning message if problems are encountered |
nasd_shutdown_cond() | condition variable | destroy the condition variable, printing a warning message if problems are encountered |
nasd_shutdown_rwlock() | readers/writers lock | destroy the lock, printing a warning message if problems are encountered |
nasd_shutdown_page() (Digital Unix kernel only) | vm_page_t | Free 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:
In this example, each initialization step is performed separately. As far as the caller ofnasd_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
.
![]() | ![]() | ![]() |
---|---|---|
Timeouts | Other system wrappers | NASD Programmer's Documentation |