//#define WIN32_LEAN_AND_MEAN 1 //#include /* * handfact.h * * author: John R. Douceur * date: 26 January 1998 * * This header file defines structures, function prototypes, and macros for * the handle factory. The code is object-oriented C, transliterated from a * C++ implementation. * * The handle factory is a component that generates and validates handles. It * is intended to be used in a software module that provides client software * modules with means to refer to information structures contained within the * provider. While such a means could simply be a pointer, this would not * enable the deletion of the information structures without explicitly * notifying the clients of such deletion. Unlike pointers, the handles * generated by the handle factory can be examined (by the handle factory) * to determine their validity. * * Handles can be invalidated in one of two ways. The handle can be released * by calling the release_HF_handle() function, indicating to the handle * factory that the handle is no longer necessary and that future requests * to dereference this handle should be met with a null pointer. Alternately, * the handle can be revoked by the handle factory; this will happen unter two * circumstances. If a large number of handles (more than four billion) are * issued and subsequently released, it becomes necessary to reuse portions of * the handle space for future assignments; under these circumstances, very * old handles will be revoked well before this recycling occurs, to give the * holders of those handles ample opportunity to notice that their handles * have become invalid and to request new handles. The other situation in * which revokation can occur is if the amount of available memory becomes * too small to allocate additional space to expand the handle database; then, * if the assignment of a new handle is requested, the least-recently-assigned * handle will be revoked to make room for the new request. * * Use of the handle factory in a multi-threaded environment requires a lock. * This lock must be taken by a single thread for the execution of either * assign_HF_handle() or release_HF_handle(). Use of dereference_HF_handle() * does not require taking a lock, since synchronization is handled internally * through careful sequencing of read and write operations. * * Because this code is C, rather than C++, it is not possible to hide as * much of the implementation from the client code as one might wish. * Nonetheless, there is an attempt to isolate the client from some of the * implementation details through the use of macros. Below is described each * of the functions and macros necessary to use the handle factory. * */ #ifndef _INC_HANDFACT #define _INC_HANDFACT #ifdef __cplusplus extern "C" { #endif /* * There are two basic structures employed: the HFEntry and the HandleFactory. * Ideally, these would be completely hidden from the client, but the size of * the HandleFactory structure structure needs to be known by the client for * allocation purposes, and this is most easily accomplished by declaring the * structure itself here in the header file, which in turn requires declaring * the HFEntry structure. It is strongly urged that the client not directly * refer to any of the fields of either of these structures. To support the * documentation of the accompanying rhizome.c file, these structures are * annotated with internal comments, but these can be ignored by the reader * who wishes only to understand how to write client code that makes use of * the handle factory. * * The handles generated by the handle factory are of type HFHandle. This is * typedefed to an unsigned int, but this fact can be ignored by the client, * since it is an implementation detail. * */ //#include //#include // HFHandle is the type of the handles generated by the handle factory. // typedef unsigned int HFHandle; struct _HFEntry; typedef struct _HFEntry HFEntry; struct _HFEntry { // This is the element in which each handle and its associated pointer are // stored. If handle == next_handle, the entry is not assigned, and it is // available for assignment to a pointer via the assign_HF_handle() // function. If handle != next_handle, then the entry is assigned to the // pointer in the reference field. // // Each entry is on one of three lists: the primary free list, the secondary // free list, or the assigned list. Each of these lists is maintained via // the next_entry and prev_entry pointers. HFHandle handle; // value of handle HFHandle next_handle; // next value given to handle when invalidated void *reference; // pointer to which handle refers HFEntry *next_entry; // pointer to next entry in list HFEntry *prev_entry; // pointer to previous entry in list }; struct _HandleFactory; typedef struct _HandleFactory HandleFactory; struct _HandleFactory { // This structure contains private member variables for the handle factory. // The table_size and entries fields are marked volatile to insure that the // operations performed on them occur in the specified sequence. The handle // factory can operate in a multi-threaded environment without requiring // that a lock be taken before calling dereference_HF_handle(), and this is // accomplished by careful sequencing of the read and write operations on // these two variables. // // There are two sets of table_size and entries variables, which are used // to provide a synchronization mechanism in conjunction with the two // sync variables. The varset variable indicates which set of these three // variables is used by default. Most normal operations (assign, release, // suspend, reinstate) simply use the default set of table_size and entries. // However, the expand and contract routines update both sets, and the // dereference routine needs to examine the sets in a special way to ensure // that it does not conflict with a concurrent expansion or contraction. // // The dereference_HF_handle() routine increments one of the sync variables // to indicate an intention to refer to the corresponding table_size and // entries variables. The expand_HF_table() and contract_HF_table() // routines each massively decrement one of the sync variables to indicate // an intention to change the corresponding table_size and entries // variables. All changes to the sync variables are done through // interlocked operations. // // The table that holds the handles can only be contracted (shrunk in half) // when for each assigned handle in the lower half of the table, there is // no assigned handle in the corresponding upper half of the table. The // number of correspondences between the two table halves is given by // pair_count. volatile int table_size[2]; // size of table for storing entries HFEntry *volatile entries[2]; // pointer to tables of entries LONG sync[2]; // synchronization variable int varset; // variable set for default usage HFHandle handle_base; // rolling point of lowest handle value int population; // number of handles currently assigned int pair_count; // contractions can occur when pair_count == 0 int hysteresis_debt; // must be zero before contraction HFEntry entry_list[3]; // array of all three entry lists }; /* * The client interface to the handle factory is provided by seven functions * and one macro. It is expected that the provider will first instantiate a * handle factory, either in the static data segment, on the stack, or on the * heap. Then, the provider will assign handles to various pointers by * calling assign_HF_handle(), which it will distribute to its clients. When * the provider wishes to release these handles, it will do so by calling * release_HF_handle(). Each time a client presents a handle to the provider, * the provider can validate the handle and retrieve the associated pointer * by calling dereference_HF_handle(). A client can temporarily suspend a * handle by calling suspend_HF_handle(), after which it can either reinstate * the handle by calling reinstate_HF_handle() or release the handle by calling * release_HF_handle(). * */ // A handle factory may be allocated in the static data segment or on the stack // simply by declaring a variable of type HandleFactory. To allocate it on the // heap, the following macro returns a pointer to a new HandleFactory structure. // If this macro is used, a corresponding call to free() must be made to // deallocate the structure from the heap. // #define NEW_HandleFactory(_h) AllocMem(&(_h), sizeof(HandleFactory)) #define FreeHandleFactory(_h) FreeMem(_h) // Since this is not C++, the HandleFactory structure is not self-constructing; // therefore, the following constructor code must be called on the HandleFactory // structure after it is allocated. If the construction is successful, the // function returns a value of 0. If the construction fails (due, for example, // to an inability to allocate memory), the function returns a value of 1. // int constructHandleFactory( HandleFactory *hfact); // Since this is not C++, the HandleFactory structure is not self-destructing; // therefore, the following destructor code must be called on the HandleFactory // structure before it is deallocated. // void destructHandleFactory( HandleFactory *hfact); // This function generates a new handle value, associates the handle value with // the provided reference pointer, and returns the handle value. Barring // highly unusual circumstances, this handle will remain valid until it is // explicitly released by a call to release_HF_handle(). However, there is no // guarantee that the handle will persist for an arbitrary duration; it may // become necessary for the handle factory to revoke the handle under some // circumstances, particularly when the handle becomes very old or when memory // becomes scarce. // // The assign_HF_handle() function will never return a handle value of zero. // Thus, the client program is free to use a zero handle value as an escape // indicator, if desired. // // In a multi-threaded environment, a single thread must take a lock prior to // calling this function, and this must be the same lock taken before calling // release_HF_handle(), suspend_HF_handle(), and reinstate_HF_handle(). // HFHandle assign_HF_handle( HandleFactory *hfact, void *reference); // This function releases a handle, indicating that further attempts to // dereference the handle should result in a null pointer value rather than the // pointer value that was originally assigned to the handle. The handle factory // checks the validity of the handle and returns a corresponding status code. // If the handle is currently assigned, then it is released, and the function // returns a value of 0. If the handle is not currently assigned, the function // aborts and returns a value of 1. // // In a multi-threaded environment, a single thread must take a lock prior to // calling this function, and this must be the same lock taken before calling // assign_HF_handle(), suspend_HF_handle(), and reinstate_HF_handle(). // int release_HF_handle( HandleFactory *hfact, HFHandle handle); // This function suspends a handle, indicating that further attempts to // dereference the handle should result in a null pointer value rather than the // pointer value that was originally assigned to the handle, unless and until // reinstate_HF_handle() is called on the handle value. The handle factory // checks the validity of the handle and returns a corresponding status code. // If the handle is currently assigned and not suspended, then it is suspended, // and the function returns a value of 0. If the handle is not currently // assigned or has already been suspended, the function aborts and returns a // value of 1. // // In a multi-threaded environment, a single thread must take a lock prior to // calling this function, and this must be the same lock taken before calling // assign_HF_handle(), release_HF_handle(), and reinstate_HF_handle(). // int suspend_HF_handle( HandleFactory *hfact, HFHandle handle); // This function reinstates a suspended handle, indicating that further attempts // to dereference the handle should result in the pointer value that was // originally assigned to the handle, rather than the null pointer value to // which a suspended handle dereferences. The handle factory checks the // validity of the handle and returns a corresponding status code. If the handle // is currently assigned and suspended, then it is reinstated, and the function // returns a value of 0. If the handle is not currently assigned or is not // suspended, the function aborts and returns a value of 1. // // In a multi-threaded environment, a single thread must take a lock prior to // calling this function, and this must be the same lock taken before calling // assign_HF_handle(), release_HF_handle(), and suspend_HF_handle(). // int reinstate_HF_handle( HandleFactory *hfact, HFHandle handle); // This function validates a handle and returns either the associated pointer // (if the handle is valid) or a null pointer value (if the handle is invalid). // If the handle has not been released or suspended but a null value is // returned, then the handle has been revoked by the handle factory. This is // expected to be a highly unusual occurrence; however, since it can happen, any // program that employs the handle factory must have some auxiliary mechanism // for retrieving the desired pointer information. Once the pointer is // retrieved through this (presumably expensive) auxiliary means, a new handle // can be reassigned to the pointer by another call to assign_HF_handle(). // // Even in a multi-threaded environment, it is not necessary to take a lock // prior to calling this function. Careful sequencing of read and write // operations inside the handle factory code obviates the need to explicitly // lock the data structure for dereferencing handles. // void * dereference_HF_handle( HandleFactory *hfact, HFHandle handle); #ifdef _TEST_HANDFACT // This is a test routine that simply verifies the internal valididy of the // handle factory's data structures. By defining the constant _TEST_HANDFACT, // this routine will be compiled and available to the client code. It can be // called at any time, unless running in a multi-threaded environment, in which // case the caller must first take the same lock used for assign_HF_handle(), // release_HF_handle(), suspend_HF_handle(), and reinstate_HF_handle(). If the // routine returns any value other than zero, then the internal lists of records // are in an inconsistent state. // int verify_HF_lists( HandleFactory *hfact); #endif /* _TEST_HANDFACT */ #ifdef __cplusplus } #endif #endif /* _INC_HANDFACT */