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

1286 lines
35 KiB

  1. /*++
  2. Copyright (c) 1998-2001 Microsoft Corporation
  3. Module Name:
  4. hash.cxx
  5. Abstract:
  6. Contains the HTTP response cache hash table logic.
  7. Author:
  8. Alex Chen (alexch) 28-Mar-2001
  9. Revision History:
  10. George V. Reilly (GeorgeRe) 09-May-2001
  11. Cleaned up and tuned up
  12. --*/
  13. #include "precomp.h"
  14. #include "hashp.h"
  15. // Global Variables
  16. ULONG g_UlHashTableBits;
  17. ULONG g_UlHashTableSize;
  18. ULONG g_UlHashTableMask;
  19. ULONG g_UlHashIndexShift;
  20. //
  21. // Optimization: Use the space of (g_UlCacheLineSize - sizeof (HASHBUCKET))
  22. // to store a few records (Hash, pUriCacheEntry) such that we can scan the
  23. // records first before jumping to the single list for searching.
  24. //
  25. // g_UlNumOfHashUriKeys: The number of stored records in the space.
  26. //
  27. ULONG g_UlNumOfHashUriKeys;
  28. /***************************************************************************++
  29. Routine Description:
  30. This routine determine the hash table size according to
  31. (1) user-define value (reading from registry) or
  32. (2) system memory size estimation, if (1) is not defined
  33. Arguments:
  34. HashTableBits - The number of buckets is (1 << HashTableBits)
  35. --***************************************************************************/
  36. VOID
  37. UlpGetHashTableSize(
  38. IN LONG HashTableBits
  39. )
  40. {
  41. SYSTEM_BASIC_INFORMATION sbi;
  42. ULONG TotalPhysicalMemMB;
  43. NTSTATUS Status;
  44. Status = NtQuerySystemInformation(
  45. SystemBasicInformation,
  46. &sbi,
  47. sizeof(sbi),
  48. NULL);
  49. ASSERT(NT_SUCCESS(Status));
  50. // Capture total physical memory, in terms of megabytes
  51. TotalPhysicalMemMB = PAGES_TO_MEGABYTES(sbi.NumberOfPhysicalPages);
  52. //
  53. // HashTableBits is equal to DEFAULT_HASH_TABLE_BITS
  54. // if it is not defined in the registry
  55. //
  56. if (HashTableBits != DEFAULT_HASH_TABLE_BITS)
  57. {
  58. // Use the registry value
  59. // BUGBUG: We must check for reasonable values, so that a
  60. // malicious or careless user doesn't cause us to eat up
  61. // all of (Non)PagedPool.
  62. g_UlHashTableBits = HashTableBits;
  63. }
  64. else
  65. {
  66. //
  67. // Registry value REGISTRY_HASH_TABLE_BITS is not defined,
  68. // use system memory size estimation instead
  69. //
  70. MM_SYSTEMSIZE SystemSize = MmQuerySystemSize();
  71. if (SystemSize == MmSmallSystem)
  72. {
  73. // Hash Table Size: 4K buckets
  74. g_UlHashTableBits = 12;
  75. }
  76. else if (SystemSize == MmMediumSystem)
  77. {
  78. // Hash Table Size: 16K buckets
  79. g_UlHashTableBits = 14;
  80. }
  81. else
  82. {
  83. // Hash Table Size: 64K buckets
  84. // BUGBUG: A 64KB server is considered an MmLargeSystem.
  85. // Tune g_UlHashTableBits according to physical memory
  86. g_UlHashTableBits = 16;
  87. }
  88. }
  89. g_UlHashIndexShift = g_UlCacheLineBits;
  90. #ifdef HASH_TEST
  91. g_UlHashTableBits = 3;
  92. #endif
  93. g_UlHashTableSize = (1 << g_UlHashTableBits);
  94. g_UlHashTableMask = g_UlHashTableSize - 1;
  95. } // UlpGetHashTableSize
  96. /***************************************************************************++
  97. Routine Description:
  98. Validates that a locked HASHBUCKET is `compact'. If there are less than
  99. g_UlNumOfHashUriKeys entries in a bucket, they are clumped together at
  100. the beginning of the records array, and all the empty slots are at the
  101. end. All empty slots must have Hash == HASH_INVALID_SIGNATURE and
  102. pUriCacheEntry == NULL. Conversely, all the non-empty slots at the
  103. beginning of the array must have point to valid UL_URI_CACHE_ENTRYs
  104. and must have Hash == correct hash signature, which cannot be
  105. HASH_INVALID_SIGNATURE. If the single list pointer is non-NULL, then
  106. the records array must be full.
  107. If the HASHBUCKET is compact, then we can abort a search for a key as
  108. soon as we see HASH_INVALID_SIGNATURE. This invariant speeds up Find and
  109. Insert at the cost of making Delete and Flush a little more
  110. complex. Since we expect to do a lot more Finds than Deletes or Inserts,
  111. this is an acceptable tradeoff.
  112. Storing hash signatures means that we have a very fast test that
  113. eliminates almost all false positives. We very seldom find two keys
  114. that have matching hash signatures, but different strings.
  115. Arguments:
  116. pBucket - The hash bucket
  117. --***************************************************************************/
  118. BOOLEAN
  119. UlpHashBucketIsCompact(
  120. IN const PHASHBUCKET pBucket)
  121. {
  122. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  123. PUL_URI_CACHE_ENTRY pPrevUriCacheEntry = NULL;
  124. PHASHURIKEY pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  125. ULONG i, j, Entries = 0;
  126. // First, validate the records array
  127. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  128. {
  129. ULONG Hash = pHashUriKey[i].Hash;
  130. if (HASH_INVALID_SIGNATURE == Hash)
  131. {
  132. // There are no more valid entries in the records array
  133. // and no singly linked list
  134. ASSERT(NULL == pBucket->pUriCacheEntry);
  135. pPrevUriCacheEntry = NULL;
  136. for (j = i; j < g_UlNumOfHashUriKeys; j++)
  137. {
  138. ASSERT(NULL == pHashUriKey[j].pUriCacheEntry);
  139. ASSERT(HASH_INVALID_SIGNATURE == pHashUriKey[j].Hash);
  140. }
  141. }
  142. else
  143. {
  144. // non-empty slot
  145. ++Entries;
  146. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  147. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry));
  148. ASSERT(pUriCacheEntry->Cached);
  149. ASSERT(Hash == pUriCacheEntry->UriKey.Hash);
  150. ASSERT(Hash == HashRandomizeBits(
  151. HashStringNoCaseW(
  152. pUriCacheEntry->UriKey.pUri,
  153. 0
  154. )));
  155. ASSERT(pPrevUriCacheEntry != pUriCacheEntry);
  156. pPrevUriCacheEntry = pUriCacheEntry;
  157. }
  158. }
  159. // Next, validate the singly linked list
  160. for (pUriCacheEntry = pBucket->pUriCacheEntry;
  161. NULL != pUriCacheEntry;
  162. pUriCacheEntry
  163. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next)
  164. {
  165. ++Entries;
  166. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry));
  167. ASSERT(pUriCacheEntry->Cached);
  168. ASSERT(pUriCacheEntry->UriKey.Hash
  169. == HashRandomizeBits(
  170. HashStringNoCaseW(
  171. pUriCacheEntry->UriKey.pUri,
  172. 0
  173. )));
  174. ASSERT(pPrevUriCacheEntry != pUriCacheEntry);
  175. pPrevUriCacheEntry = pUriCacheEntry;
  176. }
  177. ASSERT(Entries == pBucket->Entries);
  178. return TRUE;
  179. } // UlpHashBucketIsCompact
  180. /***************************************************************************++
  181. Routine Description:
  182. This routine initialize the hash table
  183. Arguments:
  184. pHashTable - The hash table
  185. PoolType - Specifies the type of pool memory to allocate
  186. HashTableBits - The number of buckets is (1 << HashTableBits)
  187. Returns:
  188. NTSTATUS - STATUS_SUCCESS or STATUS_NO_MEMORY
  189. --***************************************************************************/
  190. NTSTATUS
  191. UlInitializeHashTable(
  192. IN OUT PHASHTABLE pHashTable,
  193. IN POOL_TYPE PoolType,
  194. IN LONG HashTableBits
  195. )
  196. {
  197. ULONG i;
  198. ULONG_PTR CacheLineMask, CacheLineSize = g_UlCacheLineSize;
  199. //
  200. // First, get the hash table size from the registry.
  201. // If not defined in the registry, determine the hash table
  202. // size by the system memory size
  203. //
  204. UlpGetHashTableSize(HashTableBits);
  205. #ifdef HASH_TEST
  206. CacheLineSize = 64;
  207. g_UlHashIndexShift = 6;
  208. #endif
  209. CacheLineMask = CacheLineSize - 1;
  210. ASSERT((CacheLineSize & CacheLineMask) == 0); // power of 2
  211. ASSERT(CacheLineSize == (1U << g_UlHashIndexShift));
  212. pHashTable->Signature = UL_HASH_TABLE_POOL_TAG;
  213. pHashTable->PoolType = PoolType;
  214. pHashTable->NumberOfBytes = g_UlHashTableSize * CacheLineSize;
  215. ASSERT(CacheLineSize > sizeof(HASHBUCKET));
  216. // number of keys stored in the initial clump
  217. g_UlNumOfHashUriKeys = (((ULONG) CacheLineSize - sizeof (HASHBUCKET))
  218. / sizeof(HASHURIKEY));
  219. pHashTable->pBuckets = NULL;
  220. #ifdef HASH_TEST
  221. g_UlNumOfHashUriKeys = 3;
  222. #endif
  223. ASSERT((sizeof(HASHBUCKET) + g_UlNumOfHashUriKeys * sizeof(HASHURIKEY))
  224. <= (1U << g_UlHashIndexShift));
  225. // Allocate the memory
  226. pHashTable->pAllocMem
  227. = (PHASHBUCKET) UL_ALLOCATE_POOL(
  228. PoolType,
  229. pHashTable->NumberOfBytes + CacheLineMask,
  230. UL_HASH_TABLE_POOL_TAG
  231. );
  232. if (NULL == pHashTable->pAllocMem)
  233. {
  234. pHashTable->Signature = MAKE_FREE_TAG(UL_HASH_TABLE_POOL_TAG);
  235. return STATUS_NO_MEMORY;
  236. }
  237. // Align the memory the cache line size boundary
  238. pHashTable->pBuckets
  239. = (PHASHBUCKET)((((ULONG_PTR)(pHashTable->pAllocMem)) + CacheLineMask)
  240. & ~CacheLineMask);
  241. // Initialize each bucket and padding array elements
  242. for (i=0; i < g_UlHashTableSize; i++)
  243. {
  244. PHASHBUCKET pBucket = UlpHashTableIndexedBucket(pHashTable, i);
  245. PHASHURIKEY pHashUriKey;
  246. ULONG j;
  247. UlInitializeRWSpinLock(&pBucket->RWSpinLock);
  248. pBucket->pUriCacheEntry = NULL;
  249. pBucket->Entries = 0;
  250. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  251. for (j = 0; j < g_UlNumOfHashUriKeys ;j++)
  252. {
  253. pHashUriKey[j].Hash = HASH_INVALID_SIGNATURE;
  254. pHashUriKey[j].pUriCacheEntry = NULL;
  255. }
  256. ASSERT(UlpHashBucketIsCompact(pBucket));
  257. }
  258. return STATUS_SUCCESS;
  259. } // UlInitializeHashTable
  260. /***************************************************************************++
  261. Routine Description:
  262. This routine terminates the hash table
  263. (flush all entries and free the table).
  264. Arguments:
  265. pHashTable - The hash table
  266. --***************************************************************************/
  267. VOID
  268. UlTerminateHashTable(
  269. IN PHASHTABLE pHashTable
  270. )
  271. {
  272. if ( pHashTable->pAllocMem != NULL )
  273. {
  274. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  275. // Clear the hash table (delete all entries)
  276. UlClearHashTable(pHashTable);
  277. // Free the hash table buckets
  278. UL_FREE_POOL(pHashTable->pAllocMem, UL_HASH_TABLE_POOL_TAG);
  279. pHashTable->Signature = MAKE_FREE_TAG(UL_HASH_TABLE_POOL_TAG);
  280. pHashTable->pAllocMem = pHashTable->pBuckets = NULL;
  281. }
  282. } // UlTerminateHashTable
  283. /***************************************************************************++
  284. Routine Description:
  285. This routine does a cache lookup on a hash table
  286. to see if there is a valid entry corresponding to the request key.
  287. Increment the reference counter of the entry by 1 inside the lock
  288. protection to ensure this entry will be still alive after returning
  289. this entry back.
  290. Arguments:
  291. pHashTable - The hash table
  292. pUriKey - the search key
  293. Returns:
  294. PUL_URI_CACHE_ENTRY - pointer to entry or NULL
  295. --***************************************************************************/
  296. PUL_URI_CACHE_ENTRY
  297. UlGetFromHashTable(
  298. IN PHASHTABLE pHashTable,
  299. IN PURI_KEY pUriKey
  300. )
  301. {
  302. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  303. PHASHBUCKET pBucket;
  304. PHASHURIKEY pHashUriKey;
  305. ULONG i;
  306. HASH_PAGED_CODE(pHashTable);
  307. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  308. pBucket = UlpHashTableBucketFromUriKey(pHashTable, pUriKey);
  309. UlAcquireRWSpinLockShared(&pBucket->RWSpinLock);
  310. ASSERT(UlpHashBucketIsCompact(pBucket));
  311. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  312. // Scan the records array first
  313. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  314. {
  315. ULONG Hash = pHashUriKey[i].Hash;
  316. if (HASH_INVALID_SIGNATURE == Hash)
  317. {
  318. // There are no more valid entries in the bucket
  319. ASSERT(NULL == pBucket->pUriCacheEntry);
  320. ASSERT(NULL == pHashUriKey[i].pUriCacheEntry);
  321. pUriCacheEntry = NULL;
  322. goto unlock;
  323. }
  324. if (Hash == pUriKey->Hash)
  325. {
  326. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  327. ASSERT(NULL != pUriCacheEntry);
  328. if (UlpEqualUriKeys(&pUriCacheEntry->UriKey, pUriKey))
  329. {
  330. goto addref;
  331. }
  332. }
  333. }
  334. ASSERT(i == g_UlNumOfHashUriKeys);
  335. // Jump to the single list for searching
  336. for (pUriCacheEntry = pBucket->pUriCacheEntry;
  337. NULL != pUriCacheEntry;
  338. pUriCacheEntry
  339. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next)
  340. {
  341. if (pUriCacheEntry->UriKey.Hash == pUriKey->Hash
  342. && UlpEqualUriKeys(&pUriCacheEntry->UriKey, pUriKey))
  343. {
  344. goto addref;
  345. }
  346. }
  347. // Not found
  348. ASSERT(NULL == pUriCacheEntry);
  349. goto unlock;
  350. addref:
  351. ASSERT(NULL != pUriCacheEntry);
  352. REFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, CHECKOUT);
  353. unlock:
  354. ASSERT(UlpHashBucketIsCompact(pBucket));
  355. UlReleaseRWSpinLockShared(&pBucket->RWSpinLock);
  356. return pUriCacheEntry;
  357. } // UlGetFromHashTable
  358. /***************************************************************************++
  359. Routine Description:
  360. This routine does a cache lookup on a hash table
  361. to see if there is a valid entry corresponding to the request URI,
  362. if found, delete this entry. However, increment the reference counter
  363. of the entry by 1 insde the lock protection to ensure this entry will be
  364. still alive after returning this entry back.
  365. Arguments:
  366. pHashTable - The hash table
  367. pUriKey - the search key
  368. Returns:
  369. PUL_URI_CACHE_ENTRY - pointer to entry removed from table or NULL
  370. --***************************************************************************/
  371. PUL_URI_CACHE_ENTRY
  372. UlDeleteFromHashTable(
  373. IN PHASHTABLE pHashTable,
  374. IN PURI_KEY pUriKey
  375. )
  376. {
  377. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  378. PUL_URI_CACHE_ENTRY PrevUriCacheEntry;
  379. PHASHBUCKET pBucket;
  380. PHASHURIKEY pHashUriKey;
  381. ULONG i;
  382. HASH_PAGED_CODE(pHashTable);
  383. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  384. pBucket = UlpHashTableBucketFromUriKey(pHashTable, pUriKey);
  385. UlAcquireRWSpinLockExclusive(&pBucket->RWSpinLock);
  386. ASSERT(UlpHashBucketIsCompact(pBucket));
  387. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  388. // Scan the records array first
  389. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  390. {
  391. ULONG Hash = pHashUriKey[i].Hash;
  392. if (HASH_INVALID_SIGNATURE == Hash)
  393. {
  394. ASSERT(NULL == pBucket->pUriCacheEntry);
  395. ASSERT(NULL == pHashUriKey[i].pUriCacheEntry);
  396. pUriCacheEntry = NULL;
  397. goto unlock;
  398. }
  399. if (Hash == pUriKey->Hash)
  400. {
  401. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  402. ASSERT(NULL != pUriCacheEntry);
  403. if (UlpEqualUriKeys(&pUriCacheEntry->UriKey, pUriKey))
  404. {
  405. --pBucket->Entries;
  406. if (pBucket->pUriCacheEntry)
  407. {
  408. // If there exists an entry in the single list,
  409. // move it to the array
  410. pHashUriKey[i].Hash
  411. = pBucket->pUriCacheEntry->UriKey.Hash;
  412. pHashUriKey[i].pUriCacheEntry = pBucket->pUriCacheEntry;
  413. pBucket->pUriCacheEntry
  414. = (PUL_URI_CACHE_ENTRY)
  415. pBucket->pUriCacheEntry->BucketEntry.Next;
  416. }
  417. else
  418. {
  419. // if this is not the last entry in the records array,
  420. // move the last entry to this slot
  421. ULONG j;
  422. for (j = g_UlNumOfHashUriKeys; --j >= i; )
  423. {
  424. if (NULL != pHashUriKey[j].pUriCacheEntry)
  425. {
  426. ASSERT(HASH_INVALID_SIGNATURE
  427. != pHashUriKey[j].Hash);
  428. ASSERT(j >= i);
  429. pHashUriKey[i].Hash = pHashUriKey[j].Hash;
  430. pHashUriKey[i].pUriCacheEntry
  431. = pHashUriKey[j].pUriCacheEntry;
  432. // Zap the last entry. Correct even if j == i
  433. pHashUriKey[j].Hash = HASH_INVALID_SIGNATURE;
  434. pHashUriKey[j].pUriCacheEntry = NULL;
  435. goto unlock;
  436. }
  437. else
  438. {
  439. ASSERT(HASH_INVALID_SIGNATURE
  440. == pHashUriKey[j].Hash);
  441. }
  442. }
  443. // We can't get here, since pHashUriKey[i] should
  444. // have terminated the loop even if there wasn't
  445. // any non-empty slot following it.
  446. ASSERT(! "Overshot the deleted entry");
  447. }
  448. goto unlock;
  449. }
  450. }
  451. }
  452. ASSERT(i == g_UlNumOfHashUriKeys);
  453. // Jump to the single list for searching
  454. pUriCacheEntry = pBucket->pUriCacheEntry;
  455. PrevUriCacheEntry = NULL;
  456. while (NULL != pUriCacheEntry)
  457. {
  458. if (pUriCacheEntry->UriKey.Hash == pUriKey->Hash
  459. && UlpEqualUriKeys(&pUriCacheEntry->UriKey, pUriKey))
  460. {
  461. if (PrevUriCacheEntry == NULL)
  462. {
  463. // Delete First Entry
  464. pBucket->pUriCacheEntry
  465. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next;
  466. }
  467. else
  468. {
  469. PrevUriCacheEntry->BucketEntry.Next
  470. = pUriCacheEntry->BucketEntry.Next;
  471. }
  472. --pBucket->Entries;
  473. goto unlock;
  474. }
  475. PrevUriCacheEntry = pUriCacheEntry;
  476. pUriCacheEntry
  477. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next;
  478. }
  479. // Not found
  480. ASSERT(NULL == pUriCacheEntry);
  481. unlock:
  482. ASSERT((LONG) pBucket->Entries >= 0);
  483. ASSERT(UlpHashBucketIsCompact(pBucket));
  484. UlReleaseRWSpinLockExclusive(&pBucket->RWSpinLock);
  485. return pUriCacheEntry;
  486. } // UlDeleteFromHashTable
  487. /***************************************************************************++
  488. Routine Description:
  489. This routine does a cache lookup on a hash table
  490. to see if a given entry exists, if not found, add this entry to the
  491. hash table. Increment the reference counter of the entry by 1 insde the
  492. lock protection.
  493. Arguments:
  494. pHashTable - The hash table
  495. pUriCacheEntry - the given entry
  496. Returns
  497. ULC_RETCODE - ULC_SUCCESS or ULC_KEY_EXISTS
  498. --***************************************************************************/
  499. ULC_RETCODE
  500. UlAddToHashTable(
  501. IN PHASHTABLE pHashTable,
  502. IN PUL_URI_CACHE_ENTRY pUriCacheEntry
  503. )
  504. {
  505. PUL_URI_CACHE_ENTRY pTmpUriCacheEntry;
  506. PURI_KEY pUriKey = &(pUriCacheEntry->UriKey);
  507. PHASHBUCKET pBucket;
  508. PHASHURIKEY pHashUriKey;
  509. LONG EmptySlot = INVALID_SLOT_INDEX;
  510. ULONG i;
  511. ULC_RETCODE rc;
  512. HASH_PAGED_CODE(pHashTable);
  513. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  514. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry));
  515. ASSERT(pUriCacheEntry->Cached);
  516. pBucket = UlpHashTableBucketFromUriKey(pHashTable, pUriKey);
  517. UlAcquireRWSpinLockExclusive(&pBucket->RWSpinLock);
  518. ASSERT(UlpHashBucketIsCompact(pBucket));
  519. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  520. // Scan the records array first
  521. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  522. {
  523. ULONG Hash = pHashUriKey[i].Hash;
  524. if (HASH_INVALID_SIGNATURE == Hash)
  525. {
  526. ASSERT(NULL == pBucket->pUriCacheEntry);
  527. ASSERT(NULL == pHashUriKey[i].pUriCacheEntry);
  528. EmptySlot = (LONG) i;
  529. goto insert;
  530. }
  531. if (Hash == pUriKey->Hash)
  532. {
  533. pTmpUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  534. ASSERT(NULL != pTmpUriCacheEntry);
  535. if (UlpEqualUriKeys(&pTmpUriCacheEntry->UriKey, pUriKey))
  536. {
  537. // duplicate key exists
  538. pUriCacheEntry->Cached = FALSE;
  539. rc = ULC_KEY_EXISTS;
  540. goto unlock;
  541. }
  542. }
  543. }
  544. ASSERT(i == g_UlNumOfHashUriKeys);
  545. ASSERT(EmptySlot == INVALID_SLOT_INDEX);
  546. // Jump to the single list for searching
  547. for (pTmpUriCacheEntry = pBucket->pUriCacheEntry;
  548. NULL != pTmpUriCacheEntry;
  549. pTmpUriCacheEntry
  550. = (PUL_URI_CACHE_ENTRY) pTmpUriCacheEntry->BucketEntry.Next)
  551. {
  552. if (pTmpUriCacheEntry->UriKey.Hash == pUriKey->Hash
  553. && UlpEqualUriKeys(&pTmpUriCacheEntry->UriKey, pUriKey))
  554. {
  555. // duplicate key exists
  556. pUriCacheEntry->Cached = FALSE;
  557. rc = ULC_KEY_EXISTS;
  558. goto unlock;
  559. }
  560. }
  561. insert:
  562. //
  563. // Not found, no duplicate key in hash table
  564. //
  565. if (EmptySlot != INVALID_SLOT_INDEX)
  566. {
  567. ASSERT(0 <= EmptySlot && EmptySlot < (LONG) g_UlNumOfHashUriKeys);
  568. // First, try to add this entry to the array if there is an empty slot.
  569. pHashUriKey[EmptySlot].Hash = pUriKey->Hash;
  570. pHashUriKey[EmptySlot].pUriCacheEntry = pUriCacheEntry;
  571. }
  572. else
  573. {
  574. // Otherwise, add this entry to the head of the single list
  575. pUriCacheEntry->BucketEntry.Next
  576. = (PSINGLE_LIST_ENTRY) pBucket->pUriCacheEntry;
  577. pBucket->pUriCacheEntry = pUriCacheEntry;
  578. }
  579. REFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, ADD);
  580. ASSERT((LONG) pBucket->Entries >= 0);
  581. ++pBucket->Entries;
  582. rc = ULC_SUCCESS;
  583. unlock:
  584. ASSERT(UlpHashBucketIsCompact(pBucket));
  585. UlReleaseRWSpinLockExclusive(&pBucket->RWSpinLock);
  586. return rc;
  587. } // UlAddToHashTable
  588. /***************************************************************************++
  589. Routine Description:
  590. Removes entries based on a caller-specified filter. The caller
  591. provides a predicate function which takes a cache entry as a
  592. parameter. The function will be called for each item in the cache.
  593. If the function returns ULC_DELETE, the item will be removed.
  594. Otherwise the item will remain in the cache.
  595. All removals are done on a hash table bucket.
  596. Walk through all the entries under this bucket.
  597. Assume bucket exclusive lock is held.
  598. Arguments:
  599. pBucket - The hash table bucket
  600. pFilterRoutine - A pointer to the filter function
  601. pContext - A parameter to the filter function
  602. pDeletedCount - A pointer to the number of deleted entries on this bucket
  603. bStop - A pointer to a boolean variable returned to caller
  604. (TRUE if the filter function asks for action stop)
  605. --***************************************************************************/
  606. BOOLEAN
  607. UlpFilterFlushHashBucket(
  608. IN PHASHBUCKET pBucket,
  609. IN PUL_URI_FILTER pFilterRoutine,
  610. IN PVOID pContext,
  611. OUT PULONG pDeletedCount
  612. )
  613. {
  614. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  615. PUL_URI_CACHE_ENTRY pPrevUriCacheEntry;
  616. PUL_URI_CACHE_ENTRY pTmpUriCacheEntry;
  617. UL_CACHE_PREDICATE result;
  618. LONG ReferenceCount;
  619. PHASHURIKEY pHashUriKey;
  620. ULONG i;
  621. LONG LastSlot;
  622. BOOLEAN bStop = FALSE;
  623. // Check if bucket exclusive lock is held
  624. ASSERT( UlIsLockedExclusive(&pBucket->RWSpinLock) );
  625. ASSERT(UlpHashBucketIsCompact(pBucket));
  626. // Scan the single list first
  627. pUriCacheEntry = pBucket->pUriCacheEntry;
  628. pPrevUriCacheEntry = NULL;
  629. while (NULL != pUriCacheEntry)
  630. {
  631. BOOLEAN bDelete = FALSE;
  632. result = (*pFilterRoutine)(pUriCacheEntry, pContext);
  633. switch (result)
  634. {
  635. case ULC_ABORT:
  636. bStop = TRUE;
  637. goto end;
  638. case ULC_NO_ACTION:
  639. // nothing to do
  640. break;
  641. case ULC_PERFORM:
  642. case ULC_PERFORM_STOP:
  643. case ULC_DELETE:
  644. case ULC_DELETE_STOP:
  645. {
  646. // Delete this entry
  647. bDelete = TRUE;
  648. ASSERT(pBucket->Entries > 0);
  649. --pBucket->Entries;
  650. pTmpUriCacheEntry = pUriCacheEntry;
  651. if (NULL == pPrevUriCacheEntry)
  652. {
  653. // Delete First Entry
  654. pBucket->pUriCacheEntry
  655. = (PUL_URI_CACHE_ENTRY)
  656. pUriCacheEntry->BucketEntry.Next;
  657. pUriCacheEntry = pBucket->pUriCacheEntry;
  658. }
  659. else
  660. {
  661. pPrevUriCacheEntry->BucketEntry.Next
  662. = pUriCacheEntry->BucketEntry.Next;
  663. pUriCacheEntry
  664. = (PUL_URI_CACHE_ENTRY)
  665. pPrevUriCacheEntry->BucketEntry.Next;
  666. }
  667. ASSERT(UlpHashBucketIsCompact(pBucket));
  668. DEREFERENCE_URI_CACHE_ENTRY(pTmpUriCacheEntry, FILTER);
  669. ++(*pDeletedCount);
  670. if (result == ULC_PERFORM_STOP || result == ULC_DELETE_STOP)
  671. {
  672. bStop = TRUE;
  673. goto end;
  674. }
  675. break;
  676. }
  677. default:
  678. break;
  679. }
  680. if (!bDelete)
  681. {
  682. pPrevUriCacheEntry = pUriCacheEntry;
  683. pUriCacheEntry
  684. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next;
  685. }
  686. }
  687. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  688. //
  689. // Now, scan the records array.
  690. //
  691. // Because we keep the records array compact, we need to keep
  692. // track of the last valid slot, so that we can move its contents
  693. // to the slot that's being deleted.
  694. //
  695. LastSlot = INVALID_SLOT_INDEX;
  696. if (NULL == pBucket->pUriCacheEntry)
  697. {
  698. for (i = g_UlNumOfHashUriKeys; i-- > 0; )
  699. {
  700. if (NULL != pHashUriKey[i].pUriCacheEntry)
  701. {
  702. ASSERT(HASH_INVALID_SIGNATURE != pHashUriKey[i].Hash);
  703. LastSlot = (LONG) i;
  704. break;
  705. }
  706. else
  707. {
  708. ASSERT(HASH_INVALID_SIGNATURE == pHashUriKey[i].Hash);
  709. }
  710. }
  711. // Is records array completely empty?
  712. if (LastSlot == INVALID_SLOT_INDEX)
  713. goto end;
  714. }
  715. else
  716. {
  717. // final slot cannot be empty
  718. ASSERT(HASH_INVALID_SIGNATURE
  719. != pHashUriKey[g_UlNumOfHashUriKeys-1].Hash);
  720. }
  721. // Walk through the records array
  722. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  723. {
  724. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  725. if (NULL == pUriCacheEntry)
  726. {
  727. ASSERT(HASH_INVALID_SIGNATURE == pHashUriKey[i].Hash);
  728. goto end;
  729. }
  730. else
  731. {
  732. ASSERT(HASH_INVALID_SIGNATURE != pHashUriKey[i].Hash);
  733. }
  734. result = (*pFilterRoutine)(pUriCacheEntry, pContext);
  735. switch (result)
  736. {
  737. case ULC_ABORT:
  738. bStop = TRUE;
  739. goto end;
  740. case ULC_NO_ACTION:
  741. // nothing to do
  742. break;
  743. case ULC_PERFORM:
  744. case ULC_PERFORM_STOP:
  745. case ULC_DELETE:
  746. case ULC_DELETE_STOP:
  747. {
  748. // Delete this entry
  749. ASSERT(pBucket->Entries > 0);
  750. --pBucket->Entries;
  751. if (NULL != pBucket->pUriCacheEntry)
  752. {
  753. // If there exists an entry in the single list,
  754. // move it to the array
  755. ASSERT(LastSlot == INVALID_SLOT_INDEX);
  756. pHashUriKey[i].Hash
  757. = pBucket->pUriCacheEntry->UriKey.Hash;
  758. pHashUriKey[i].pUriCacheEntry = pBucket->pUriCacheEntry;
  759. pBucket->pUriCacheEntry
  760. = (PUL_URI_CACHE_ENTRY)
  761. pBucket->pUriCacheEntry->BucketEntry.Next;
  762. if (NULL == pBucket->pUriCacheEntry)
  763. {
  764. LastSlot = g_UlNumOfHashUriKeys - 1;
  765. ASSERT(HASH_INVALID_SIGNATURE
  766. != pHashUriKey[LastSlot].Hash);
  767. }
  768. }
  769. else
  770. {
  771. // Move the entry in the last slot to this position,
  772. // zap the last slot, and move LastSlot backwards
  773. if (LastSlot != INVALID_SLOT_INDEX
  774. && (LONG) i < LastSlot)
  775. {
  776. ASSERT(HASH_INVALID_SIGNATURE
  777. != pHashUriKey[LastSlot].Hash);
  778. pHashUriKey[i].Hash = pHashUriKey[LastSlot].Hash;
  779. pHashUriKey[i].pUriCacheEntry
  780. = pHashUriKey[LastSlot].pUriCacheEntry;
  781. pHashUriKey[LastSlot].Hash = HASH_INVALID_SIGNATURE;
  782. pHashUriKey[LastSlot].pUriCacheEntry = NULL;
  783. if (--LastSlot == i)
  784. LastSlot = INVALID_SLOT_INDEX;
  785. else
  786. ASSERT(HASH_INVALID_SIGNATURE
  787. != pHashUriKey[LastSlot].Hash);
  788. }
  789. else
  790. {
  791. // Just reset this array element
  792. pHashUriKey[i].Hash = HASH_INVALID_SIGNATURE;
  793. pHashUriKey[i].pUriCacheEntry = NULL;
  794. LastSlot = INVALID_SLOT_INDEX;
  795. }
  796. }
  797. ASSERT(UlpHashBucketIsCompact(pBucket));
  798. DEREFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, FILTER);
  799. ++(*pDeletedCount);
  800. if (result == ULC_PERFORM_STOP || result == ULC_DELETE_STOP)
  801. {
  802. bStop = TRUE;
  803. goto end;
  804. }
  805. break;
  806. }
  807. default:
  808. break;
  809. }
  810. }
  811. end:
  812. ASSERT(UlpHashBucketIsCompact(pBucket));
  813. return bStop;
  814. } // UlpFilterFlushHashBucket
  815. /***************************************************************************++
  816. Routine Description:
  817. Removes entries based on a caller-specified filter. The caller
  818. provides a predicate function which takes a cache entry as a
  819. parameter. The function will be called with each item in the cache.
  820. If the function returns ULC_DELETE, the item will be removed.
  821. Otherwise the item will remain in the cache.
  822. Arguments:
  823. pHashTable - The hash table
  824. pFilterRoutine - A pointer to the filter function
  825. pContext - A parameter to the filter function
  826. pDeletedCount - A pointer to the number of deleted entries
  827. Returns:
  828. ULONG - Number of entries flushed from the table
  829. --***************************************************************************/
  830. ULONG
  831. UlFilterFlushHashTable(
  832. IN PHASHTABLE pHashTable,
  833. IN PUL_URI_FILTER pFilterRoutine,
  834. IN PVOID pContext
  835. )
  836. {
  837. ULONG i;
  838. BOOLEAN bStop = FALSE;
  839. ULONG DeletedCount = 0;
  840. HASH_PAGED_CODE(pHashTable);
  841. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  842. //
  843. // Scan and delete (if matching the filter) each bucket
  844. // of the cache table.
  845. //
  846. for (i = 0; !bStop && i < g_UlHashTableSize; i++)
  847. {
  848. PHASHBUCKET pBucket = UlpHashTableIndexedBucket(pHashTable, i);
  849. ULONG DeletedInBucket = 0;
  850. UlAcquireRWSpinLockExclusive(&pBucket->RWSpinLock);
  851. bStop = UlpFilterFlushHashBucket(
  852. pBucket,
  853. pFilterRoutine,
  854. pContext,
  855. &DeletedInBucket
  856. );
  857. UlReleaseRWSpinLockExclusive(&pBucket->RWSpinLock);
  858. DeletedCount += DeletedInBucket;
  859. }
  860. return DeletedCount;
  861. } // UlFilterFlushHashTable
  862. /***************************************************************************++
  863. Routine Description:
  864. Removes all entries on a bucket.
  865. Assume bucket exclusive lock is held.
  866. Arguments:
  867. pBucket - The hash table bucket
  868. --***************************************************************************/
  869. VOID
  870. UlpClearHashBucket(
  871. IN PHASHBUCKET pBucket
  872. )
  873. {
  874. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  875. PUL_URI_CACHE_ENTRY pTmpUriCacheEntry;
  876. PHASHURIKEY pHashUriKey;
  877. ULONG i;
  878. LONG ReferenceCount;
  879. // Check if bucket exclusive lock is held
  880. ASSERT( UlIsLockedExclusive(&pBucket->RWSpinLock) );
  881. ASSERT(UlpHashBucketIsCompact(pBucket));
  882. // Scan the single list first
  883. pUriCacheEntry = pBucket->pUriCacheEntry;
  884. while (NULL != pUriCacheEntry)
  885. {
  886. pTmpUriCacheEntry = pUriCacheEntry;
  887. pBucket->pUriCacheEntry
  888. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next;
  889. pUriCacheEntry = pBucket->pUriCacheEntry;
  890. DEREFERENCE_URI_CACHE_ENTRY(pTmpUriCacheEntry, CLEAR);
  891. }
  892. ASSERT(NULL == pBucket->pUriCacheEntry);
  893. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  894. // Scan the records array
  895. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  896. {
  897. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  898. if (NULL == pUriCacheEntry)
  899. {
  900. ASSERT(HASH_INVALID_SIGNATURE == pHashUriKey[i].Hash);
  901. break;
  902. }
  903. else
  904. {
  905. ASSERT(HASH_INVALID_SIGNATURE != pHashUriKey[i].Hash);
  906. }
  907. DEREFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, CLEAR);
  908. }
  909. pBucket->Entries = 0;
  910. ASSERT(UlpHashBucketIsCompact(pBucket));
  911. } // UlpClearHashBucket
  912. /***************************************************************************++
  913. Routine Description:
  914. Removes all entries of the hash table.
  915. Arguments:
  916. pHashTable - The hash table
  917. --***************************************************************************/
  918. VOID
  919. UlClearHashTable(
  920. IN PHASHTABLE pHashTable
  921. )
  922. {
  923. ULONG i;
  924. PHASHBUCKET pBucket;
  925. HASH_PAGED_CODE(pHashTable);
  926. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  927. for (i = 0; i < g_UlHashTableSize ;i++)
  928. {
  929. pBucket = UlpHashTableIndexedBucket(pHashTable, i);
  930. UlAcquireRWSpinLockExclusive(&pBucket->RWSpinLock);
  931. UlpClearHashBucket(pBucket);
  932. UlReleaseRWSpinLockExclusive(&pBucket->RWSpinLock);
  933. }
  934. } // UlClearHashTable