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.

698 lines
20 KiB

  1. /*++
  2. Copyright (c) 1997-1999 Microsoft Corporation
  3. Module Name:
  4. genhash.h
  5. Abstract:
  6. *******************************************************************************
  7. *******************************************************************************
  8. ** **
  9. ** **
  10. ** G E N E R I C H A S H T A B L E **
  11. ** **
  12. ** **
  13. *******************************************************************************
  14. *******************************************************************************
  15. A generic hash table is an array of GENERIC_HASH_ROW_ENTRY structs. Each
  16. row entry contains an FRS_LIST struct that has a critical section, a list
  17. head and a count. Each entry in the table has a GENERIC_HASH_ENTRY_HEADER
  18. at the front of it with a list entry, a ULONG hash value and a reference
  19. count. Access to a row of the hash table is controlled by the critical
  20. section in the FRS_LIST struct.
  21. Parameters for a generic hash table:
  22. Number of rows in the hash table.
  23. Table name for error messages.
  24. A compare function for insert (collisions) and for lookups,
  25. A key offset into the node entry to the start of the key data for the compare
  26. function.
  27. A key length for the compare.
  28. A memory free function to use if the ref count goes to zero.
  29. A hash function on the key data,
  30. A display node routine that takes the address of an entry. For error msgs
  31. and dump table.
  32. RowLockEnabled, TRUE means row locking is enabled (i.e. multithread
  33. usage of table). (Always enabled. FALSE is TBI)
  34. RefCountEnabled, TRUE if ref counting on data entries is enabled.
  35. (Always enabled. FALSE is TBI)
  36. A LockTimeout value in milliseconds. (TBI)
  37. AN optional heap handle to pass to the memory free function usefull if all the
  38. table entries are coming out of a special heap. (TBI)
  39. OffsetsEnabled - If TRUE then all the pointers in the table are calculated
  40. as offsets relative to the the OffsetBase. This is useful if you want to save
  41. the table contents to disk and you have a designated chunk of memory that the
  42. table elements are allocated out of (including the table structs). (TBI)
  43. OffsetBase - see above. (TBI)
  44. Author:
  45. David Orbits [davidor] 22-Apr-1997
  46. Environment:
  47. User Mode Service
  48. Revision History:
  49. --*/
  50. #ifndef _GENHASH_DEFINED_
  51. #define _GENHASH_DEFINED_
  52. typedef struct _GENERIC_HASH_TABLE_ *PGENERIC_HASH_TABLE;
  53. //
  54. // The free routine is called by the generic table package whenever
  55. // it needs to deallocate memory from the table that was allocated by calling
  56. // the user supplied allocation function.
  57. //
  58. typedef
  59. VOID
  60. (NTAPI *PGENERIC_HASH_FREE_ROUTINE) (
  61. PGENERIC_HASH_TABLE Table,
  62. PVOID Buffer
  63. );
  64. //
  65. // The compare routine is called on lookups to find an entry and on inserts
  66. // to check for duplicates.
  67. //
  68. typedef
  69. BOOL
  70. (NTAPI *PGENERIC_HASH_COMPARE_ROUTINE) (
  71. PVOID Buf1,
  72. PVOID Buf2,
  73. ULONG Length
  74. );
  75. //
  76. // The hash calc routine is called to generate the hash value of the key data
  77. // on lookups and inserts.
  78. //
  79. typedef
  80. ULONG
  81. (NTAPI *PGENERIC_HASH_CALC_ROUTINE) (
  82. PVOID Buf,
  83. ULONG Length
  84. );
  85. //
  86. // The filter function for use by GhtCleanTableByFilter.
  87. //
  88. typedef
  89. BOOL
  90. (NTAPI *PGENERIC_HASH_FILTER_ROUTINE) (
  91. PGENERIC_HASH_TABLE Table,
  92. PVOID Buf,
  93. PVOID Context
  94. );
  95. //
  96. // The print routine is called to dump an element.
  97. //
  98. typedef
  99. VOID
  100. (NTAPI *PGENERIC_HASH_PRINT_ROUTINE) (
  101. PGENERIC_HASH_TABLE Table,
  102. PVOID Buffer
  103. );
  104. //
  105. // The argument function passed to GhtEnumerateTable().
  106. //
  107. typedef
  108. ULONG_PTR
  109. (NTAPI *PGENERIC_HASH_ENUM_ROUTINE) (
  110. PGENERIC_HASH_TABLE Table,
  111. PVOID Buffer,
  112. PVOID Context
  113. );
  114. #define GHT_ACTION_NOOP 0
  115. #define GHT_ACTION_REMOVE 1
  116. #define GHT_ACTION_DELETE 2
  117. //
  118. // Status code defs. DON'T REORDER. ADD TO END ONLY.
  119. //
  120. typedef enum _GHT_STATUS {
  121. GHT_STATUS_SUCCESS = 0,
  122. GHT_STATUS_REMOVED,
  123. GHT_STATUS_LOCKCONFLICT,
  124. GHT_STATUS_LOCKTIMEOUT,
  125. GHT_STATUS_NOT_FOUND,
  126. GHT_STATUS_FAILURE
  127. } GHT_STATUS;
  128. //
  129. // Each entry that is placed in a hash table must start with a
  130. // GENERIC_HASH_ENTRY_HEADER. It is used to link the entries in a hash row,
  131. // holds the ULONG hash value for quick lookups and holds the reference
  132. // count on the entry.
  133. //
  134. typedef struct _GENERIC_HASH_ENTRY_HEADER {
  135. USHORT Type; // Type and size must match def in
  136. USHORT Size; // FRS_NODE_HEADER to use FrsAllocType().
  137. LIST_ENTRY ListEntry;
  138. ULONG HashValue;
  139. LONG ReferenceCount;
  140. } GENERIC_HASH_ENTRY_HEADER, *PGENERIC_HASH_ENTRY_HEADER;
  141. //
  142. // The GENERIC_HASH_ROW_ENTRY is the list head for each row in the table.
  143. // It has the lock for the row, a row count and some row access stats.
  144. //
  145. typedef struct _GENERIC_HASH_ROW_ENTRY {
  146. FRS_LIST HashRow;
  147. ULONG RowInserts;
  148. ULONG RowRemoves;
  149. ULONG RowCompares;
  150. ULONG RowLookups;
  151. ULONG RowLookupFails;
  152. } GENERIC_HASH_ROW_ENTRY, *PGENERIC_HASH_ROW_ENTRY;
  153. VOID
  154. GhtDestroyTable(
  155. PGENERIC_HASH_TABLE HashTable
  156. );
  157. ULONG
  158. GhtCleanTableByFilter(
  159. PGENERIC_HASH_TABLE HashTable,
  160. IN PGENERIC_HASH_ENUM_ROUTINE Function,
  161. PVOID Context
  162. );
  163. #if DBG
  164. #define GHT_DUMP_TABLE(_Sev_, _HashTable_) GhtDumpTable(_Sev_, _HashTable_)
  165. VOID
  166. GhtDumpTable(
  167. ULONG Severity,
  168. PGENERIC_HASH_TABLE HashTable
  169. );
  170. #else DBG
  171. #define GHT_DUMP_TABLE(_Sev_, _HashTable_)
  172. #endif DBG
  173. ULONG_PTR
  174. GhtEnumerateTable(
  175. IN PGENERIC_HASH_TABLE HashTable,
  176. IN PGENERIC_HASH_ENUM_ROUTINE Function,
  177. IN PVOID Context
  178. );
  179. ULONG_PTR
  180. GhtEnumerateTableNoRef(
  181. IN PGENERIC_HASH_TABLE HashTable,
  182. IN PGENERIC_HASH_ENUM_ROUTINE Function,
  183. IN PVOID Context
  184. );
  185. PGENERIC_HASH_ENTRY_HEADER
  186. GhtGetNextEntry(
  187. IN PGENERIC_HASH_TABLE HashTable,
  188. PGENERIC_HASH_ENTRY_HEADER HashEntry
  189. );
  190. ULONG
  191. GhtCountEntries(
  192. IN PGENERIC_HASH_TABLE HashTable
  193. );
  194. PGENERIC_HASH_ENTRY_HEADER
  195. GhtGetEntryNumber(
  196. IN PGENERIC_HASH_TABLE HashTable,
  197. IN LONG EntryNumber
  198. );
  199. PGENERIC_HASH_TABLE
  200. GhtCreateTable(
  201. PCHAR ArgName,
  202. ULONG NumberRows,
  203. ULONG KeyOffset,
  204. ULONG KeyLength,
  205. PGENERIC_HASH_FREE_ROUTINE GhtFree,
  206. PGENERIC_HASH_COMPARE_ROUTINE GhtCompare,
  207. PGENERIC_HASH_CALC_ROUTINE GhtHashCalc,
  208. PGENERIC_HASH_PRINT_ROUTINE GhtPrint
  209. );
  210. GHT_STATUS
  211. GhtLookup2(
  212. PGENERIC_HASH_TABLE HashTable,
  213. PVOID pKeyValue,
  214. BOOL WaitIfLocked,
  215. PVOID *RetHashEntry,
  216. ULONG DupIndex
  217. );
  218. //
  219. // If duplicates are present then Return the first one in the list.
  220. // This is the oldest duplicate based on insertion order. New Inserts always
  221. // go to the end of the duplicate list.
  222. //
  223. #define GhtLookup(_Table, _key, _wait, _retval) \
  224. GhtLookup2(_Table, _key, _wait, _retval, 1)
  225. //
  226. // If duplicates are present then return the last one in the list.
  227. // This is the most recent duplicate inserted.
  228. //
  229. #define GhtLookupNewest(_Table, _key, _wait, _retval) \
  230. GhtLookup2(_Table, _key, _wait, _retval, 0)
  231. GHT_STATUS
  232. GhtInsert(
  233. PGENERIC_HASH_TABLE HashTable,
  234. PVOID HashEntryArg,
  235. BOOL WaitIfLocked,
  236. BOOL DuplicatesOk
  237. );
  238. GHT_STATUS
  239. GhtDeleteEntryByAddress(
  240. PGENERIC_HASH_TABLE HashTable,
  241. PVOID HashEntryArg,
  242. BOOL WaitIfLocked
  243. );
  244. GHT_STATUS
  245. GhtRemoveEntryByAddress(
  246. PGENERIC_HASH_TABLE HashTable,
  247. PVOID HashEntryArg,
  248. BOOL WaitIfLocked
  249. );
  250. GHT_STATUS
  251. GhtReferenceEntryByAddress(
  252. PGENERIC_HASH_TABLE HashTable,
  253. PVOID HashEntryArg,
  254. BOOL WaitIfLocked
  255. );
  256. GHT_STATUS
  257. GhtDereferenceEntryByAddress(
  258. PGENERIC_HASH_TABLE HashTable,
  259. PVOID HashEntryArg,
  260. BOOL WaitIfLocked
  261. );
  262. GHT_STATUS
  263. GhtAdjustRefCountByKey(
  264. PGENERIC_HASH_TABLE HashTable,
  265. PVOID pKeyValue,
  266. LONG Delta,
  267. ULONG ActionIfZero,
  268. BOOL WaitIfLocked,
  269. PVOID *RetHashEntry
  270. );
  271. GHT_STATUS
  272. GhtSwapEntryByAddress(
  273. PGENERIC_HASH_TABLE HashTable,
  274. PVOID OldHashEntryArg,
  275. PVOID NewHashEntryArg,
  276. BOOL WaitIfLocked
  277. );
  278. /*
  279. The hash function returns a 32 bit ULONG used to index the table. kernrate
  280. It keeps stats on # acitve entries, ...
  281. Each hash row header element has an FRS_LIST, a count of lookups,
  282. deletes, collisions, ...
  283. Each hash entry (allocated by the caller for inserts) has a standard header.
  284. The GENERIC_HASH_ENTRY_HEADER has list_entry, ULONG HashValue, Ref Count.
  285. This is followed by user node specific data.
  286. PGENERIC_HASH_TABLE
  287. GhtCreateTable(
  288. PCHAR ArgName,
  289. ULONG NumberRows,
  290. ULONG KeyOffset,
  291. ULONG KeyLength,
  292. PGENERIC_HASH_FREE_ROUTINE GhtFree,
  293. PGENERIC_HASH_COMPARE_ROUTINE GhtCompare,
  294. PGENERIC_HASH_CALC_ROUTINE GhtHashCalc,
  295. PGENERIC_HASH_PRINT_ROUTINE GhtPrint
  296. )
  297. Create a hash table.
  298. VOID
  299. GhtDestroyTable(PGEN_HASH_TABLE)
  300. Free all the elements in the table and free the hash table structures.
  301. GHT_STATUS
  302. GhtInsert(PGEN_HASH_HEADER, WaitIfLocked)
  303. takes the tablectx, and a PGEN_HASH_HEADER. It calls the hash function with a
  304. ptr to the key data (entry+key offset) which returns a ULONG that is stored in
  305. GEN_HASH_HEADER.HashValue. Insert then calculates the index of HashValue Mod
  306. TableLenth. With the index it finds the hash row header and acquires the row
  307. lock (optional). It then walks the list looking for a hash value match. The
  308. entires are kept in ascending order so the lookup stops as soon as new entry
  309. value is < the list entry value. It then inserts the entry in the table,
  310. updates the counts in the row header and releases the lock and returns. If it
  311. finds a match it calls the user compare function with NewEntry+offset and
  312. ListEntry+offset to validate the match. The validate returns true if it matches
  313. and false if it fails (i.e. continue walking the list). Handling Duplicates
  314. ???? Insert returns GHT_STATUS_SUCCESS if the entry was inserted and
  315. GHT_STATUS_FAILURE if this is a duplicate node (the compare function returned
  316. TRUE). The refcount is incremented if the node was inserted and RefCounting is
  317. enabled. If the row is locked and WaitIfLocked is FALSE then return status
  318. GHT_STATUS_LOCKCONFLICT else wait on the row.
  319. GHT_STATUS
  320. GhtInsertAndLockRow(PGEN_HASH_HEADER, WaitIfLocked, &PGEN_HASH_ROW_HANDLE)
  321. Same as GhtInsert but leave the row locked if insert was successful.
  322. It returns a Row handle for unlock.
  323. GHT_STATUS
  324. GhtLookup(pKeyValue, WaitIfLocked, &PGEN_HASH_HEADER)
  325. like an insert but it takes a pointer to a key value as an argument
  326. along with the table ctx. The row index is computed, the row is optionally
  327. locked and the list is searched for the entry. In this case the validation
  328. routine is called with a ptr to the arg key value and the ListEntry+NodeKeyOffset.
  329. If the entry is found then a ptr to the entry is returned with an optional
  330. reference count incremented and status GHT_STATUS_SUCCESS. If the entry is
  331. not found return status GHT_STATUS_NOT_FOUND.
  332. If the row is locked and WaitIfLocked is TRUE then we wait on the row event.
  333. If the row is locked and WaitIfLocked is FALSE then return status
  334. GHT_STATUS_LOCKCONFLICT. In this case you can't tell if the entry is in
  335. the table.
  336. GHT_STATUS
  337. GHTLookupAndLockRow(pKeyValue, WaitIfLocked, &PGEN_HASH_HEADER, &PGEN_HASH_ROW_HANDLE)
  338. Do a lookup and leave row locked if entry found. Returns entry address
  339. if found or NULL if not found or row was locked and WaitIfLocked is FALSE.
  340. Return the RowHandle if the entry was found or NULL if the row was locked and
  341. WaitIfLocked is FALSE. Status returns:
  342. GHT_STATUS_SUCCESS found entry and row is locked.
  343. GHT_STATUS_LOCKCONFLICT row is locked, don't know status of entry
  344. GHT_STATUS_NOT_FOUND Entry is not in table.
  345. GHT_STATUS
  346. GhtDeleteEntryByKey(pKeyValue, WaitIfLocked, &PGEN_HASH_HEADER)
  347. Does a lookup and a delete entry. Locks the row after the lookup and unlocks
  348. the row after the delete. Returns a pointer to the entry or if a free memory
  349. routine is provided, it frees the entry. Return GHT_STATUS_NOT_FOUND if the
  350. entry is not in the table. Return GHT_STATUS_SUCCESS if the entry was deleted.
  351. Return GHT_STATUS_FAILURE if the ref count was not 1. The entry was not
  352. deleted. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  353. WaitIfLocked was FALSE.
  354. GHT_STATUS
  355. GhtDeleteEntryByAddress(PGEN_HASH_HEADER, WaitIfLocked)
  356. takes an entry address and fetches the hash value to acquire the row lock.
  357. Remove the entry from the row and call the memory free function to release
  358. the entries memory. Drop the row lock. Return GHT_STATUS_SUCCESS if we
  359. deleted the entry and the ref count was 1. Return GHT_STATUS_FAILURE if
  360. the ref count was not 1. The entry was not deleted.
  361. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  362. WaitIfLocked was FALSE.
  363. Note: This function is only safe if you have a reference on the entry otherwise
  364. another thread could have already deleted the entry.
  365. GHT_STATUS
  366. GhtRemoveEntryByAddress(
  367. PGENERIC_HASH_TABLE HashTable,
  368. PVOID HashEntryArg,
  369. BOOL WaitIfLocked
  370. )
  371. Takes HashEntry address and fetches the hash value to acquire the row lock.
  372. Remove the entry from the table. The reference count is decremented.
  373. If the ref count is > 2 when this call is made (1 for the caller and 1 for
  374. being in the table) then another thread may have a ref to the entry. If you
  375. move the entry to another hash table the caller should be sure that other threads
  376. with references can deal with the table change.
  377. GHT_STATUS
  378. GhtLockRowByKey(pKeyValue, WaitIfLocked, &PGEN_HASH_ROW_HANDLE)
  379. takes a key value and locks the row and returns the row handle for use by
  380. unlock. This routine does not do a lookup so it doesn't matter if the entry
  381. specified by the key is in the table or not. Return GHT_STATUS_SUCCESS if we
  382. got the lock. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  383. WaitIfLocked was FALSE.
  384. GHT_STATUS
  385. GhtLockRowByAddress(PGEN_HASH_HEADER, WaitIfLocked, &PGEN_HASH_ROW_HANDLE)
  386. takes an entry address and fetches the hash value to acquire the row lock.
  387. It returns the row handle for unlock. Return GHT_STATUS_SUCCESS if we
  388. got the lock. Return GHT_STATUS_LOCKCONFLICT if we failed to get the lock and
  389. WaitIfLocked was FALSE.
  390. Note: This function is only safe if you have a reference on the entry otherwise
  391. another thread could have already deleted the entry.
  392. GHT_STATUS
  393. GhtLockTable(WaitTime)
  394. Locks all the rows in the table and returns with GHT_STATUS_SUCCESS.
  395. Fails with GHT_STATUS_LOCKTIMEOUT if it takes > WaitTime millisec.
  396. This is useful if you need to snapshot the table.
  397. GHT_STATUS
  398. UnlockTable()
  399. Unlock all the rows in the table. Only do this if you locked 'em all.
  400. GHT_STATUS
  401. GhtUnLockRowByKey(pKeyValue)
  402. takes a key value, generates the row handle and unlocks the row.
  403. This routine does not do a lookup so it doesn't matter if the entry
  404. specified by the key is in the table or not. Return GHT_STATUS_SUCCESS if we
  405. released the lock.
  406. Raises an exception if the ROW lock was not held by this thread.
  407. GHT_STATUS
  408. GHTUnlockRowByAddress(PGEN_HASH_HEADER)
  409. takes an entry address and fetches the hash value to release the row lock and
  410. signal the event. Raises an exception if the ROW lock was not held by this thread.
  411. Note: This function is only safe if you have a reference on the entry otherwise
  412. another thread could have already deleted the entry making the saved hash
  413. value invalid.
  414. GHT_STATUS
  415. GHTUnlockRowByHandle(PGEN_HASH_ROW_HANDLE)
  416. takes a hashvalue as a row handle to release the lock (and signal the event).
  417. Raises an exception if the ROW lock was not held by this thread.
  418. GHT_STATUS
  419. GHTDecrementRefCount(PGEN_HASH_HEADER, FreeIfZero)
  420. take the entry address and use the hash value to get the row lock and decrement
  421. the ref count. If the refcount goes to zero remove the entry from the table.
  422. If FreeIfZero is TRUE then delete the entry. Return GHT_STATUS_REMOVED if the
  423. entry is removed from the table. Otherwise return GHT_STATUS_SUCCESS.
  424. Since the caller already has a ref count there is no possiblity of the entry
  425. having been deleted.
  426. GHT_STATUS
  427. GhtIncrementRefCount(PGEN_HASH_HEADER)
  428. take the entry address and use the hash value to get the row lock and increment
  429. the ref count. Return GHT_STATUS_SUCCESS. Since the caller already has a ref
  430. count (since they have an address) there is no possiblity of the entry having
  431. been deleted.
  432. GHT_STATUS
  433. GhtAdjustRefCountByKey(
  434. PGENERIC_HASH_TABLE HashTable,
  435. PVOID pKeyValue,
  436. LONG Delta,
  437. ULONG ActionIfZero,
  438. BOOL WaitIfLocked,
  439. PVOID *RetHashEntry
  440. )
  441. #define GHT_ACTION_NOOP 0
  442. #define GHT_ACTION_REMOVE 1
  443. #define GHT_ACTION_DELETE 2
  444. Use the keyValue to get the row lock and find the entry.
  445. Add delta to the ref count. If ref cnt is zero The value of ActionIfZero
  446. determines one of noop, remove entry, remove and delete entry.
  447. Return GHT_STATUS_NOT_FOUND if the entry is not found in the table.
  448. Otherwise return GHT_STATUS_SUCCESS. If the element is removed from the
  449. table the address is returned in RethashEntry.
  450. GHT_STATUS
  451. GhtSwapEntryByAddress(
  452. PGENERIC_HASH_TABLE HashTable,
  453. PVOID OldHashEntryArg,
  454. PVOID NewHashEntryArg,
  455. BOOL WaitIfLocked
  456. )
  457. This routine replaces an existing old hash entry with a new entry. It verifies
  458. tha the old hash entry is still in the table. It assumes that the key value of
  459. the new entry is the same as the old entry. NO CHECK IS MADE.
  460. The expected use is when the caller needs to reallocate an entry with
  461. more storage.
  462. NOTE ALSO: The reference count is copied from the old entry to the new one.
  463. Using this routine means that the caller is using GhtDecrementRefCountByKey()
  464. and GhtIncrementRefCountByKey() to access the ref counts on any element in the
  465. table since the entry could get swapped making the pointer invalid.
  466. Note: This function is only safe if you have a reference on the entry otherwise
  467. another thread could have already deleted the entry and your entry address is
  468. pointing at freed memory. A Lookup which gave you the address bumps the
  469. reference count. An insert in which you kept the address does NOT bump
  470. the reference count.
  471. GHT_STATUS
  472. GHTResizeTable()
  473. Acquire all the row locks, build a new row header array.
  474. walk thru the old table removing the elements and using the saved hash value
  475. to insert them into the new row header array. Update the ptr to the base
  476. of the new row header array and Free the old row header array. If the wait
  477. to acquire all the row locks is excessive (> 3 sec) then return GHT_STATUS_LOCKTIMEOUT
  478. to avoid deadlock.
  479. GHT_STATUS
  480. GhtEnumerateTable
  481. GhtDumpTable
  482. GhtEnumTableWithFunction
  483. walk the table and call the specified function for each entry.
  484. GhtGetTableStats
  485. Return status codes.
  486. GHT_STATUS_SUCCESS
  487. GHT_STATUS_REMOVED
  488. GHT_STATUS_LOCKCONFLICT
  489. GHT_STATUS_LOCKTIMEOUT
  490. GHT_STATUS_NOT_FOUND
  491. GHT_STATUS_FAILURE
  492. */
  493. #endif // _GENHASH_DEFINED_