Leaked source code of windows server 2003
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.

1559 lines
69 KiB

  1. /*
  2. * handfact.c
  3. *
  4. * author: John R. Douceur
  5. * date: 26 January 1998
  6. *
  7. * This source file provides functions that implement assignment, release, and
  8. * dereferencing operations with a handle_factory. The code is object-oriented
  9. * C, transliterated from a 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. * None of the code or comments in this file need to be understood by writers
  43. * of client code; all explanatory information for clients is found in the
  44. * associated header file, handfact.h.
  45. *
  46. */
  47. #include "gpcpre.h"
  48. /*
  49. * There are a number of aspects to the handle factory that must be understood
  50. * by anyone wishing to modify this code. The description in this comment
  51. * block is intended to provide a progressive overview of the handle factory.
  52. *
  53. * The basic system comprises a table of entries. Each assigned handle
  54. * corresponds to a single, unique entry, as determined by the handle value
  55. * modulo the table size. A handle is validated by comparing the handle value
  56. * to the stored handle value in the entry. The unassigned entries are kept
  57. * on a list; when an entry is released (or revoked), it is put on the tail of
  58. * the list, and when an entry is needed for an assignment, it is taken from
  59. * the head of the list.
  60. *
  61. * If there are no unassigned entries in the table when a new handle is
  62. * requested, a new table of twice the size is allocated, and all assigned
  63. * handles are relocated to the new table. All unassigned handles in the new
  64. * table are placed on the unassigned list.
  65. *
  66. * As handles are released, the space required for handle entries is reduced.
  67. * The table can be contracted into a table of half the size if no two assigned
  68. * handles will yield the same entry address. Two handles which will yield
  69. * the same entry address in a half-size table are called a pair, and the
  70. * number of such pairs is tracked in the variable pair_count, which must be
  71. * zero in order to contract the table. In order to minimize the number of
  72. * pairs in the table, there are actually two lists of unassigned entries.
  73. * Assigning an entry from the primary list will not increase the pair count,
  74. * whereas assigning an entry from the secondary list will increase the pair
  75. * count. Thus, assignments are always made from the primary list, if it is
  76. * not empty.
  77. *
  78. * Assigned handles are also kept on a list, in order of assignment. If it
  79. * becomes necessary to revoke a handle to make room for another, the oldest
  80. * handle will be revoked, and it will be found at the head of this list.
  81. *
  82. */
  83. // This macro allocates an array of HFEntry structures. The size of the array
  84. // is provided as an argument to the macro.
  85. //
  86. //#define NEW_HFEntry_array(array_size) \
  87. // ((HFEntry *)malloc(array_size * sizeof(HFEntry)))
  88. #define NEW_HFEntry_array(_a,array_size) \
  89. GpcAllocMem(&_a,array_size * sizeof(HFEntry), HandleFactoryTag)
  90. // This macro allocates an array of integers. The size of the array is
  91. // provided as an argument to the macro.
  92. //
  93. //#define NEW_int_array(array_size) \
  94. // ((int *)malloc(array_size * sizeof(int)))
  95. #define NEW_int_array(_a,array_size) \
  96. GpcAllocMem(&_a,array_size * sizeof(int), HandleFactoryTag)
  97. /*
  98. * Following are prototypes for static functions that are used internally by
  99. * the handle factory routines.
  100. *
  101. */
  102. // This function doubles the size of the table in which the handles and pointers
  103. // are stored. It is called by assign_HF_handle() when there is insufficient
  104. // space in the table to assign the newly requested handle. If the expansion
  105. // is successful, the function returns a value of 0. If the expansion fails
  106. // (due, for example, to an inability to allocate memory), the function returns
  107. // a value of 1.
  108. //
  109. int expand_HF_table(
  110. HandleFactory *hfact);
  111. // This function halves the size of the table in which the handles and pointers
  112. // are stored. In order to reduce the amount of space consumed by the handle
  113. // factory, this function is called called by release_HF_handle() and
  114. // revoke_ancient_HF_handles() when they determine that the table can and should
  115. // be contracted. The table can be contracted when pair_count == 0 and
  116. // table_size > 2. However, the table may not be contracted then, because
  117. // hysteresis is employed both to keep the mean assignment and release times
  118. // constant and to minimize the allocation chatter of rapidly expanding and
  119. // contracting the table. If the contraction is successful, the function
  120. // returns a value of 0. If the contraction fails, the function returns a
  121. // value of 1.
  122. //
  123. int contract_HF_table(
  124. HandleFactory *hfact);
  125. // This function revokes handles that are between handle_base and handle_base
  126. // + 2 * HANDLE_RANGE_STEP - 1, inclusive. It then increments the value of
  127. // handle_base by HANDLE_RANGE_STEP. Suspended handles will be revoked one
  128. // revokation pass later than non-suspended handles.
  129. //
  130. void revoke_ancient_HF_handles(
  131. HandleFactory *hfact);
  132. // Every entry is on one of three lists, and the heads and tails of these lists
  133. // are maintained in the entry_list[] array. The index of this array is given
  134. // by the following three manifest constants.
  135. //
  136. #define LD_PRIMARY 0 // first list from which to select an entry to assign
  137. #define LD_SECONDARY 1 // second list from which to select an entry to assign
  138. #define LD_ASSIGNED 2 // list of assigned entries, in order of assignment age
  139. // When the handle space is recycled, there is a danger of handle collisions.
  140. // In order to substantially reduce the likelihood of these collisions, very
  141. // old handles are revoked well before their recycling begins, to give the
  142. // holders of these handles ample opportunity to notice that their handles
  143. // have become invalid and to request new handles. Thus, handles are revoked
  144. // when they become more than MAX_HANDLE_RANGE less than the currently generated
  145. // handles. To reduce overhead, revokations are performed in batches of size
  146. // determined by HANDLE_RANGE_STEP.
  147. //
  148. // A handle may be suspended by incrementing the handle value by
  149. // HANDLE_RANGE_STEP. This causes the comparison in dereference_HF_handle() to
  150. // fail, so the handle is judged to be invalid. To reinstate the handle, the
  151. // handle value is decremented by HANDLE_RANGE_STEP, returning the handle to its
  152. // original value. A handle that is suspended will be revoked one revokation
  153. // pass later than it would have been if it hadn't been suspended.
  154. //
  155. #define HANDLE_RANGE_STEP ((HFHandle)0x20000000)
  156. #define MAX_HANDLE_RANGE ((HFHandle)0x90000000)
  157. // To keep the mean assignment and release times constant (and, indirectly, to
  158. // minimize the allocation chatter of rapidly expanding and contracting the
  159. // table), the table is not necessarily contracted as soon as possible.
  160. // Hysteresis is employed to postpone the contraction until the computational
  161. // cost of previous expansions and contractions is distributed over a sufficient
  162. // number of assignment or release operations to maintain a constant cost per
  163. // operation ratio. The cost of each expansion is equal to the overhead of
  164. // memory allocation and deallocation plus the cost to split each entry into
  165. // two entries. The cost of each contraction is equal to the overhead of
  166. // memory allocation and deallocation plus the cost to merge each pair of
  167. // entries into one entry. The cost of memory allocation and deallocation is
  168. // equal to ALLOCATION_COST times the mean cost of a single split or merge
  169. // operation. This value was determined by empirical measurement.
  170. //
  171. #define ALLOCATION_COST 12
  172. // Since this is not C++, the HandleFactory structure is not self-constructing;
  173. // therefore, the following constructor code must be called on the HandleFactory
  174. // structure after it is allocated. If the construction is successful, the
  175. // function returns a value of 0. If the construction fails (due, for example,
  176. // to an inability to allocate memory), the function returns a value of 1.
  177. //
  178. int
  179. constructHandleFactory(
  180. HandleFactory *hfact)
  181. {
  182. // The table size is initially set to 2, and it will never be smaller.
  183. hfact->table_size = 2;
  184. // Allocate space for the initial table.
  185. NEW_HFEntry_array(hfact->entries,hfact->table_size);
  186. if (hfact->entries == 0)
  187. {
  188. // Memory could not be allocated for the array of entries created by
  189. // the constructor. Therefore, we return an indication of failure to
  190. // the client.
  191. return 1;
  192. }
  193. hfact->verifier0 = 0; // the verifiers are initialized with the same value
  194. hfact->verifier1 = 0; // the verifiers are initialized with the same value
  195. hfact->handle_base = 0; // handles will start with 0
  196. hfact->population = 0; // no handles initially assigned
  197. hfact->pair_count = 0; // since no assigned handles, no pairs
  198. hfact->hysteresis_debt = 0;
  199. // Initialize the two entries that are initially allocated. Both are marked
  200. // as unassigned; the larger value (2) is put on the secondary list, and the
  201. // smaller value (1) on the secondary list. Record 0 contains an initial
  202. // handle value of 2 instead of 0 because a handle value of 0 is reserved.
  203. hfact->entries[0].handle = hfact->handle_base + hfact->table_size;
  204. hfact->entries[0].next_handle = hfact->handle_base + hfact->table_size;
  205. hfact->entries[0].reference = 0;
  206. hfact->entries[0].next_entry = &hfact->entry_list[LD_SECONDARY];
  207. hfact->entries[0].prev_entry = &hfact->entry_list[LD_SECONDARY];
  208. hfact->entries[1].handle = hfact->handle_base + 1;
  209. hfact->entries[1].next_handle = hfact->handle_base + 1;
  210. hfact->entries[1].reference = 0;
  211. hfact->entries[1].next_entry = &hfact->entry_list[LD_PRIMARY];
  212. hfact->entries[1].prev_entry = &hfact->entry_list[LD_PRIMARY];
  213. // Initialize the primary list. This list initially contains entry 1.
  214. hfact->entry_list[LD_PRIMARY].handle = 0;
  215. hfact->entry_list[LD_PRIMARY].next_handle = 0;
  216. hfact->entry_list[LD_PRIMARY].reference = 0;
  217. hfact->entry_list[LD_PRIMARY].next_entry = &hfact->entries[1];
  218. hfact->entry_list[LD_PRIMARY].prev_entry = &hfact->entries[1];
  219. // Initialize the secondary list. This list initially contains entry 0.
  220. hfact->entry_list[LD_SECONDARY].handle = 0;
  221. hfact->entry_list[LD_SECONDARY].next_handle = 0;
  222. hfact->entry_list[LD_SECONDARY].reference = 0;
  223. hfact->entry_list[LD_SECONDARY].next_entry = &hfact->entries[0];
  224. hfact->entry_list[LD_SECONDARY].prev_entry = &hfact->entries[0];
  225. // Initialize the assigned list. This list initially is empty.
  226. hfact->entry_list[LD_ASSIGNED].handle = 0;
  227. hfact->entry_list[LD_ASSIGNED].next_handle = 0;
  228. hfact->entry_list[LD_ASSIGNED].reference = 0;
  229. hfact->entry_list[LD_ASSIGNED].next_entry = &hfact->entry_list[LD_ASSIGNED];
  230. hfact->entry_list[LD_ASSIGNED].prev_entry = &hfact->entry_list[LD_ASSIGNED];
  231. // Reduce handle_base by HANDLE_RANGE_STEP so that suspended handles will
  232. // not slip through revokation.
  233. hfact->handle_base -= HANDLE_RANGE_STEP;
  234. // return an indication of success to the client.
  235. return 0;
  236. }
  237. // Since this is not C++, the HandleFactory structure is not self-destructing;
  238. // therefore, the following destructor code must be called on the HandleFactory
  239. // structure before it is deallocated.
  240. //
  241. void
  242. destructHandleFactory(
  243. HandleFactory *hfact)
  244. {
  245. // Free the space consumed by the table of handles.
  246. GpcFreeMem(hfact->entries, HandleFactoryTag);
  247. }
  248. // This function generates a new handle value, associates the handle value with
  249. // the provided reference pointer, and returns the handle value. Barring
  250. // highly unusual circumstances, this handle will remain valid until it is
  251. // explicitly released by a call to release_HF_handle(). However, there is no
  252. // guarantee that the handle will persist for an arbitrary duration; it may
  253. // become necessary for the handle factory to revoke the handle under some
  254. // circumstances, particularly when the handle becomes very old or when memory
  255. // becomes scarce.
  256. //
  257. // The assign_HF_handle() function will never return a handle value of zero.
  258. // Thus, the client program is free to use a zero handle value as an escape
  259. // indicator, if desired.
  260. //
  261. // In a multi-threaded environment, a single thread must take a lock prior to
  262. // calling this function, and this must be the same lock taken before calling
  263. // release_HF_handle().
  264. //
  265. HFHandle
  266. assign_HF_handle(
  267. HandleFactory *hfact,
  268. void *reference)
  269. {
  270. int list;
  271. HFEntry *entry;
  272. volatile HFEntry *seq_entry; // volatile to ensure sequencing
  273. HFHandle handle;
  274. HFHandle handle_range;
  275. if (hfact->population >= hfact->table_size)
  276. {
  277. // All entries in the table are assigned, so it is necessary to
  278. // increase the table size.
  279. int expansion_failure = expand_HF_table(hfact);
  280. if (expansion_failure)
  281. {
  282. //
  283. // just fail
  284. //
  285. return 0;
  286. #if 0
  287. // Expanding the table failed, presumably due to inability to
  288. // allocate sufficient memory. So, instead, we revoke the least-
  289. // recently assigned handle. First, remove the entry from the
  290. // assigned list and place it on the secondary list.
  291. entry = hfact->entry_list[LD_ASSIGNED].next_entry;
  292. entry->next_entry->prev_entry = &hfact->entry_list[LD_ASSIGNED];
  293. hfact->entry_list[LD_ASSIGNED].next_entry = entry->next_entry;
  294. entry->next_entry = &hfact->entry_list[LD_SECONDARY];
  295. entry->prev_entry = hfact->entry_list[LD_SECONDARY].prev_entry;
  296. hfact->entry_list[LD_SECONDARY].prev_entry->next_entry = entry;
  297. hfact->entry_list[LD_SECONDARY].prev_entry = entry;
  298. // Then, invalidate the handle. The order of the operations is
  299. // important to correct multi-threaded operation.
  300. seq_entry = entry;
  301. seq_entry->handle = entry->next_handle; // first invalidate handle
  302. seq_entry->reference = 0; // then clear reference
  303. // Decrement the pair count and population, so that when they are
  304. // incremented in the code below, they will have correct values.
  305. hfact->pair_count--;
  306. hfact->population--;
  307. #endif
  308. }
  309. }
  310. // At this point, there is at least one available entry. If there is any
  311. // entry on the primary list, it should be selected.
  312. list = LD_PRIMARY;
  313. if (hfact->entry_list[LD_PRIMARY].next_entry ==
  314. &hfact->entry_list[LD_PRIMARY])
  315. {
  316. // The primary list is empty, so we take from the secondary list. By
  317. // definition, this will increase the pair count.
  318. list = LD_SECONDARY;
  319. hfact->pair_count++;
  320. }
  321. // Remove the entry from the head of the appropriate list and place it on
  322. // the assigned list.
  323. entry = hfact->entry_list[list].next_entry;
  324. handle = entry->handle;
  325. entry->next_entry->prev_entry = entry->prev_entry;
  326. entry->prev_entry->next_entry = entry->next_entry;
  327. entry->next_entry = &hfact->entry_list[LD_ASSIGNED];
  328. entry->prev_entry = hfact->entry_list[LD_ASSIGNED].prev_entry;
  329. hfact->entry_list[LD_ASSIGNED].prev_entry->next_entry = entry;
  330. hfact->entry_list[LD_ASSIGNED].prev_entry = entry;
  331. // Set the reference pointer to that provided as an argument.
  332. entry->reference = reference;
  333. // The next handle for this entry will be greater by the table size. It
  334. // is important to set this value in this routine because unequal values of
  335. // handle and next_handle indicate an assigned entry.
  336. entry->next_handle = handle + hfact->table_size;
  337. if (entry->next_handle == 0)
  338. {
  339. // The handle value has wrapped around back to zero; however, zero is
  340. // a reserved value, so we instead set the next handle to the subsequent
  341. // legal value, which is the table size.
  342. entry->next_handle = hfact->table_size;
  343. }
  344. // The population has increased by one.
  345. hfact->population++;
  346. // We're being tricky with unsigned integer math here. We revoke ancient
  347. // handles if the value of the handle we are currently issuing is greater
  348. // than the handle base by more than MAX_HANDLE_RANGE, modulo the size of
  349. // the handle space. The modulo is implicit.
  350. handle_range = handle - hfact->handle_base;
  351. if (handle_range > MAX_HANDLE_RANGE)
  352. {
  353. revoke_ancient_HF_handles(hfact);
  354. }
  355. // This assignment operation decreases the hysteresis debt.
  356. if (hfact->hysteresis_debt > 0)
  357. {
  358. hfact->hysteresis_debt--;
  359. }
  360. // Return the newly assigned handle.
  361. return handle;
  362. }
  363. // This function releases a handle, indicating that further attempts to
  364. // dereference the handle should result in a null pointer value rather than the
  365. // pointer value that was originally assigned to the handle. The handle factory
  366. // checks the validity of the handle and returns a corresponding status code.
  367. // If the handle is currently assigned, then it is released, and the function
  368. // returns a value of 0. If the handle is not currently assigned, the function
  369. // aborts and returns a value of 1.
  370. //
  371. // In a multi-threaded environment, a single thread must take a lock prior to
  372. // calling this function, and this must be the same lock taken before calling
  373. // assign_HF_handle().
  374. //
  375. int
  376. release_HF_handle(
  377. HandleFactory *hfact,
  378. HFHandle handle)
  379. {
  380. int entry_index;
  381. HFEntry *entry;
  382. HFEntry *other_entry;
  383. int list;
  384. HFHandle adjusted_next_handle;
  385. HFHandle adjusted_other_next_handle;
  386. volatile HFEntry *seq_entry; // volatile to ensure sequencing
  387. // Compute the index of the entry by taking the handle value modulo the
  388. // table size. Since the table size is a power of two, we can simply
  389. // subtract one to produce a mask and then conjoin the mask with the
  390. // handle value.
  391. entry_index = handle & hfact->table_size - 1;
  392. entry = &hfact->entries[entry_index];
  393. if ((entry->handle != handle && entry->handle != handle + HANDLE_RANGE_STEP)
  394. || entry->handle == entry->next_handle)
  395. {
  396. // Either the indexed entry does not refer to the provided handle nor to
  397. // the provided handle's suspension value, or the entry is unassigned.
  398. // In any of these cases, abort and return an error code to the client.
  399. return 1;
  400. }
  401. // The "other entry" is the entry that would have to be merged with the
  402. // indexed entry if the table size were to be contracted in half.
  403. other_entry = &hfact->entries[entry_index ^ hfact->table_size / 2];
  404. if (other_entry->handle == other_entry->next_handle)
  405. {
  406. // We're being tricky with unsigned integer math here. Before comparing
  407. // the two next handles, we subtract from each the value of handle_base,
  408. // modulo the size of the handle space (the modulo is implicit). This
  409. // allows the effective comparison of their logical acyclic values
  410. // rather than their actual cyclic values.
  411. adjusted_next_handle = entry->next_handle - hfact->handle_base;
  412. adjusted_other_next_handle =
  413. other_entry->next_handle - hfact->handle_base;
  414. if (adjusted_other_next_handle < adjusted_next_handle)
  415. {
  416. // The other entry is unassigned and has a smaller handle value
  417. // than the indexed entry. Thus, the other entry should be moved
  418. // from the secondary list to the primary list, and the indexed
  419. // entry should be placed on the secondary list.
  420. other_entry->next_entry->prev_entry = other_entry->prev_entry;
  421. other_entry->prev_entry->next_entry = other_entry->next_entry;
  422. other_entry->next_entry = &hfact->entry_list[LD_PRIMARY];
  423. other_entry->prev_entry = hfact->entry_list[LD_PRIMARY].prev_entry;
  424. hfact->entry_list[LD_PRIMARY].prev_entry->next_entry = other_entry;
  425. hfact->entry_list[LD_PRIMARY].prev_entry = other_entry;
  426. list = LD_SECONDARY;
  427. }
  428. else
  429. {
  430. // The other entry is unassigned and has a larger handle value
  431. // than the indexed entry. Thus, the indexed entry should be
  432. // placed on the secondary list.
  433. list = LD_PRIMARY;
  434. }
  435. }
  436. else
  437. {
  438. // The other entry is assigned. Thus, the indexed entry should be
  439. // placed on the secondary list. Also, since the two entries were
  440. // both assigned, they formed a pair. Since we are releasing one of
  441. // them, the pair count drops by one.
  442. list = LD_SECONDARY;
  443. hfact->pair_count--;
  444. }
  445. // Remove the entry from the assigned list and place it on the
  446. // appropriate list.
  447. entry->next_entry->prev_entry = entry->prev_entry;
  448. entry->prev_entry->next_entry = entry->next_entry;
  449. entry->next_entry = &hfact->entry_list[list];
  450. entry->prev_entry = hfact->entry_list[list].prev_entry;
  451. hfact->entry_list[list].prev_entry->next_entry = entry;
  452. hfact->entry_list[list].prev_entry = entry;
  453. // Invalidate the handle. The order of the operations is important to
  454. // correct multi-threaded operation.
  455. seq_entry = entry;
  456. seq_entry->handle = entry->next_handle; // first invalidate handle
  457. seq_entry->reference = 0; // then clear reference
  458. // The population has decreased by one.
  459. hfact->population--;
  460. // This release operation decreases the hysteresis debt.
  461. if (hfact->hysteresis_debt > 0)
  462. {
  463. hfact->hysteresis_debt--;
  464. }
  465. // To contract the table, there must be no pairs, because otherwise two
  466. // assigned handles would yield the same entry index and thereby conflict.
  467. // Furthermore, the table size must be greater than 2, because much of the
  468. // handle factory code assumes that the table is at least of size 2. In
  469. // addition to these strict requirements, hysteresis is employed both to
  470. // keep the mean assignment and release times constant and to minimize the
  471. // allocation chatter of rapidly expanding and contracting the table. Only
  472. // if the hysteresis debt is zero will the table be contracted.
  473. if (hfact->pair_count == 0 && hfact->table_size > 2 &&
  474. hfact->hysteresis_debt == 0)
  475. {
  476. contract_HF_table(hfact);
  477. // Note that we ignore the return code. If the contraction is
  478. // unsuccessful, we just continue as usual. There is no real harm in
  479. // not contracting the table, except that we consume more space than
  480. // necessary.
  481. }
  482. // return an indication of success to the client.
  483. return 0;
  484. }
  485. // This function suspends a handle, indicating that further attempts to
  486. // dereference the handle should result in a null pointer value rather than the
  487. // pointer value that was originally assigned to the handle, unless and until
  488. // reinstate_HF_handle() is called on the handle value. The handle factory
  489. // checks the validity of the handle and returns a corresponding status code.
  490. // If the handle is currently assigned and not suspended, then it is suspended,
  491. // and the function returns a value of 0. If the handle is not currently
  492. // assigned or has already been suspended, the function aborts and returns a
  493. // value of 1.
  494. //
  495. // In a multi-threaded environment, a single thread must take a lock prior to
  496. // calling this function, and this must be the same lock taken before calling
  497. // assign_HF_handle(), release_HF_handle(), and reinstate_HF_handle().
  498. //
  499. int
  500. suspend_HF_handle(
  501. HandleFactory *hfact,
  502. HFHandle handle)
  503. {
  504. int entry_index;
  505. HFEntry *entry;
  506. // Compute the index of the entry by taking the handle value modulo the
  507. // table size. Since the table size is a power of two, we can simply
  508. // subtract one to produce a mask and then conjoin the mask with the
  509. // handle value.
  510. entry_index = handle & hfact->table_size - 1;
  511. entry = &hfact->entries[entry_index];
  512. if (entry->handle != handle || entry->handle == entry->next_handle)
  513. {
  514. // Either the indexed entry does not refer to the provided handle, or
  515. // the entry is unassigned. In either case, abort and return an error
  516. // code to the client.
  517. return 1;
  518. }
  519. // Suspend the handle.
  520. entry->handle += HANDLE_RANGE_STEP;
  521. // This suspension operation decreases the hysteresis debt.
  522. if (hfact->hysteresis_debt > 0)
  523. {
  524. hfact->hysteresis_debt--;
  525. }
  526. // return an indication of success to the client.
  527. return 0;
  528. }
  529. // This function reinstates a suspended handle, indicating that further attempts
  530. // to dereference the handle should result in the pointer value that was
  531. // originally assigned to the handle, rather than the null pointer value to
  532. // which a suspended handle dereferences. The handle factory checks the
  533. // validity of the handle and returns a corresponding status code. If the handle
  534. // is currently assigned and suspended, then it is reinstated, and the function
  535. // returns a value of 0. If the handle is not currently assigned or is not
  536. // suspended, the function aborts and returns a value of 1.
  537. //
  538. // In a multi-threaded environment, a single thread must take a lock prior to
  539. // calling this function, and this must be the same lock taken before calling
  540. // assign_HF_handle(), release_HF_handle(), and suspend_HF_handle().
  541. //
  542. int
  543. reinstate_HF_handle(
  544. HandleFactory *hfact,
  545. HFHandle handle)
  546. {
  547. int entry_index;
  548. HFEntry *entry;
  549. // Compute the index of the entry by taking the handle value modulo the
  550. // table size. Since the table size is a power of two, we can simply
  551. // subtract one to produce a mask and then conjoin the mask with the
  552. // handle value.
  553. entry_index = handle & hfact->table_size - 1;
  554. entry = &hfact->entries[entry_index];
  555. if (entry->handle != handle + HANDLE_RANGE_STEP ||
  556. entry->handle == entry->next_handle)
  557. {
  558. // Either the indexed entry does not refer to the provided handle's
  559. // suspension value, or the entry is unassigned. In either case, abort
  560. // and return an error code to the client.
  561. return 1;
  562. }
  563. // Reinstate the handle.
  564. entry->handle -= HANDLE_RANGE_STEP;
  565. // This reinstatement operation decreases the hysteresis debt.
  566. if (hfact->hysteresis_debt > 0)
  567. {
  568. hfact->hysteresis_debt--;
  569. }
  570. // return an indication of success to the client.
  571. return 0;
  572. }
  573. // This function validates a handle and returns either the associated pointer
  574. // (if the handle is valid) or a null pointer value (if the handle is invalid).
  575. // If the handle has not been released but a null value is returned, then the
  576. // handle has been revoked by the handle factory. This is expected to be a
  577. // highly unusual occurrence; however, since it can happen, any program that
  578. // employs the handle factory must have some auxiliary mechanism for retrieving
  579. // the desired pointer information. Once the pointer is retrieved through this
  580. // (presumably expensive) auxiliary means, a new handle can be reassigned to
  581. // the pointer by another call to assign_HF_handle().
  582. //
  583. // Even in a multi-threaded environment, it is not necessary to take a lock
  584. // prior to calling this function. Careful sequencing of read and write
  585. // operations inside the handle factory code obviates the need to explicitly
  586. // lock the data structure for dereferencing handles.
  587. //
  588. void *
  589. dereference_HF_handle(
  590. HandleFactory *hfact,
  591. HFHandle handle)
  592. {
  593. HFHandle entry_handle;
  594. void *reference;
  595. //int verifier;
  596. int entry_index;
  597. /*volatile*/ HFEntry *entry; // volatile to ensure sequencing
  598. // This loop spins until the verifier variables begin and end a pass of
  599. // the loop with the same value. There is an extremely short sequence
  600. // of instructions in the expand and contract routines that modifies the
  601. // values of the entries and table_size variables, and these modifications
  602. // are bracketed by increments of the verifier variables in the reverse
  603. // order as they are here read. As long as the verifiers have the same
  604. // value, then entries and table_size are in a consistent state. This loop
  605. // should very rarely be executed more than once, since the modification in
  606. // the other routines is so short.
  607. do
  608. {
  609. //verifier = hfact->verifier1;
  610. // Compute the index of the entry by taking the handle value modulo the
  611. // table size. Since the table size is a power of two, we can simply
  612. // subtract one to produce a mask and then conjoin the mask with the
  613. // handle value.
  614. entry_index = handle & hfact->table_size - 1;
  615. entry = &hfact->entries[entry_index];
  616. // Get local copies of the reference pointer and handle value. The
  617. // order of the operations is important to correct multi-threaded
  618. // operation.
  619. reference = entry->reference; // first get reference
  620. entry_handle = entry->handle; // then get handle to check validity
  621. } while (0 /*verifier != hfact->verifier0*/);
  622. if (entry_handle == handle)
  623. {
  624. // The stored handle matches the provided handle, so the latter is
  625. // valid. We thus return the reference pointer.
  626. return reference;
  627. }
  628. else
  629. {
  630. // The stored handle does not match the provided handle, so the latter
  631. // is invalid. We thus return a null pointer.
  632. return 0;
  633. }
  634. }
  635. void *
  636. dereference_HF_handle_with_cb(
  637. HandleFactory *hfact,
  638. HFHandle handle,
  639. ULONG offset)
  640. {
  641. HFHandle entry_handle;
  642. void *reference;
  643. //int verifier;
  644. int entry_index;
  645. /*volatile*/ HFEntry *entry; // volatile to ensure sequencing
  646. // This loop spins until the verifier variables begin and end a pass of
  647. // the loop with the same value. There is an extremely short sequence
  648. // of instructions in the expand and contract routines that modifies the
  649. // values of the entries and table_size variables, and these modifications
  650. // are bracketed by increments of the verifier variables in the reverse
  651. // order as they are here read. As long as the verifiers have the same
  652. // value, then entries and table_size are in a consistent state. This loop
  653. // should very rarely be executed more than once, since the modification in
  654. // the other routines is so short.
  655. do
  656. {
  657. //verifier = hfact->verifier1;
  658. // Compute the index of the entry by taking the handle value modulo the
  659. // table size. Since the table size is a power of two, we can simply
  660. // subtract one to produce a mask and then conjoin the mask with the
  661. // handle value.
  662. entry_index = handle & hfact->table_size - 1;
  663. entry = &hfact->entries[entry_index];
  664. // Get local copies of the reference pointer and handle value. The
  665. // order of the operations is important to correct multi-threaded
  666. // operation.
  667. if ((entry->reference) && (entry->handle == handle)) {
  668. ASSERT(((PCLASSIFICATION_BLOCK)entry->reference)->NumberOfElements > offset);
  669. reference = (void *)((PCLASSIFICATION_BLOCK)entry->reference)->arpBlobBlock[offset];
  670. TRACE(CLASSIFY, entry->reference, reference, "dereference_HF_handle_with_cb");
  671. return reference;
  672. } else {
  673. reference = 0;
  674. return 0;
  675. }
  676. } while (0 /*verifier != hfact->verifier0*/);
  677. }
  678. #ifdef _TEST_HANDFACT
  679. // This is a test routine that simply verifies the internal valididy of the
  680. // handle factory's data structures. By defining the constant _TEST_HANDFACT,
  681. // this routine will be compiled and available to the client code. It can be
  682. // called at any time, unless running in a multi-threaded environment, in which
  683. // case the caller must first take the same lock used for assign_HF_handle()
  684. // and release_HF_handle. If the routine returns any value other than zero,
  685. // then the internal lists of records are in an inconsistent state.
  686. //
  687. int
  688. verify_HF_lists(
  689. HandleFactory *hfact)
  690. {
  691. int entry_count[3];
  692. int list;
  693. HFEntry *entry;
  694. for (list = 0; list < 3; list++)
  695. {
  696. entry_count[list] = 0;
  697. entry = &hfact->entry_list[list];
  698. do
  699. {
  700. entry_count[list]++;
  701. if (entry->next_entry->prev_entry != entry)
  702. {
  703. return 1;
  704. }
  705. entry = entry->next_entry;
  706. } while (entry != &hfact->entry_list[list]);
  707. entry_count[list]--;
  708. }
  709. if (entry_count[2] != hfact->population)
  710. {
  711. return 2;
  712. }
  713. if (entry_count[0] + entry_count[2] - 2 * hfact->pair_count !=
  714. entry_count[1])
  715. {
  716. return 3;
  717. }
  718. if (entry_count[0] + entry_count[1] + entry_count[2] != hfact->table_size)
  719. {
  720. return 4;
  721. }
  722. return 0;
  723. }
  724. #endif /* _TEST_HANDFACT */
  725. // This function doubles the size of the table in which the handles and pointers
  726. // are stored. It is called by assign_HF_handle() when there is insufficient
  727. // space in the table to assign the newly requested handle. If the expansion
  728. // is successful, the function returns a value of 0. If the expansion fails
  729. // (due, for example, to an inability to allocate memory), the function returns
  730. // a value of 1.
  731. //
  732. int expand_HF_table(
  733. HandleFactory *hfact)
  734. {
  735. int double_size;
  736. HFEntry *new_entries;
  737. HFEntry *old_entries;
  738. HFEntry *old_entry;
  739. HFEntry *low_entry;
  740. HFEntry *high_entry;
  741. HFEntry *assigned_entry;
  742. HFEntry *secondary_entry;
  743. HFEntry *other_entry;
  744. HFHandle handle;
  745. HFHandle next_handle;
  746. HFHandle other_handle;
  747. void *reference;
  748. int other_entry_index;
  749. int index;
  750. // Expanded table is double the size of the old table.
  751. double_size = hfact->table_size * 2;
  752. // Allocate space for the expanded table.
  753. NEW_HFEntry_array(new_entries,double_size);
  754. if (new_entries == 0)
  755. {
  756. // Memory could not be allocated for the new array of entries.
  757. // Therefore, we return an indication of failure.
  758. return 1;
  759. }
  760. // Since we are doubling the table size, we will be treating one more bit
  761. // of each handle as a bit of the entry index. The value of this bit
  762. // determines the index of the entry in the new table. For each entry,
  763. // we have to determine the value of this bit and relocate the entry to
  764. // the indicated location.
  765. for (index = 0; index < hfact->table_size; index++)
  766. {
  767. old_entry = &hfact->entries[index];
  768. low_entry = &new_entries[index];
  769. high_entry = &new_entries[hfact->table_size + index];
  770. handle = old_entry->handle;
  771. next_handle = old_entry->next_handle;
  772. reference = old_entry->reference;
  773. // One of the two entries in the new table that correspond to the
  774. // indexed entry in the old table will have a next handle value equal
  775. // to the next handle value of the entry in the old table, and one will
  776. // have a handle value equal to the indexed entry's next handle plus
  777. // the old table size.
  778. other_handle = next_handle + hfact->table_size;
  779. if (other_handle == 0)
  780. {
  781. // The handle value has wrapped around back to zero; however, zero
  782. // is a reserved value, so we instead set the next handle to the
  783. // subsequent legal value, which is the new table size.
  784. other_handle = double_size;
  785. }
  786. if ((handle & hfact->table_size) == 0)
  787. {
  788. // The handle of the old entry has a zero in its next bit, so the
  789. // old entry will be located in the lower half of the new table.
  790. if ((next_handle & hfact->table_size) == 0)
  791. {
  792. // The next handle of the old entry has a zero in its next bit,
  793. // so this value will be the next handle for the lower entry
  794. // and the other next handle value will be the next handle
  795. // value for the higher entry. The high entry handle is set
  796. // equal to its next handle because it is unassigned.
  797. high_entry->handle = other_handle;
  798. high_entry->next_handle = other_handle;
  799. low_entry->next_handle = next_handle;
  800. }
  801. else
  802. {
  803. // The next handle of the old entry has a zero in its next bit,
  804. // so this value will be the next handle for the higher entry
  805. // and the other next handle value will be the next handle
  806. // value for the lower entry. The high entry handle is set
  807. // equal to its next handle because it is unassigned.
  808. high_entry->handle = next_handle;
  809. high_entry->next_handle = next_handle;
  810. low_entry->next_handle = other_handle;
  811. }
  812. // The high entry is unassigned, so set its reference to null.
  813. // Copy the information from the old entry to the low entry.
  814. // Remove the old entry from the assigned list, and replace it
  815. // with the low entry.
  816. high_entry->reference = 0;
  817. low_entry->handle = handle;
  818. low_entry->reference = reference;
  819. old_entry->next_entry->prev_entry = low_entry;
  820. old_entry->prev_entry->next_entry = low_entry;
  821. low_entry->next_entry = old_entry->next_entry;
  822. low_entry->prev_entry = old_entry->prev_entry;
  823. }
  824. else
  825. {
  826. // The handle of the old entry has a one in its next bit, so the
  827. // old entry will be located in the higher half of the new table.
  828. if ((next_handle & hfact->table_size) == 0)
  829. {
  830. // The next handle of the old entry has a zero in its next bit,
  831. // so this value will be the next handle for the lower entry
  832. // and the other next handle value will be the next handle
  833. // value for the higher entry. The low entry handle is set
  834. // equal to its next handle because it is unassigned.
  835. high_entry->next_handle = other_handle;
  836. low_entry->handle = next_handle;
  837. low_entry->next_handle = next_handle;
  838. }
  839. else
  840. {
  841. // The next handle of the old entry has a zero in its next bit,
  842. // so this value will be the next handle for the higher entry
  843. // and the other next handle value will be the next handle
  844. // value for the lower entry. The low entry handle is set
  845. // equal to its next handle because it is unassigned.
  846. high_entry->next_handle = next_handle;
  847. low_entry->handle = other_handle;
  848. low_entry->next_handle = other_handle;
  849. }
  850. // The low entry is unassigned, so set its reference to null.
  851. // Copy the information from the old entry to the high entry.
  852. // Remove the old entry from the assigned list, and replace it
  853. // with the high entry.
  854. low_entry->reference = 0;
  855. high_entry->handle = handle;
  856. high_entry->reference = reference;
  857. old_entry->next_entry->prev_entry = high_entry;
  858. old_entry->prev_entry->next_entry = high_entry;
  859. high_entry->next_entry = old_entry->next_entry;
  860. high_entry->prev_entry = old_entry->prev_entry;
  861. }
  862. }
  863. // All of the unassigned entries in the new table will be placed on the
  864. // secondary list. We loop through the assigned list and place the
  865. // unassigned entry corresponding each assigned entry onto the secondary
  866. // list. Doing the list assignment in this manner tends to approximately
  867. // sort the secondary list according to handle value, since the assigned
  868. // list is sorted according to assignment order, and this approximately
  869. // correlates to the handle value.
  870. assigned_entry = hfact->entry_list[LD_ASSIGNED].next_entry;
  871. secondary_entry = &hfact->entry_list[LD_SECONDARY];
  872. while (assigned_entry != &hfact->entry_list[LD_ASSIGNED])
  873. {
  874. other_entry_index =
  875. assigned_entry->handle + hfact->table_size & double_size - 1;
  876. other_entry = &new_entries[other_entry_index];
  877. secondary_entry->next_entry = other_entry;
  878. other_entry->prev_entry = secondary_entry;
  879. secondary_entry = other_entry;
  880. assigned_entry = assigned_entry->next_entry;
  881. }
  882. // Wrap up lists by connecting in tails.
  883. secondary_entry->next_entry = &hfact->entry_list[LD_SECONDARY];
  884. hfact->entry_list[LD_SECONDARY].prev_entry = secondary_entry;
  885. // This expansion increases the hysteresis debt by the cost of one set of
  886. // allocation and deallocation operations plus the cost of splitting each
  887. // entry into two entries.
  888. hfact->hysteresis_debt += ALLOCATION_COST + hfact->table_size;
  889. // Save a pointer to the old entry table so that it can be deallocated.
  890. old_entries = hfact->entries;
  891. // Note that we have not modified the handle, next_handle, or reference
  892. // fields of any entries in the old table. Therefore, any calls to the
  893. // dereference_HF_handle() routine that may have been made by other threads
  894. // during the above operations would have been performed successfully.
  895. // We are now about to increase the table size and update the entries
  896. // variable to point to the new table. These operations must be performed
  897. // atomically in order for the dereference routine to perform correctly.
  898. // We thus bracket the operations with increments to the verifier variables.
  899. // When the verifiers have the same value, the table_size and entries
  900. // variables are in a consistent state. This is checked by the dereference
  901. // routine.
  902. hfact->verifier0++; // begin critical section
  903. hfact->entries = new_entries;
  904. hfact->table_size = double_size;
  905. hfact->verifier1 = hfact->verifier0; // end critical section
  906. // Deallocate the old table.
  907. GpcFreeMem(old_entries, handleFactoryTag);
  908. // Since the new table was created by expanding a half-size table, the pair
  909. // count must be zero.
  910. hfact->pair_count = 0;
  911. // return an indication of success.
  912. return 0;
  913. }
  914. // This function halves the size of the table in which the handles and pointers
  915. // are stored. In order to reduce the amount of space consumed by the handle
  916. // factory, this function is called called by release_HF_handle() and
  917. // revoke_ancient_HF_handles() when they determine that the table can and should
  918. // be contracted. The table can be contracted when pair_count == 0 and
  919. // table_size > 2. However, the table may not be contracted then, because
  920. // hysteresis is employed both to keep the mean assignment and release times
  921. // constant and to minimize the allocation chatter of rapidly expanding and
  922. // contracting the table. If the contraction is successful, the function
  923. // returns a value of 0. If the contraction fails, the function returns a
  924. // value of 1.
  925. //
  926. int contract_HF_table(
  927. HandleFactory *hfact)
  928. {
  929. HFEntry *new_entries;
  930. HFEntry *old_entries;
  931. int *list;
  932. int half_size;
  933. int quarter_size;
  934. int index;
  935. HFEntry *high_entry1;
  936. HFEntry *high_entry0;
  937. HFEntry *low_entry1;
  938. HFEntry *low_entry0;
  939. HFEntry *new_entry1;
  940. HFEntry *new_entry0;
  941. HFHandle adjusted_high_next_handle1;
  942. HFHandle adjusted_low_next_handle1;
  943. HFHandle next_handle1;
  944. HFHandle adjusted_high_next_handle0;
  945. HFHandle adjusted_low_next_handle0;
  946. HFHandle next_handle0;
  947. HFHandle adjusted_new_handle0;
  948. HFHandle adjusted_new_handle1;
  949. HFEntry *entry;
  950. HFEntry *primary_entry;
  951. HFEntry *secondary_entry;
  952. // Contracted table is half the size of the old table.
  953. half_size = hfact->table_size / 2;
  954. quarter_size = half_size / 2;
  955. // Allocate space for the contracted table.
  956. NEW_HFEntry_array(new_entries,half_size);
  957. if (new_entries == 0)
  958. {
  959. // Memory could not be allocated for the new array of entries, so we
  960. // are ironically prevented from reducing the amount of memory that
  961. // the handle factory is consuming. Therefore, we return an indication
  962. // of failure.
  963. return 1;
  964. }
  965. // Allocate space for auxiliary array of list indicators
  966. NEW_int_array(list,half_size);
  967. if (list == 0)
  968. {
  969. // Memory could not be allocated for the auxiliary array, so again we
  970. // are ironically prevented from reducing the amount of memory that
  971. // the handle factory is consuming. Therefore, we return an indication
  972. // of failure. First, however, we must free the memory allocated for
  973. // the new array of entries above.
  974. GpcFreeMem(new_entries, handleFactoryTag);
  975. return 1;
  976. }
  977. // Since we are halving the size of the table, it might seem reasonable to
  978. // loop through each index of the new table and merge the two corresponding
  979. // entries from the old table. This is in fact what the following routine
  980. // does; however, it does it by looping through only half of the new indices
  981. // and processing two merges for each index. It does this so that it can
  982. // then examine the two new entries to determine on which list to place each
  983. // of them.
  984. for (index = 0; index < quarter_size; index++)
  985. {
  986. // We're looking at four entries at once. First we merge high_entry1
  987. // and low_entry1, and then we independently merge high_entry0 and
  988. // low_entry0. After the two merges, we examine the results jointly.
  989. high_entry1 = &hfact->entries[half_size + quarter_size + index];
  990. high_entry0 = &hfact->entries[half_size + index];
  991. low_entry1 = &hfact->entries[quarter_size + index];
  992. low_entry0 = &hfact->entries[index];
  993. new_entry1 = &new_entries[quarter_size + index];
  994. new_entry0 = &new_entries[index];
  995. // When merging two entries, the next handle value for the combined
  996. // entry is equal to the larger next handle value of the two, minus
  997. // the new table size. However, the determination of which is larger
  998. // must be made with respect to their logical acyclic values rather
  999. // than their actual cyclic values, so we subtract from each the value
  1000. // of handle_base, modulo the size of the handle space. The modulo is
  1001. // implicit.
  1002. adjusted_high_next_handle1 =
  1003. high_entry1->next_handle - hfact->handle_base;
  1004. adjusted_low_next_handle1 =
  1005. low_entry1->next_handle - hfact->handle_base;
  1006. next_handle1 = __max(adjusted_high_next_handle1,
  1007. adjusted_low_next_handle1) + hfact->handle_base - half_size;
  1008. // Since handle 1 is -- by definition -- in either the second or fourth
  1009. // quarter of the table, there is no need to check for the reserved
  1010. // value of zero.
  1011. if (high_entry1->handle != high_entry1->next_handle)
  1012. {
  1013. // The high entry is assigned, so we copy its handle value and
  1014. // reference pointer. Also, we remove it from the assigned list
  1015. // and replace it with the new entry.
  1016. new_entry1->handle = high_entry1->handle;
  1017. new_entry1->reference = high_entry1->reference;
  1018. high_entry1->next_entry->prev_entry = new_entry1;
  1019. high_entry1->prev_entry->next_entry = new_entry1;
  1020. new_entry1->next_entry = high_entry1->next_entry;
  1021. new_entry1->prev_entry = high_entry1->prev_entry;
  1022. }
  1023. else if (low_entry1->handle != low_entry1->next_handle)
  1024. {
  1025. // The low entry is assigned, so we copy its handle value and
  1026. // reference pointer. Also, we remove it from the assigned list
  1027. // and replace it with the new entry.
  1028. new_entry1->handle = low_entry1->handle;
  1029. new_entry1->reference = low_entry1->reference;
  1030. low_entry1->next_entry->prev_entry = new_entry1;
  1031. low_entry1->prev_entry->next_entry = new_entry1;
  1032. new_entry1->next_entry = low_entry1->next_entry;
  1033. new_entry1->prev_entry = low_entry1->prev_entry;
  1034. }
  1035. else
  1036. {
  1037. // Neither entry is assigned, so we indicate an unassigned condition
  1038. // in the new entry.
  1039. new_entry1->handle = next_handle1;
  1040. new_entry1->reference = 0;
  1041. if (adjusted_high_next_handle1 < adjusted_low_next_handle1)
  1042. {
  1043. // The high entry next handle has a lesser value than the low
  1044. // entry next handle, so the high entry must be on the primary
  1045. // list. We remove it from the primary list and replace it
  1046. // with the new entry.
  1047. high_entry1->next_entry->prev_entry = new_entry1;
  1048. high_entry1->prev_entry->next_entry = new_entry1;
  1049. new_entry1->next_entry = high_entry1->next_entry;
  1050. new_entry1->prev_entry = high_entry1->prev_entry;
  1051. }
  1052. else
  1053. {
  1054. // The low entry next handle has a lesser value than the high
  1055. // entry next handle, so the low entry must be on the primary
  1056. // list. We remove it from the primary list and replace it
  1057. // with the new entry.
  1058. low_entry1->next_entry->prev_entry = new_entry1;
  1059. low_entry1->prev_entry->next_entry = new_entry1;
  1060. new_entry1->next_entry = low_entry1->next_entry;
  1061. new_entry1->prev_entry = low_entry1->prev_entry;
  1062. }
  1063. }
  1064. // Set the next handle for the new entry.
  1065. new_entry1->next_handle = next_handle1;
  1066. // When merging two entries, the next handle value for the combined
  1067. // entry is equal to the larger next handle value of the two, minus
  1068. // the new table size. However, the determination of which is larger
  1069. // must be made with respect to their logical acyclic values rather
  1070. // than their actual cyclic values, so we subtract from each the value
  1071. // of handle_base, modulo the size of the handle space. The modulo is
  1072. // implicit.
  1073. adjusted_high_next_handle0 =
  1074. high_entry0->next_handle - hfact->handle_base;
  1075. adjusted_low_next_handle0 =
  1076. low_entry0->next_handle - hfact->handle_base;
  1077. next_handle0 = __max(adjusted_high_next_handle0,
  1078. adjusted_low_next_handle0) + hfact->handle_base - half_size;
  1079. if (next_handle0 == 0)
  1080. {
  1081. // The handle value has wrapped around back to zero; however, zero
  1082. // is a reserved value, so we instead set the next handle to the
  1083. // subsequent legal value, which is the new table size.
  1084. next_handle0 = half_size;
  1085. }
  1086. if (high_entry0->handle != high_entry0->next_handle)
  1087. {
  1088. // The high entry is assigned, so we copy its handle value and
  1089. // reference pointer. Also, we remove it from the assigned list
  1090. // and replace it with the new entry.
  1091. new_entry0->handle = high_entry0->handle;
  1092. new_entry0->reference = high_entry0->reference;
  1093. high_entry0->next_entry->prev_entry = new_entry0;
  1094. high_entry0->prev_entry->next_entry = new_entry0;
  1095. new_entry0->next_entry = high_entry0->next_entry;
  1096. new_entry0->prev_entry = high_entry0->prev_entry;
  1097. }
  1098. else if (low_entry0->handle != low_entry0->next_handle)
  1099. {
  1100. // The low entry is assigned, so we copy its handle value and
  1101. // reference pointer. Also, we remove it from the assigned list
  1102. // and replace it with the new entry.
  1103. new_entry0->handle = low_entry0->handle;
  1104. new_entry0->reference = low_entry0->reference;
  1105. low_entry0->next_entry->prev_entry = new_entry0;
  1106. low_entry0->prev_entry->next_entry = new_entry0;
  1107. new_entry0->next_entry = low_entry0->next_entry;
  1108. new_entry0->prev_entry = low_entry0->prev_entry;
  1109. }
  1110. else
  1111. {
  1112. // Neither entry is assigned, so we indicate an unassigned condition
  1113. // in the new entry.
  1114. new_entry0->handle = next_handle0;
  1115. new_entry0->reference = 0;
  1116. if (adjusted_high_next_handle0 < adjusted_low_next_handle0)
  1117. {
  1118. // The high entry next handle has a lesser value than the low
  1119. // entry next handle, so the high entry must be on the primary
  1120. // list. We remove it from the primary list and replace it
  1121. // with the new entry.
  1122. high_entry0->next_entry->prev_entry = new_entry0;
  1123. high_entry0->prev_entry->next_entry = new_entry0;
  1124. new_entry0->next_entry = high_entry0->next_entry;
  1125. new_entry0->prev_entry = high_entry0->prev_entry;
  1126. }
  1127. else
  1128. {
  1129. // The low entry next handle has a lesser value than the high
  1130. // entry next handle, so the low entry must be on the primary
  1131. // list. We remove it from the primary list and replace it
  1132. // with the new entry.
  1133. low_entry0->next_entry->prev_entry = new_entry0;
  1134. low_entry0->prev_entry->next_entry = new_entry0;
  1135. new_entry0->next_entry = low_entry0->next_entry;
  1136. new_entry0->prev_entry = low_entry0->prev_entry;
  1137. }
  1138. }
  1139. // Set the next handle for the new entry.
  1140. new_entry0->next_handle = next_handle0;
  1141. // Now that we have merged high_entry1 and low_entry1 into new_entry1,
  1142. // and independently merged high_entry0 and low_entry0 into new_entry0,
  1143. // we examine the two new entries to determine on which list to place
  1144. // each of them. Note that we do not actually manipulate the lists in
  1145. // this portion of the code; we merely make decisions and record these
  1146. // decisions for the future.
  1147. if (new_entry0->handle == new_entry0->next_handle &&
  1148. new_entry1->handle == new_entry1->next_handle)
  1149. {
  1150. // Both new_entry0 and new_entry1 are unassigned, so one of them
  1151. // belongs on the primary list and the other on the secondary list.
  1152. // Which goes on which is determined by a comparison of their handle
  1153. // values. We're being tricky with unsigned integer math here.
  1154. // Before comparing the two handles, we subtract from each the value
  1155. // of handle_base, modulo the size of the handle space (the modulo
  1156. // is implicit). This allows the effective comparison of their
  1157. // logical acyclic values rather than their actual cyclic values.
  1158. adjusted_new_handle0 = new_entry0->handle - hfact->handle_base;
  1159. adjusted_new_handle1 = new_entry1->handle - hfact->handle_base;
  1160. if (adjusted_new_handle0 < adjusted_new_handle1)
  1161. {
  1162. // The handle value for new_entry0 is lower, so new_entry0
  1163. // belongs on the primary list and new_entry1 on the secondary
  1164. // list. We indicate this decision in the list array.
  1165. list[index] = LD_PRIMARY;
  1166. list[quarter_size + index] = LD_SECONDARY;
  1167. }
  1168. else
  1169. {
  1170. // The handle value for new_entry1 is lower, so new_entry1
  1171. // belongs on the primary list and new_entry0 on the secondary
  1172. // list. We indicate this decision in the list array.
  1173. list[index] = LD_SECONDARY;
  1174. list[quarter_size + index] = LD_PRIMARY;
  1175. }
  1176. }
  1177. else
  1178. {
  1179. // Either new_entry0 or new_entry1 (or both) is assigned, and it is
  1180. // therefore already on the assigned list. If one of the entries
  1181. // is not assigned, it belongs on the secondary list. We indicate
  1182. // this decision in both places of the list array, which is safe to
  1183. // do since the assigned entry's list indicator will never be
  1184. // examined.
  1185. list[index] = LD_SECONDARY;
  1186. list[quarter_size + index] = LD_SECONDARY;
  1187. }
  1188. if (new_entry0->handle != new_entry0->next_handle &&
  1189. new_entry1->handle != new_entry1->next_handle)
  1190. {
  1191. // Both new_entry0 and new_entry1 are assigned, so they form a pair.
  1192. // We thus increment the pair count. Note that we never set the
  1193. // pair count to zero above, but this was not necessary since the
  1194. // table could not be contracted unless the pair count was zero.
  1195. hfact->pair_count++;
  1196. }
  1197. }
  1198. // At this point, the table has been completely contracted except for the
  1199. // reassembly of the unassigned lists. In the code above, any entries that
  1200. // had previously been on the secondary list were merged with assigned
  1201. // entries, so they are no longer relevant. Only those entries that had
  1202. // previously been (and are still) on the primary list will still be
  1203. // unassigned. We now loop through the primary list and place each list
  1204. // element on the appropriate list, as indicated by the list array. Doing
  1205. // the list assignment in these two steps preserves the general order of
  1206. // the entries, which has some value since they will tend to be partially
  1207. // sorted.
  1208. entry = hfact->entry_list[LD_PRIMARY].next_entry;
  1209. primary_entry = &hfact->entry_list[LD_PRIMARY];
  1210. secondary_entry = &hfact->entry_list[LD_SECONDARY];
  1211. while (entry != &hfact->entry_list[LD_PRIMARY])
  1212. {
  1213. if (list[entry->handle & half_size - 1] == LD_PRIMARY)
  1214. {
  1215. // The list array indicates the primary list, so place the entry
  1216. // onto the primary list.
  1217. primary_entry->next_entry = entry;
  1218. entry->prev_entry = primary_entry;
  1219. primary_entry = entry;
  1220. }
  1221. else
  1222. {
  1223. // The list array indicates the secondary list, so place the entry
  1224. // onto the secondary list.
  1225. secondary_entry->next_entry = entry;
  1226. entry->prev_entry = secondary_entry;
  1227. secondary_entry = entry;
  1228. }
  1229. entry = entry->next_entry;
  1230. }
  1231. // Wrap up lists by connecting in tails.
  1232. primary_entry->next_entry = &hfact->entry_list[LD_PRIMARY];
  1233. hfact->entry_list[LD_PRIMARY].prev_entry = primary_entry;
  1234. secondary_entry->next_entry = &hfact->entry_list[LD_SECONDARY];
  1235. hfact->entry_list[LD_SECONDARY].prev_entry = secondary_entry;
  1236. // This contraction increases the hysteresis debt by the cost of one set of
  1237. // allocation and deallocation operations plus the cost of merging each
  1238. // pair of entries into a single entry.
  1239. hfact->hysteresis_debt += ALLOCATION_COST + half_size;
  1240. // Save a pointer to the old entry table so that it can be deallocated.
  1241. old_entries = hfact->entries;
  1242. // Note that we have not modified the handle, next_handle, or reference
  1243. // fields of any entries in the old table. Therefore, any calls to the
  1244. // dereference_HF_handle() routine that may have been made by other threads
  1245. // during the above operations would have been performed successfully.
  1246. // We are now about to decrease the table size and update the entries
  1247. // variable to point to the new table. These operations must be performed
  1248. // atomically in order for the dereference routine to perform correctly.
  1249. // We thus bracket the operations with increments to the verifier variables.
  1250. // When the verifiers have the same value, the table_size and entries
  1251. // variables are in a consistent state. This is checked by the dereference
  1252. // routine.
  1253. hfact->verifier0++; // begin critical section
  1254. hfact->table_size = half_size;
  1255. hfact->entries = new_entries;
  1256. hfact->verifier1 = hfact->verifier0; // end critical section
  1257. // Deallocate the old table and the auxiliary list indicator array.
  1258. GpcFreeMem(old_entries, handleFactoryTag);
  1259. GpcFreeMem(list, handleFactoryTag);
  1260. // return an indication of success.
  1261. return 0;
  1262. }
  1263. // This function revokes handles that are between handle_base and handle_base
  1264. // + 2 * HANDLE_RANGE_STEP - 1, inclusive. It then increments the value of
  1265. // handle_base by HANDLE_RANGE_STEP. Suspended handles will be revoked one
  1266. // revokation pass later than non-suspended handles.
  1267. //
  1268. void revoke_ancient_HF_handles(
  1269. HandleFactory *hfact)
  1270. {
  1271. HFHandle new_handle_base;
  1272. int half_size;
  1273. int index;
  1274. HFEntry *high_entry;
  1275. HFEntry *low_entry;
  1276. HFHandle adjusted_high_handle;
  1277. HFHandle adjusted_low_handle;
  1278. HFHandle adjusted_high_next_handle;
  1279. HFHandle adjusted_low_next_handle;
  1280. HFHandle handle;
  1281. volatile HFEntry *seq_entry; // volatile to ensure sequencing
  1282. // Compute new handle base.
  1283. new_handle_base = hfact->handle_base + HANDLE_RANGE_STEP;
  1284. // It might seem reasonable to loop through each index of the table and
  1285. // determine whether to revoke the handle of each entry. This is in fact
  1286. // what the following routine does; however, it does it by looping through
  1287. // only half of the indices and examining two entries for each index. It
  1288. // does this so that it can compare the two entries to determine on which
  1289. // list to place each of them.
  1290. half_size = hfact->table_size / 2;
  1291. for (index = 0; index < half_size; index++)
  1292. {
  1293. // We're looking at two entries at once.
  1294. high_entry = &hfact->entries[half_size + index];
  1295. low_entry = &hfact->entries[index];
  1296. // We're being tricky with unsigned integer math here. Before making
  1297. // comparisons on either handle, we subtract from it the value of
  1298. // handle_base, modulo the size of the handle space (the modulo is
  1299. // implicit). This allows the effective comparison of its logical
  1300. // acyclic value rather than its actual cyclic value.
  1301. adjusted_high_handle = high_entry->handle - hfact->handle_base;
  1302. adjusted_low_handle = low_entry->handle - hfact->handle_base;
  1303. if (adjusted_high_handle < 2 * HANDLE_RANGE_STEP ||
  1304. adjusted_low_handle < 2 * HANDLE_RANGE_STEP)
  1305. {
  1306. // At least one of the handles is less than twice HANDLE_RANGE_STEP
  1307. // more than the current handle base, so it will need to be updated.
  1308. // For the vast majority of cases, this test is expected to fail,
  1309. // and so all of the following work can be skipped.
  1310. if (high_entry->handle != high_entry->next_handle &&
  1311. low_entry->handle != low_entry->next_handle)
  1312. {
  1313. // Both of the entries are assigned, so, since at least one of
  1314. // them will be revoked, we will be losing one pair.
  1315. hfact->pair_count--;
  1316. }
  1317. if (high_entry->handle == high_entry->next_handle ||
  1318. adjusted_high_handle < 2 * HANDLE_RANGE_STEP)
  1319. {
  1320. // Either the high entry is unassigned or in need of revokation
  1321. // (after which it will be unassigned), so we remove it from
  1322. // whatever list it is on. We do this because all unassigned
  1323. // entries will be added to the appropriate list below.
  1324. high_entry->next_entry->prev_entry = high_entry->prev_entry;
  1325. high_entry->prev_entry->next_entry = high_entry->next_entry;
  1326. // Zeroing these pointers is unnecessary, but it will help to
  1327. // catch any mistakes made further down.
  1328. high_entry->next_entry = 0;
  1329. high_entry->prev_entry = 0;
  1330. }
  1331. if (adjusted_high_handle < 2 * HANDLE_RANGE_STEP)
  1332. {
  1333. // The high handle needs to be updated.
  1334. if (high_entry->handle != high_entry->next_handle)
  1335. {
  1336. // The high handle is assigned, so this updating will
  1337. // revoke the handle. Thus, we decrement the population.
  1338. hfact->population--;
  1339. }
  1340. // Compute the handle value as the maximum of (1) the next
  1341. // handle and (2) the new handle base plus the entry index.
  1342. // We're being tricky with unsigned integer math here. The
  1343. // maximum involves partial decomposition of the sums, from
  1344. // which we then subtract the value of handle_base, modulo the
  1345. // size of the handle space (the modulo is implicit). Thus,
  1346. // the maximum is taken with respect to the logical acyclic
  1347. // values rather than the actual cyclic values.
  1348. adjusted_high_next_handle =
  1349. high_entry->next_handle - hfact->handle_base;
  1350. handle = __max(adjusted_high_next_handle,
  1351. HANDLE_RANGE_STEP + half_size + index) + hfact->handle_base;
  1352. // Since the high handle is -- by definition -- in the upper
  1353. // half of the table, there is no need to check for the reserved
  1354. // value of zero.
  1355. // Update the handle value. Since this updating will invalidate
  1356. // the handle if it is currently assigned, the order of the
  1357. // operations is important to correct multi-threaded operation.
  1358. seq_entry = high_entry;
  1359. seq_entry->next_handle = handle;
  1360. seq_entry->handle = handle; // first invalidate handle
  1361. seq_entry->reference = 0; // then clear reference
  1362. }
  1363. if (low_entry->handle == low_entry->next_handle ||
  1364. adjusted_low_handle < 2 * HANDLE_RANGE_STEP)
  1365. {
  1366. // Either the low entry is unassigned or in need of revokation
  1367. // (after which it will be unassigned), so we remove it from
  1368. // whatever list it is on. We do this because all unassigned
  1369. // entries will be added to the appropriate list below.
  1370. low_entry->next_entry->prev_entry = low_entry->prev_entry;
  1371. low_entry->prev_entry->next_entry = low_entry->next_entry;
  1372. // Zeroing these pointers is unnecessary, but it will help to
  1373. // catch any mistakes made further down.
  1374. low_entry->next_entry = 0;
  1375. low_entry->prev_entry = 0;
  1376. }
  1377. if (adjusted_low_handle < 2 * HANDLE_RANGE_STEP)
  1378. {
  1379. // The low handle needs to be updated.
  1380. if (low_entry->handle != low_entry->next_handle)
  1381. {
  1382. // The low handle is assigned, so this updating will
  1383. // revoke the handle. Thus, we decrement the population.
  1384. hfact->population--;
  1385. }
  1386. // Compute the handle value as the maximum of (1) the next
  1387. // handle and (2) the new handle base plus the entry index.
  1388. // We're being tricky with unsigned integer math here. The
  1389. // maximum involves partial decomposition of the sums, from
  1390. // which we then subtract the value of handle_base, modulo the
  1391. // size of the handle space (the modulo is implicit). Thus,
  1392. // the maximum is taken with respect to the logical acyclic
  1393. // values rather than the actual cyclic values.
  1394. adjusted_low_next_handle =
  1395. low_entry->next_handle - hfact->handle_base;
  1396. handle = __max(adjusted_low_next_handle,
  1397. HANDLE_RANGE_STEP + index) + hfact->handle_base;
  1398. if (handle == 0)
  1399. {
  1400. // The handle value has wrapped around back to zero;
  1401. // however, zero is a reserved value, so we instead set the
  1402. // handle to the subsequent legal value, which is the table
  1403. // size.
  1404. handle = hfact->table_size;
  1405. }
  1406. // Update the handle value. Since this updating will invalidate
  1407. // the handle if it is currently assigned, the order of the
  1408. // operations is important to correct multi-threaded operation.
  1409. seq_entry = low_entry;
  1410. seq_entry->next_handle = handle;
  1411. seq_entry->handle = handle; // first invalidate handle
  1412. seq_entry->reference = 0; // then clear reference
  1413. }
  1414. if (high_entry->handle != high_entry->next_handle)
  1415. {
  1416. // The high entry is still assigned, so the low entry belongs
  1417. // on the secondary list.
  1418. low_entry->next_entry = &hfact->entry_list[LD_SECONDARY];
  1419. low_entry->prev_entry =
  1420. hfact->entry_list[LD_SECONDARY].prev_entry;
  1421. hfact->entry_list[LD_SECONDARY].prev_entry->next_entry =
  1422. low_entry;
  1423. hfact->entry_list[LD_SECONDARY].prev_entry = low_entry;
  1424. }
  1425. else if (low_entry->handle != low_entry->next_handle)
  1426. {
  1427. // The low entry is still assigned, so the high entry belongs
  1428. // on the secondary list.
  1429. high_entry->next_entry = &hfact->entry_list[LD_SECONDARY];
  1430. high_entry->prev_entry =
  1431. hfact->entry_list[LD_SECONDARY].prev_entry;
  1432. hfact->entry_list[LD_SECONDARY].prev_entry->next_entry =
  1433. high_entry;
  1434. hfact->entry_list[LD_SECONDARY].prev_entry = high_entry;
  1435. }
  1436. else
  1437. {
  1438. // Neither entry is still assigned, so one entry belongs on the
  1439. // primary list and one on the secondary list. Which goes on
  1440. // which is determined by a comparison of their handle values.
  1441. // We're being tricky with unsigned integer math here. Before
  1442. // comparing the two handles, we subtract from each the value
  1443. // of handle_base, modulo the size of the handle space (the
  1444. // modulo is implicit). This allows the effective comparison
  1445. // of their logical acyclic values rather than their actual
  1446. // cyclic values.
  1447. adjusted_high_next_handle =
  1448. high_entry->next_handle - new_handle_base;
  1449. adjusted_low_next_handle =
  1450. low_entry->next_handle - new_handle_base;
  1451. if (adjusted_low_next_handle < adjusted_high_next_handle)
  1452. {
  1453. // The handle value for the low entry is smaller, so it
  1454. // belongs on the primary list and the high entry on the
  1455. // secondary list.
  1456. high_entry->next_entry = &hfact->entry_list[LD_SECONDARY];
  1457. high_entry->prev_entry =
  1458. hfact->entry_list[LD_SECONDARY].prev_entry;
  1459. hfact->entry_list[LD_SECONDARY].prev_entry->next_entry =
  1460. high_entry;
  1461. hfact->entry_list[LD_SECONDARY].prev_entry = high_entry;
  1462. low_entry->next_entry = &hfact->entry_list[LD_PRIMARY];
  1463. low_entry->prev_entry =
  1464. hfact->entry_list[LD_PRIMARY].prev_entry;
  1465. hfact->entry_list[LD_PRIMARY].prev_entry->next_entry =
  1466. low_entry;
  1467. hfact->entry_list[LD_PRIMARY].prev_entry = low_entry;
  1468. }
  1469. else
  1470. {
  1471. // The handle value for the high entry is smaller, so it
  1472. // belongs on the primary list and the low entry on the
  1473. // secondary list.
  1474. high_entry->next_entry = &hfact->entry_list[LD_PRIMARY];
  1475. high_entry->prev_entry =
  1476. hfact->entry_list[LD_PRIMARY].prev_entry;
  1477. hfact->entry_list[LD_PRIMARY].prev_entry->next_entry =
  1478. high_entry;
  1479. hfact->entry_list[LD_PRIMARY].prev_entry = high_entry;
  1480. low_entry->next_entry = &hfact->entry_list[LD_SECONDARY];
  1481. low_entry->prev_entry =
  1482. hfact->entry_list[LD_SECONDARY].prev_entry;
  1483. hfact->entry_list[LD_SECONDARY].prev_entry->next_entry =
  1484. low_entry;
  1485. hfact->entry_list[LD_SECONDARY].prev_entry = low_entry;
  1486. }
  1487. }
  1488. }
  1489. }
  1490. // Update the handle base with the new handle base.
  1491. hfact->handle_base = new_handle_base;
  1492. // To contract the table, there must be no pairs, because otherwise two
  1493. // assigned handles would yield the same entry index and thereby conflict.
  1494. // Furthermore, the table size must be greater than 2, because much of the
  1495. // handle factory code assumes that the table is at least of size 2. In
  1496. // addition to these strict requirements, hysteresis is employed both to
  1497. // keep the mean assignment and release times constant and to minimize the
  1498. // allocation chatter of rapidly expanding and contracting the table. Only
  1499. // if the hysteresis debt is zero will the table be contracted.
  1500. if (hfact->pair_count == 0 && hfact->table_size > 2 &&
  1501. hfact->hysteresis_debt == 0)
  1502. {
  1503. contract_HF_table(hfact);
  1504. // Note that we ignore the return code. If the contraction is
  1505. // unsuccessful, we just continue as usual. There is no real harm in
  1506. // not contracting the table, except that we consume more space than
  1507. // necessary.
  1508. }
  1509. }