Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

319 lines
14 KiB

  1. /*
  2. * handfact.h
  3. *
  4. * author: John R. Douceur
  5. * date: 26 January 1998
  6. *
  7. * This header file defines structures, function prototypes, and macros for
  8. * the handle factory. The code is object-oriented C, transliterated from a
  9. * C++ implementation.
  10. *
  11. * The handle factory is a component that generates and validates handles. It
  12. * is intended to be used in a software module that provides client software
  13. * modules with means to refer to information structures contained within the
  14. * provider. While such a means could simply be a pointer, this would not
  15. * enable the deletion of the information structures without explicitly
  16. * notifying the clients of such deletion. Unlike pointers, the handles
  17. * generated by the handle factory can be examined (by the handle factory)
  18. * to determine their validity.
  19. *
  20. * Handles can be invalidated in one of two ways. The handle can be released
  21. * by calling the release_HF_handle() function, indicating to the handle
  22. * factory that the handle is no longer necessary and that future requests
  23. * to dereference this handle should be met with a null pointer. Alternately,
  24. * the handle can be revoked by the handle factory; this will happen unter two
  25. * circumstances. If a large number of handles (more than four billion) are
  26. * issued and subsequently released, it becomes necessary to reuse portions of
  27. * the handle space for future assignments; under these circumstances, very
  28. * old handles will be revoked well before this recycling occurs, to give the
  29. * holders of those handles ample opportunity to notice that their handles
  30. * have become invalid and to request new handles. The other situation in
  31. * which revokation can occur is if the amount of available memory becomes
  32. * too small to allocate additional space to expand the handle database; then,
  33. * if the assignment of a new handle is requested, the least-recently-assigned
  34. * handle will be revoked to make room for the new request.
  35. *
  36. * Use of the handle factory in a multi-threaded environment requires a lock.
  37. * This lock must be taken by a single thread for the execution of either
  38. * assign_HF_handle() or release_HF_handle(). Use of dereference_HF_handle()
  39. * does not require taking a lock, since synchronization is handled internally
  40. * through careful sequencing of read and write operations.
  41. *
  42. * Because this code is C, rather than C++, it is not possible to hide as
  43. * much of the implementation from the client code as one might wish.
  44. * Nonetheless, there is an attempt to isolate the client from some of the
  45. * implementation details through the use of macros. Below is described each
  46. * of the functions and macros necessary to use the handle factory.
  47. *
  48. */
  49. #ifndef _INC_HANDFACT
  50. #define _INC_HANDFACT
  51. #ifdef __cplusplus
  52. extern "C" {
  53. #endif
  54. /*
  55. * There are two basic structures employed: the HFEntry and the HandleFactory.
  56. * Ideally, these would be completely hidden from the client, but the size of
  57. * the HandleFactory structure structure needs to be known by the client for
  58. * allocation purposes, and this is most easily accomplished by declaring the
  59. * structure itself here in the header file, which in turn requires declaring
  60. * the HFEntry structure. It is strongly urged that the client not directly
  61. * refer to any of the fields of either of these structures. To support the
  62. * documentation of the accompanying rhizome.c file, these structures are
  63. * annotated with internal comments, but these can be ignored by the reader
  64. * who wishes only to understand how to write client code that makes use of
  65. * the handle factory.
  66. *
  67. * The handles generated by the handle factory are of type HFHandle. This is
  68. * typedefed to an unsigned int, but this fact can be ignored by the client,
  69. * since it is an implementation detail.
  70. *
  71. */
  72. //#include <stdlib.h>
  73. //#include <malloc.h>
  74. // HFHandle is the type of the handles generated by the handle factory.
  75. //
  76. typedef unsigned int HFHandle;
  77. struct _HFEntry;
  78. typedef struct _HFEntry HFEntry;
  79. struct _HFEntry
  80. {
  81. // This is the element in which each handle and its associated pointer are
  82. // stored. If handle == next_handle, the entry is not assigned, and it is
  83. // available for assignment to a pointer via the assign_HF_handle()
  84. // function. If handle != next_handle, then the entry is assigned to the
  85. // pointer in the reference field.
  86. //
  87. // Each entry is on one of three lists: the primary free list, the secondary
  88. // free list, or the assigned list. Each of these lists is maintained via
  89. // the next_entry and prev_entry pointers.
  90. HFHandle handle; // value of handle
  91. HFHandle next_handle; // next value given to handle when invalidated
  92. void *reference; // pointer to which handle refers
  93. HFEntry *next_entry; // pointer to next entry in list
  94. HFEntry *prev_entry; // pointer to previous entry in list
  95. };
  96. struct _HandleFactory;
  97. typedef struct _HandleFactory HandleFactory;
  98. struct _HandleFactory
  99. {
  100. // This structure contains private member variables for the handle factory.
  101. // The first four fields are marked volatile to insure that the operations
  102. // performed on them occur in the specified sequence. The handle factory
  103. // can operate in a multi-threaded environment without requiring that a
  104. // lock be taken before calling dereference_HF_handle(), and this is
  105. // accomplished by careful sequencing of the read and write operations on
  106. // these four variables.
  107. //
  108. // The verifier variables are used to provide a simple synchronization
  109. // mechanism. When the variables have the same value, then the table_size
  110. // and entries variables are in a consistent state.
  111. //
  112. // The table that holds the handles can only be contracted (shrunk in half)
  113. // when for each assigned handle in the lower half of the table, there is
  114. // no assigned handle in the corresponding upper half of the table. The
  115. // number of correspondences between the two table halves is given by
  116. // pair_count.
  117. volatile int table_size; // size of table for storing entries
  118. HFEntry *volatile entries; // pointer to tables of entries
  119. volatile int verifier0; // synchronization variable
  120. volatile int verifier1; // synchronization variable
  121. HFHandle handle_base; // rolling point of lowest handle value
  122. int population; // number of handles currently assigned
  123. int pair_count; // contractions can occur when pair_count == 0
  124. int hysteresis_debt; // must be zero before contraction
  125. HFEntry entry_list[3]; // array of all three entry lists
  126. };
  127. /*
  128. * The client interface to the handle factory is provided by seven functions
  129. * and one macro. It is expected that the provider will first instantiate a
  130. * handle factory, either in the static data segment, on the stack, or on the
  131. * heap. Then, the provider will assign handles to various pointers by
  132. * calling assign_HF_handle(), which it will distribute to its clients. When
  133. * the provider wishes to release these handles, it will do so by calling
  134. * release_HF_handle(). Each time a client presents a handle to the provider,
  135. * the provider can validate the handle and retrieve the associated pointer
  136. * by calling dereference_HF_handle(). A client can temporarily suspend a
  137. * handle by calling suspend_HF_handle(), after which it can either reinstate
  138. * the handle by calling reinstate_HF_handle() or release the handle by calling
  139. * release_HF_handle().
  140. *
  141. */
  142. // A handle factory may be allocated in the static data segment or on the stack
  143. // simply by declaring a variable of type HandleFactory. To allocate it on the
  144. // heap, the following macro returns a pointer to a new HandleFactory structure.
  145. // If this macro is used, a corresponding call to free() must be made to
  146. // deallocate the structure from the heap.
  147. //
  148. #define NEW_HandleFactory(_h) GpcAllocMem(&(_h),\
  149. sizeof(HandleFactory),\
  150. HandleFactoryTag)
  151. #define FreeHandleFactory(_h) GpcFreeMem((_h),\
  152. HandleFactoryTag)
  153. // Since this is not C++, the HandleFactory structure is not self-constructing;
  154. // therefore, the following constructor code must be called on the HandleFactory
  155. // structure after it is allocated. If the construction is successful, the
  156. // function returns a value of 0. If the construction fails (due, for example,
  157. // to an inability to allocate memory), the function returns a value of 1.
  158. //
  159. int
  160. constructHandleFactory(
  161. HandleFactory *hfact);
  162. // Since this is not C++, the HandleFactory structure is not self-destructing;
  163. // therefore, the following destructor code must be called on the HandleFactory
  164. // structure before it is deallocated.
  165. //
  166. void
  167. destructHandleFactory(
  168. HandleFactory *hfact);
  169. // This function generates a new handle value, associates the handle value with
  170. // the provided reference pointer, and returns the handle value. Barring
  171. // highly unusual circumstances, this handle will remain valid until it is
  172. // explicitly released by a call to release_HF_handle(). However, there is no
  173. // guarantee that the handle will persist for an arbitrary duration; it may
  174. // become necessary for the handle factory to revoke the handle under some
  175. // circumstances, particularly when the handle becomes very old or when memory
  176. // becomes scarce.
  177. //
  178. // The assign_HF_handle() function will never return a handle value of zero.
  179. // Thus, the client program is free to use a zero handle value as an escape
  180. // indicator, if desired.
  181. //
  182. // In a multi-threaded environment, a single thread must take a lock prior to
  183. // calling this function, and this must be the same lock taken before calling
  184. // release_HF_handle(), suspend_HF_handle(), and reinstate_HF_handle().
  185. //
  186. HFHandle
  187. assign_HF_handle(
  188. HandleFactory *hfact,
  189. void *reference);
  190. // This function releases a handle, indicating that further attempts to
  191. // dereference the handle should result in a null pointer value rather than the
  192. // pointer value that was originally assigned to the handle. The handle factory
  193. // checks the validity of the handle and returns a corresponding status code.
  194. // If the handle is currently assigned, then it is released, and the function
  195. // returns a value of 0. If the handle is not currently assigned, the function
  196. // aborts and returns a value of 1.
  197. //
  198. // In a multi-threaded environment, a single thread must take a lock prior to
  199. // calling this function, and this must be the same lock taken before calling
  200. // assign_HF_handle(), suspend_HF_handle(), and reinstate_HF_handle().
  201. //
  202. int
  203. release_HF_handle(
  204. HandleFactory *hfact,
  205. HFHandle handle);
  206. // This function suspends a handle, indicating that further attempts to
  207. // dereference the handle should result in a null pointer value rather than the
  208. // pointer value that was originally assigned to the handle, unless and until
  209. // reinstate_HF_handle() is called on the handle value. The handle factory
  210. // checks the validity of the handle and returns a corresponding status code.
  211. // If the handle is currently assigned and not suspended, then it is suspended,
  212. // and the function returns a value of 0. If the handle is not currently
  213. // assigned or has already been suspended, the function aborts and returns a
  214. // value of 1.
  215. //
  216. // In a multi-threaded environment, a single thread must take a lock prior to
  217. // calling this function, and this must be the same lock taken before calling
  218. // assign_HF_handle(), release_HF_handle(), and reinstate_HF_handle().
  219. //
  220. int
  221. suspend_HF_handle(
  222. HandleFactory *hfact,
  223. HFHandle handle);
  224. // This function reinstates a suspended handle, indicating that further attempts
  225. // to dereference the handle should result in the pointer value that was
  226. // originally assigned to the handle, rather than the null pointer value to
  227. // which a suspended handle dereferences. The handle factory checks the
  228. // validity of the handle and returns a corresponding status code. If the handle
  229. // is currently assigned and suspended, then it is reinstated, and the function
  230. // returns a value of 0. If the handle is not currently assigned or is not
  231. // suspended, the function aborts and returns a value of 1.
  232. //
  233. // In a multi-threaded environment, a single thread must take a lock prior to
  234. // calling this function, and this must be the same lock taken before calling
  235. // assign_HF_handle(), release_HF_handle(), and suspend_HF_handle().
  236. //
  237. int
  238. reinstate_HF_handle(
  239. HandleFactory *hfact,
  240. HFHandle handle);
  241. // This function validates a handle and returns either the associated pointer
  242. // (if the handle is valid) or a null pointer value (if the handle is invalid).
  243. // If the handle has not been released or suspended but a null value is
  244. // returned, then the handle has been revoked by the handle factory. This is
  245. // expected to be a highly unusual occurrence; however, since it can happen, any
  246. // program that employs the handle factory must have some auxiliary mechanism
  247. // for retrieving the desired pointer information. Once the pointer is
  248. // retrieved through this (presumably expensive) auxiliary means, a new handle
  249. // can be reassigned to the pointer by another call to assign_HF_handle().
  250. //
  251. // Even in a multi-threaded environment, it is not necessary to take a lock
  252. // prior to calling this function. Careful sequencing of read and write
  253. // operations inside the handle factory code obviates the need to explicitly
  254. // lock the data structure for dereferencing handles.
  255. //
  256. void *
  257. dereference_HF_handle(
  258. HandleFactory *hfact,
  259. HFHandle handle);
  260. void *
  261. dereference_HF_handle_with_cb(
  262. HandleFactory *hfact,
  263. HFHandle handle,
  264. ULONG offset);
  265. #ifdef _TEST_HANDFACT
  266. // This is a test routine that simply verifies the internal valididy of the
  267. // handle factory's data structures. By defining the constant _TEST_HANDFACT,
  268. // this routine will be compiled and available to the client code. It can be
  269. // called at any time, unless running in a multi-threaded environment, in which
  270. // case the caller must first take the same lock used for assign_HF_handle(),
  271. // release_HF_handle(), suspend_HF_handle(), and reinstate_HF_handle(). If the
  272. // routine returns any value other than zero, then the internal lists of records
  273. // are in an inconsistent state.
  274. //
  275. int
  276. verify_HF_lists(
  277. HandleFactory *hfact);
  278. #if DBG
  279. #define VERIFY_HF_HFACT(_hfact) ASSERT(verify_HF_lists(_hfact)==0)
  280. #else
  281. #define VERIFY_HF_HFACT(_hfact)
  282. #endif
  283. #endif /* _TEST_HANDFACT */
  284. #ifdef __cplusplus
  285. }
  286. #endif
  287. #endif /* _INC_HANDFACT */