//#define WIN32_LEAN_AND_MEAN 1
//#include <windows.h>

/*
 *  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 <stdlib.h>
//#include <malloc.h>

// 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 */