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.

1575 lines
45 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. hash.c
  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. //
  16. // Interlocked ops for pointer-sized data - used to update g_UlHashTablePages
  17. //
  18. #ifdef _WIN64
  19. #define UlpInterlockedAddPointer InterlockedExchangeAdd64
  20. #else
  21. #define UlpInterlockedAddPointer InterlockedExchangeAdd
  22. #endif // _WIN64
  23. // Global Variables
  24. ULONG g_UlHashTableBits;
  25. ULONG g_UlHashTableSize;
  26. ULONG g_UlHashTableMask;
  27. ULONG g_UlHashIndexShift;
  28. //
  29. // Total Pages used by all cache entries in the hash table
  30. //
  31. LONG_PTR g_UlHashTablePages;
  32. //
  33. // Optimization: Use the space of (g_UlCacheLineSize - sizeof (HASHBUCKET))
  34. // to store a few records (Hash, pUriCacheEntry) such that we can scan the
  35. // records first before jumping to the single list for searching.
  36. //
  37. // g_UlNumOfHashUriKeys: The number of stored records in the space.
  38. //
  39. ULONG g_UlNumOfHashUriKeys;
  40. #ifdef ALLOC_PRAGMA
  41. #pragma alloc_text( INIT, UlpGetHashTableSize )
  42. #pragma alloc_text( PAGE, UlInitializeHashTable )
  43. #pragma alloc_text( PAGE, UlpHashBucketIsCompact )
  44. #pragma alloc_text( PAGE, UlTerminateHashTable )
  45. #pragma alloc_text( PAGE, UlGetFromHashTable )
  46. #pragma alloc_text( PAGE, UlDeleteFromHashTable )
  47. #pragma alloc_text( PAGE, UlAddToHashTable )
  48. #pragma alloc_text( PAGE, UlpFilterFlushHashBucket )
  49. #pragma alloc_text( PAGE, UlFilterFlushHashTable )
  50. #pragma alloc_text( PAGE, UlpClearHashBucket )
  51. #pragma alloc_text( PAGE, UlClearHashTable )
  52. #pragma alloc_text( PAGE, UlGetHashTablePages )
  53. #endif // ALLOC_PRAGMA
  54. #if 0
  55. NOT PAGEABLE --
  56. #endif
  57. /***************************************************************************++
  58. Routine Description:
  59. This routine determine the hash table size according to
  60. (1) user-define value (reading from registry) or
  61. (2) system memory size estimation, if (1) is not defined
  62. Arguments:
  63. HashTableBits - The number of buckets is (1 << HashTableBits)
  64. --***************************************************************************/
  65. VOID
  66. UlpGetHashTableSize(
  67. IN LONG HashTableBits
  68. )
  69. {
  70. // Each hashbucket is (1 << g_UlHashIndexShift) bytes long
  71. g_UlHashIndexShift = g_UlCacheLineBits;
  72. ASSERT(g_UlHashIndexShift < 10);
  73. //
  74. // HashTableBits is equal to DEFAULT_HASH_TABLE_BITS
  75. // if it is not defined in the registry
  76. //
  77. if (HashTableBits != DEFAULT_HASH_TABLE_BITS)
  78. {
  79. // Use the registry value
  80. // We must check for reasonable values, so that a
  81. // malicious or careless user doesn't cause us to eat up
  82. // all of (Non)PagedPool.
  83. if (HashTableBits < 3)
  84. {
  85. // Use a minimum of 8 buckets
  86. HashTableBits = 3;
  87. }
  88. else if ((ULONG) HashTableBits > 24 - g_UlHashIndexShift)
  89. {
  90. // Never use more than 16MB for the hash table.
  91. // Assuming g_UlHashIndexShift = 6 (CacheLine = 64 bytes),
  92. // that's 256K buckets.
  93. HashTableBits = 24 - g_UlHashIndexShift;
  94. }
  95. g_UlHashTableBits = HashTableBits;
  96. }
  97. else
  98. {
  99. #if DBG
  100. //
  101. // Default to a relatively small number of buckets so that the
  102. // assertions don't consume an inordinate amount of time whenever
  103. // we flush the hash table.
  104. //
  105. g_UlHashTableBits = 9;
  106. #else // !DBG
  107. //
  108. // Registry value REGISTRY_HASH_TABLE_BITS is not defined,
  109. // use system memory size estimation instead
  110. //
  111. if (g_UlTotalPhysicalMemMB <= 128)
  112. {
  113. // Small machine. Hash Table Size: 512 buckets
  114. g_UlHashTableBits = 9;
  115. }
  116. else if (g_UlTotalPhysicalMemMB <= 512)
  117. {
  118. // Medium machine. Hash Table Size: 8K buckets
  119. g_UlHashTableBits = 13;
  120. }
  121. else
  122. {
  123. // Large machine. Hash Table Size: 64K buckets
  124. g_UlHashTableBits = 16;
  125. }
  126. #endif // !DBG
  127. }
  128. #ifdef HASH_TEST
  129. g_UlHashTableBits = 3;
  130. #endif
  131. ASSERT(3 <= g_UlHashTableBits && g_UlHashTableBits <= 20);
  132. g_UlHashTableSize = (1 << g_UlHashTableBits);
  133. g_UlHashTableMask = g_UlHashTableSize - 1;
  134. } // UlpGetHashTableSize
  135. /***************************************************************************++
  136. Routine Description:
  137. Validates that a locked HASHBUCKET is `compact'. If there are less than
  138. g_UlNumOfHashUriKeys entries in a bucket, they are clumped together at
  139. the beginning of the records array, and all the empty slots are at the
  140. end. All empty slots must have Hash == HASH_INVALID_SIGNATURE and
  141. pUriCacheEntry == NULL. Conversely, all the non-empty slots at the
  142. beginning of the array must have point to valid UL_URI_CACHE_ENTRYs
  143. and must have Hash == correct hash signature, which cannot be
  144. HASH_INVALID_SIGNATURE. If the single list pointer is non-NULL, then
  145. the records array must be full.
  146. If the HASHBUCKET is compact, then we can abort a search for a key as
  147. soon as we see HASH_INVALID_SIGNATURE. This invariant speeds up Find and
  148. Insert at the cost of making Delete and Flush a little more
  149. complex. Since we expect to do a lot more Finds than Deletes or Inserts,
  150. this is an acceptable tradeoff.
  151. Storing hash signatures means that we have a very fast test that
  152. eliminates almost all false positives. We very seldom find two keys
  153. that have matching hash signatures, but different strings.
  154. Arguments:
  155. pBucket - The hash bucket
  156. --***************************************************************************/
  157. BOOLEAN
  158. UlpHashBucketIsCompact(
  159. IN const PHASHBUCKET pBucket)
  160. {
  161. #ifdef HASH_FULL_ASSERTS
  162. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  163. PUL_URI_CACHE_ENTRY pPrevUriCacheEntry = NULL;
  164. PHASHURIKEY pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  165. ULONG i, j, Entries = 0;
  166. // First, validate the records array
  167. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  168. {
  169. ULONG Hash = pHashUriKey[i].Hash;
  170. if (HASH_INVALID_SIGNATURE == Hash)
  171. {
  172. // There are no more valid entries in the records array
  173. // and no singly linked list
  174. ASSERT(NULL == pBucket->pUriCacheEntry);
  175. pPrevUriCacheEntry = NULL;
  176. for (j = i; j < g_UlNumOfHashUriKeys; j++)
  177. {
  178. ASSERT(NULL == pHashUriKey[j].pUriCacheEntry);
  179. ASSERT(HASH_INVALID_SIGNATURE == pHashUriKey[j].Hash);
  180. }
  181. }
  182. else
  183. {
  184. // non-empty slot
  185. ++Entries;
  186. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  187. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry));
  188. ASSERT(pUriCacheEntry->Cached);
  189. ASSERT(Hash == pUriCacheEntry->UriKey.Hash);
  190. ASSERT(Hash == HashRandomizeBits(
  191. HashStringNoCaseW(
  192. pUriCacheEntry->UriKey.pUri,
  193. 0
  194. )));
  195. ASSERT(pPrevUriCacheEntry != pUriCacheEntry);
  196. pPrevUriCacheEntry = pUriCacheEntry;
  197. }
  198. }
  199. // Next, validate the singly linked list
  200. for (pUriCacheEntry = pBucket->pUriCacheEntry;
  201. NULL != pUriCacheEntry;
  202. pUriCacheEntry
  203. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next)
  204. {
  205. ++Entries;
  206. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry));
  207. ASSERT(pUriCacheEntry->Cached);
  208. ASSERT(pUriCacheEntry->UriKey.Hash
  209. == HashRandomizeBits(
  210. HashStringNoCaseW(
  211. pUriCacheEntry->UriKey.pUri,
  212. 0
  213. )));
  214. ASSERT(pPrevUriCacheEntry != pUriCacheEntry);
  215. pPrevUriCacheEntry = pUriCacheEntry;
  216. }
  217. ASSERT(Entries == pBucket->Entries);
  218. #else // !HASH_FULL_ASSERTS
  219. UNREFERENCED_PARAMETER(pBucket);
  220. #endif // !HASH_FULL_ASSERTS
  221. return TRUE;
  222. } // UlpHashBucketIsCompact
  223. #ifdef HASH_TEST
  224. NTSYSAPI
  225. NTSTATUS
  226. NTAPI
  227. ZwYieldExecution (
  228. VOID
  229. );
  230. /***************************************************************************++
  231. Routine Description:
  232. Randomly delay for a long time. Exercises the yield logic in RWSPINLOCK
  233. Arguments:
  234. None
  235. Returns:
  236. Nothing
  237. --***************************************************************************/
  238. VOID
  239. UlpRandomlyBlockHashTable()
  240. {
  241. static LONG Count = 0;
  242. if ((++Count & 0x3F) == 0)
  243. {
  244. DbgPrint("UlpRandomlyBlockHashTable: starting delay\n");
  245. #if 1
  246. LARGE_INTEGER Interval;
  247. Interval.QuadPart = - C_NS_TICKS_PER_SEC * 3;
  248. KeDelayExecutionThread(KernelMode, FALSE, &Interval);
  249. #elif 0
  250. ZwYieldExecution();
  251. #else
  252. // An effort to make sure the optimizer doesn't omit the inner loop
  253. volatile ULONG* pJunk = &g_UlHashTableBits;
  254. const ULONG Junk = *pJunk;
  255. LONG i;
  256. for (i = 0; i < 10 * 1000 * 1000; ++i)
  257. {
  258. // Won't happen
  259. if (*pJunk != Junk)
  260. break;
  261. }
  262. #endif
  263. DbgPrint("UlpRandomlyBlockHashTable: finishing delay\n");
  264. }
  265. } // UlpRandomlyBlockHashTable
  266. # define RANDOMLY_BLOCK_HASHTABLE() UlpRandomlyBlockHashTable()
  267. #else // !HASH_TEST
  268. # define RANDOMLY_BLOCK_HASHTABLE() NOP_FUNCTION
  269. #endif // !HASH_TEST
  270. /***************************************************************************++
  271. Routine Description:
  272. This routine initialize the hash table
  273. Arguments:
  274. pHashTable - The hash table
  275. PoolType - Specifies the type of pool memory to allocate
  276. HashTableBits - The number of buckets is (1 << HashTableBits)
  277. Returns:
  278. NTSTATUS - STATUS_SUCCESS or STATUS_NO_MEMORY
  279. --***************************************************************************/
  280. NTSTATUS
  281. UlInitializeHashTable(
  282. IN OUT PHASHTABLE pHashTable,
  283. IN POOL_TYPE PoolType,
  284. IN LONG HashTableBits
  285. )
  286. {
  287. ULONG i;
  288. ULONG_PTR CacheLineMask, CacheLineSize = g_UlCacheLineSize;
  289. //
  290. // First, get the hash table size from the registry.
  291. // If not defined in the registry, determine the hash table
  292. // size by the system memory size
  293. //
  294. UlpGetHashTableSize(HashTableBits);
  295. #ifdef HASH_TEST
  296. CacheLineSize = 64;
  297. g_UlHashIndexShift = 6;
  298. #endif
  299. CacheLineMask = CacheLineSize - 1;
  300. ASSERT((CacheLineSize & CacheLineMask) == 0); // power of 2
  301. ASSERT(CacheLineSize == (1U << g_UlHashIndexShift));
  302. pHashTable->Signature = UL_HASH_TABLE_POOL_TAG;
  303. pHashTable->PoolType = PoolType;
  304. pHashTable->NumberOfBytes = g_UlHashTableSize * CacheLineSize;
  305. ASSERT(CacheLineSize > sizeof(HASHBUCKET));
  306. // number of keys stored in the initial clump
  307. g_UlNumOfHashUriKeys = (((ULONG) CacheLineSize - sizeof (HASHBUCKET))
  308. / sizeof(HASHURIKEY));
  309. pHashTable->pBuckets = NULL;
  310. #ifdef HASH_TEST
  311. g_UlNumOfHashUriKeys = 3;
  312. #endif
  313. ASSERT((sizeof(HASHBUCKET) + g_UlNumOfHashUriKeys * sizeof(HASHURIKEY))
  314. <= (1U << g_UlHashIndexShift));
  315. // Allocate the memory
  316. pHashTable->pAllocMem
  317. = (PHASHBUCKET) UL_ALLOCATE_POOL(
  318. PoolType,
  319. pHashTable->NumberOfBytes + CacheLineMask,
  320. UL_HASH_TABLE_POOL_TAG
  321. );
  322. if (NULL == pHashTable->pAllocMem)
  323. {
  324. pHashTable->Signature = MAKE_FREE_TAG(UL_HASH_TABLE_POOL_TAG);
  325. return STATUS_NO_MEMORY;
  326. }
  327. // Initialize cache entry page count
  328. g_UlHashTablePages = 0;
  329. // Align the memory the cache line size boundary
  330. pHashTable->pBuckets
  331. = (PHASHBUCKET)((((ULONG_PTR)(pHashTable->pAllocMem)) + CacheLineMask)
  332. & ~CacheLineMask);
  333. // Initialize each bucket and padding array elements
  334. for (i = 0; i < g_UlHashTableSize; i++)
  335. {
  336. PHASHBUCKET pBucket = UlpHashTableIndexedBucket(pHashTable, i);
  337. PHASHURIKEY pHashUriKey;
  338. ULONG j;
  339. UlInitializeRWSpinLock(&pBucket->RWSpinLock);
  340. pBucket->pUriCacheEntry = NULL;
  341. pBucket->Entries = 0;
  342. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  343. for (j = 0; j < g_UlNumOfHashUriKeys ;j++)
  344. {
  345. pHashUriKey[j].Hash = HASH_INVALID_SIGNATURE;
  346. pHashUriKey[j].pUriCacheEntry = NULL;
  347. }
  348. ASSERT(UlpHashBucketIsCompact(pBucket));
  349. }
  350. return STATUS_SUCCESS;
  351. } // UlInitializeHashTable
  352. /***************************************************************************++
  353. Routine Description:
  354. This routine terminates the hash table
  355. (flush all entries and free the table).
  356. Arguments:
  357. pHashTable - The hash table
  358. --***************************************************************************/
  359. VOID
  360. UlTerminateHashTable(
  361. IN PHASHTABLE pHashTable
  362. )
  363. {
  364. if ( pHashTable->pAllocMem != NULL )
  365. {
  366. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  367. // Clear the hash table (delete all entries)
  368. UlClearHashTable(pHashTable);
  369. // Free the hash table buckets
  370. UL_FREE_POOL(pHashTable->pAllocMem, UL_HASH_TABLE_POOL_TAG);
  371. pHashTable->Signature = MAKE_FREE_TAG(UL_HASH_TABLE_POOL_TAG);
  372. pHashTable->pAllocMem = pHashTable->pBuckets = NULL;
  373. ASSERT( g_UlHashTablePages == 0 );
  374. }
  375. } // UlTerminateHashTable
  376. /***************************************************************************++
  377. Routine Description:
  378. This routine does a cache lookup on a hash table
  379. to see if there is a valid entry corresponding to the request key.
  380. Increment the reference counter of the entry by 1 inside the lock
  381. protection to ensure this entry will be still alive after returning
  382. this entry back.
  383. Arguments:
  384. pHashTable - The hash table
  385. pUriKey - the search key
  386. Returns:
  387. PUL_URI_CACHE_ENTRY - pointer to entry or NULL
  388. --***************************************************************************/
  389. PUL_URI_CACHE_ENTRY
  390. UlGetFromHashTable(
  391. IN PHASHTABLE pHashTable,
  392. IN PURI_SEARCH_KEY pSearchKey
  393. )
  394. {
  395. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  396. PHASHBUCKET pBucket;
  397. PHASHURIKEY pHashUriKey;
  398. ULONG i;
  399. ULONG SearchKeyHash;
  400. PURI_KEY pKey;
  401. PEX_URI_KEY pExKey;
  402. HASH_PAGED_CODE(pHashTable);
  403. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  404. ASSERT(IS_VALID_URI_SEARCH_KEY(pSearchKey));
  405. // Identify what type of search key has been passed.
  406. if (pSearchKey->Type == UriKeyTypeExtended)
  407. {
  408. SearchKeyHash = pSearchKey->ExKey.Hash;
  409. pExKey = &pSearchKey->ExKey;
  410. pKey = NULL;
  411. }
  412. else
  413. {
  414. ASSERT(pSearchKey->Type == UriKeyTypeNormal);
  415. SearchKeyHash = pSearchKey->Key.Hash;
  416. pExKey = NULL;
  417. pKey = &pSearchKey->Key;
  418. }
  419. pBucket = UlpHashTableBucketFromUriKeyHash(pHashTable, SearchKeyHash);
  420. UlAcquireRWSpinLockShared(&pBucket->RWSpinLock);
  421. RANDOMLY_BLOCK_HASHTABLE();
  422. ASSERT(UlpHashBucketIsCompact(pBucket));
  423. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  424. // Scan the records array first
  425. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  426. {
  427. ULONG Hash = pHashUriKey[i].Hash;
  428. if (HASH_INVALID_SIGNATURE == Hash)
  429. {
  430. // There are no more valid entries in the bucket
  431. ASSERT(NULL == pBucket->pUriCacheEntry);
  432. ASSERT(NULL == pHashUriKey[i].pUriCacheEntry);
  433. pUriCacheEntry = NULL;
  434. goto unlock;
  435. }
  436. if (Hash == SearchKeyHash)
  437. {
  438. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  439. ASSERT(NULL != pUriCacheEntry);
  440. if (pExKey)
  441. {
  442. ASSERT(pKey == NULL);
  443. if(UlpEqualUriKeysEx(pExKey, &pUriCacheEntry->UriKey))
  444. {
  445. goto addref;
  446. }
  447. }
  448. else
  449. {
  450. if(UlpEqualUriKeys(pKey, &pUriCacheEntry->UriKey))
  451. {
  452. goto addref;
  453. }
  454. }
  455. }
  456. }
  457. ASSERT(i == g_UlNumOfHashUriKeys);
  458. // Jump to the single list for searching
  459. for (pUriCacheEntry = pBucket->pUriCacheEntry;
  460. NULL != pUriCacheEntry;
  461. pUriCacheEntry
  462. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next)
  463. {
  464. if (pUriCacheEntry->UriKey.Hash == SearchKeyHash)
  465. {
  466. if (pExKey)
  467. {
  468. ASSERT(pKey == NULL);
  469. if(UlpEqualUriKeysEx(pExKey, &pUriCacheEntry->UriKey))
  470. {
  471. goto addref;
  472. }
  473. }
  474. else
  475. {
  476. if(UlpEqualUriKeys(pKey, &pUriCacheEntry->UriKey))
  477. {
  478. goto addref;
  479. }
  480. }
  481. }
  482. }
  483. // Not found
  484. ASSERT(NULL == pUriCacheEntry);
  485. goto unlock;
  486. addref:
  487. ASSERT(NULL != pUriCacheEntry);
  488. REFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, CHECKOUT);
  489. unlock:
  490. ASSERT(UlpHashBucketIsCompact(pBucket));
  491. UlReleaseRWSpinLockShared(&pBucket->RWSpinLock);
  492. return pUriCacheEntry;
  493. } // UlGetFromHashTable
  494. /***************************************************************************++
  495. Routine Description:
  496. This routine does a cache lookup on a hash table
  497. to see if there is a valid entry corresponding to the request URI,
  498. if found, delete this entry. However, increment the reference counter
  499. of the entry by 1 insde the lock protection to ensure this entry will be
  500. still alive after returning this entry back.
  501. Arguments:
  502. pHashTable - The hash table
  503. pUriKey - the search key
  504. pProcess - The app pool process that is requesting the delete
  505. Returns:
  506. PUL_URI_CACHE_ENTRY - pointer to entry removed from table or NULL
  507. --***************************************************************************/
  508. PUL_URI_CACHE_ENTRY
  509. UlDeleteFromHashTable(
  510. IN PHASHTABLE pHashTable,
  511. IN PURI_KEY pUriKey,
  512. IN PUL_APP_POOL_PROCESS pProcess
  513. )
  514. {
  515. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  516. PUL_URI_CACHE_ENTRY PrevUriCacheEntry;
  517. PHASHBUCKET pBucket;
  518. PHASHURIKEY pHashUriKey;
  519. ULONG i;
  520. LONG_PTR UriCacheEntryPages;
  521. HASH_PAGED_CODE(pHashTable);
  522. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  523. pBucket = UlpHashTableBucketFromUriKeyHash(pHashTable, pUriKey->Hash);
  524. UlAcquireRWSpinLockExclusive(&pBucket->RWSpinLock);
  525. RANDOMLY_BLOCK_HASHTABLE();
  526. ASSERT(UlpHashBucketIsCompact(pBucket));
  527. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  528. // Scan the records array first
  529. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  530. {
  531. ULONG Hash = pHashUriKey[i].Hash;
  532. if (HASH_INVALID_SIGNATURE == Hash)
  533. {
  534. ASSERT(NULL == pBucket->pUriCacheEntry);
  535. ASSERT(NULL == pHashUriKey[i].pUriCacheEntry);
  536. pUriCacheEntry = NULL;
  537. goto unlock;
  538. }
  539. if (Hash == pUriKey->Hash)
  540. {
  541. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  542. ASSERT(NULL != pUriCacheEntry);
  543. if (pUriCacheEntry->pProcess == pProcess &&
  544. UlpEqualUriKeys(&pUriCacheEntry->UriKey, pUriKey))
  545. {
  546. --pBucket->Entries;
  547. if (pBucket->pUriCacheEntry)
  548. {
  549. // If there exists an entry in the single list,
  550. // move it to the array
  551. pHashUriKey[i].Hash
  552. = pBucket->pUriCacheEntry->UriKey.Hash;
  553. pHashUriKey[i].pUriCacheEntry = pBucket->pUriCacheEntry;
  554. pBucket->pUriCacheEntry
  555. = (PUL_URI_CACHE_ENTRY)
  556. pBucket->pUriCacheEntry->BucketEntry.Next;
  557. }
  558. else
  559. {
  560. // if this is not the last entry in the records array,
  561. // move the last entry to this slot
  562. ULONG j;
  563. for (j = g_UlNumOfHashUriKeys; --j >= i; )
  564. {
  565. if (NULL != pHashUriKey[j].pUriCacheEntry)
  566. {
  567. ASSERT(HASH_INVALID_SIGNATURE
  568. != pHashUriKey[j].Hash);
  569. ASSERT(j >= i);
  570. pHashUriKey[i].Hash = pHashUriKey[j].Hash;
  571. pHashUriKey[i].pUriCacheEntry
  572. = pHashUriKey[j].pUriCacheEntry;
  573. // Zap the last entry. Correct even if j == i
  574. pHashUriKey[j].Hash = HASH_INVALID_SIGNATURE;
  575. pHashUriKey[j].pUriCacheEntry = NULL;
  576. goto unlock;
  577. }
  578. else
  579. {
  580. ASSERT(HASH_INVALID_SIGNATURE
  581. == pHashUriKey[j].Hash);
  582. }
  583. }
  584. // We can't get here, since pHashUriKey[i] should
  585. // have terminated the loop even if there wasn't
  586. // any non-empty slot following it.
  587. ASSERT(! "Overshot the deleted entry");
  588. }
  589. goto unlock;
  590. }
  591. }
  592. }
  593. ASSERT(i == g_UlNumOfHashUriKeys);
  594. // Jump to the single list for searching
  595. pUriCacheEntry = pBucket->pUriCacheEntry;
  596. PrevUriCacheEntry = NULL;
  597. while (NULL != pUriCacheEntry)
  598. {
  599. if (pUriCacheEntry->pProcess == pProcess &&
  600. pUriCacheEntry->UriKey.Hash == pUriKey->Hash &&
  601. UlpEqualUriKeys(&pUriCacheEntry->UriKey, pUriKey))
  602. {
  603. if (PrevUriCacheEntry == NULL)
  604. {
  605. // Delete First Entry
  606. pBucket->pUriCacheEntry
  607. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next;
  608. }
  609. else
  610. {
  611. PrevUriCacheEntry->BucketEntry.Next
  612. = pUriCacheEntry->BucketEntry.Next;
  613. }
  614. --pBucket->Entries;
  615. goto unlock;
  616. }
  617. PrevUriCacheEntry = pUriCacheEntry;
  618. pUriCacheEntry
  619. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next;
  620. }
  621. // Not found
  622. ASSERT(NULL == pUriCacheEntry);
  623. unlock:
  624. ASSERT((LONG) pBucket->Entries >= 0);
  625. ASSERT(UlpHashBucketIsCompact(pBucket));
  626. UlReleaseRWSpinLockExclusive(&pBucket->RWSpinLock);
  627. if(pUriCacheEntry != NULL) {
  628. UriCacheEntryPages = pUriCacheEntry->NumPages;
  629. UlpInterlockedAddPointer( &g_UlHashTablePages, -UriCacheEntryPages );
  630. }
  631. return pUriCacheEntry;
  632. } // UlDeleteFromHashTable
  633. /***************************************************************************++
  634. Routine Description:
  635. This routine does a cache lookup on a hash table
  636. to see if a given entry exists, if not found, add this entry to the
  637. hash table. Increment the reference counter of the entry by 1 insde the
  638. lock protection.
  639. Arguments:
  640. pHashTable - The hash table
  641. pUriCacheEntry - the given entry
  642. Returns
  643. NTSTATUS - STATUS_SUCCESS or STATUS_DUPLICATE_NAME
  644. --***************************************************************************/
  645. NTSTATUS
  646. UlAddToHashTable(
  647. IN PHASHTABLE pHashTable,
  648. IN PUL_URI_CACHE_ENTRY pUriCacheEntry
  649. )
  650. {
  651. PUL_URI_CACHE_ENTRY pTmpUriCacheEntry;
  652. PUL_URI_CACHE_ENTRY pPrevUriCacheEntry;
  653. PUL_URI_CACHE_ENTRY pDerefUriCacheEntry = NULL;
  654. PURI_KEY pUriKey;
  655. PHASHBUCKET pBucket;
  656. PHASHURIKEY pHashUriKey;
  657. LONG EmptySlot = INVALID_SLOT_INDEX;
  658. NTSTATUS Status = STATUS_SUCCESS;
  659. ULONG i;
  660. LONG_PTR UriCacheEntryPages;
  661. HASH_PAGED_CODE(pHashTable);
  662. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  663. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry));
  664. ASSERT(pUriCacheEntry->Cached);
  665. pUriKey = &pUriCacheEntry->UriKey;
  666. pBucket = UlpHashTableBucketFromUriKeyHash(pHashTable, pUriKey->Hash);
  667. UlAcquireRWSpinLockExclusive(&pBucket->RWSpinLock);
  668. RANDOMLY_BLOCK_HASHTABLE();
  669. ASSERT(UlpHashBucketIsCompact(pBucket));
  670. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  671. // Scan the records array first
  672. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  673. {
  674. ULONG Hash = pHashUriKey[i].Hash;
  675. if (HASH_INVALID_SIGNATURE == Hash)
  676. {
  677. ASSERT(NULL == pBucket->pUriCacheEntry);
  678. ASSERT(NULL == pHashUriKey[i].pUriCacheEntry);
  679. EmptySlot = (LONG) i;
  680. goto insert;
  681. }
  682. if (Hash == pUriKey->Hash)
  683. {
  684. pTmpUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  685. ASSERT(NULL != pTmpUriCacheEntry);
  686. if (UlpEqualUriKeys(&pTmpUriCacheEntry->UriKey, pUriKey))
  687. {
  688. // Duplicate key exists. We will always overwrite the old entry
  689. // unless it was a response && the new entry is a fragment.
  690. if (IS_RESPONSE_CACHE_ENTRY(pUriCacheEntry) ||
  691. IS_FRAGMENT_CACHE_ENTRY(pTmpUriCacheEntry))
  692. {
  693. pHashUriKey[i].pUriCacheEntry = pUriCacheEntry;
  694. UriCacheEntryPages = pUriCacheEntry->NumPages - pTmpUriCacheEntry->NumPages;
  695. pDerefUriCacheEntry = pTmpUriCacheEntry;
  696. REFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, ADD);
  697. UlpInterlockedAddPointer(&g_UlHashTablePages, UriCacheEntryPages );
  698. Status = STATUS_SUCCESS;
  699. }
  700. else
  701. {
  702. pUriCacheEntry->Cached = FALSE;
  703. Status = STATUS_DUPLICATE_NAME;
  704. }
  705. goto unlock;
  706. }
  707. }
  708. }
  709. ASSERT(i == g_UlNumOfHashUriKeys);
  710. ASSERT(EmptySlot == INVALID_SLOT_INDEX);
  711. // Jump to the single list for searching
  712. pPrevUriCacheEntry = NULL;
  713. for (pTmpUriCacheEntry = pBucket->pUriCacheEntry;
  714. NULL != pTmpUriCacheEntry;
  715. pPrevUriCacheEntry = pTmpUriCacheEntry,
  716. pTmpUriCacheEntry
  717. = (PUL_URI_CACHE_ENTRY) pTmpUriCacheEntry->BucketEntry.Next)
  718. {
  719. if (pTmpUriCacheEntry->UriKey.Hash == pUriKey->Hash
  720. && UlpEqualUriKeys(&pTmpUriCacheEntry->UriKey, pUriKey))
  721. {
  722. // Duplicate key exists. We will always overwrite the old entry
  723. // unless it was a response && the new entry is a fragment.
  724. if (IS_RESPONSE_CACHE_ENTRY(pUriCacheEntry) ||
  725. IS_FRAGMENT_CACHE_ENTRY(pTmpUriCacheEntry))
  726. {
  727. pUriCacheEntry->BucketEntry.Next =
  728. pTmpUriCacheEntry->BucketEntry.Next;
  729. if (NULL == pPrevUriCacheEntry)
  730. {
  731. pBucket->pUriCacheEntry = pUriCacheEntry;
  732. }
  733. else
  734. {
  735. pPrevUriCacheEntry->BucketEntry.Next =
  736. (PSINGLE_LIST_ENTRY) pUriCacheEntry;
  737. }
  738. UriCacheEntryPages = pUriCacheEntry->NumPages - pTmpUriCacheEntry->NumPages;
  739. pDerefUriCacheEntry = pTmpUriCacheEntry;
  740. REFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, ADD);
  741. UlpInterlockedAddPointer(&g_UlHashTablePages, UriCacheEntryPages );
  742. Status = STATUS_SUCCESS;
  743. }
  744. else
  745. {
  746. pUriCacheEntry->Cached = FALSE;
  747. Status = STATUS_DUPLICATE_NAME;
  748. }
  749. goto unlock;
  750. }
  751. }
  752. insert:
  753. //
  754. // Not found: no duplicate key in hash table
  755. //
  756. if (EmptySlot != INVALID_SLOT_INDEX)
  757. {
  758. ASSERT(0 <= EmptySlot && EmptySlot < (LONG) g_UlNumOfHashUriKeys);
  759. // First, try to add this entry to the array if there is an empty slot.
  760. pHashUriKey[EmptySlot].Hash = pUriKey->Hash;
  761. pHashUriKey[EmptySlot].pUriCacheEntry = pUriCacheEntry;
  762. }
  763. else
  764. {
  765. // Otherwise, add this entry to the head of the single list
  766. pUriCacheEntry->BucketEntry.Next
  767. = (PSINGLE_LIST_ENTRY) pBucket->pUriCacheEntry;
  768. pBucket->pUriCacheEntry = pUriCacheEntry;
  769. }
  770. REFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, ADD);
  771. UlpInterlockedAddPointer( &g_UlHashTablePages,
  772. pUriCacheEntry->NumPages );
  773. ASSERT((LONG) pBucket->Entries >= 0);
  774. ++pBucket->Entries;
  775. Status = STATUS_SUCCESS;
  776. unlock:
  777. ASSERT(UlpHashBucketIsCompact(pBucket));
  778. UlReleaseRWSpinLockExclusive(&pBucket->RWSpinLock);
  779. if (pDerefUriCacheEntry)
  780. {
  781. DEREFERENCE_URI_CACHE_ENTRY(pDerefUriCacheEntry, ADD);
  782. }
  783. return Status;
  784. } // UlAddToHashTable
  785. /***************************************************************************++
  786. Routine Description:
  787. Removes entries based on a caller-specified filter. The caller
  788. provides a predicate function which takes a cache entry as a
  789. parameter. The function will be called for each item in the cache.
  790. If the function returns ULC_DELETE, the item will be removed.
  791. Otherwise the item will remain in the cache.
  792. All removals are done on a hash table bucket.
  793. Walk through all the entries under this bucket.
  794. Assume bucket exclusive lock is held.
  795. Arguments:
  796. pBucket - The hash table bucket
  797. pFilterRoutine - A pointer to the filter function
  798. pContext - A parameter to the filter function
  799. pDeletedCount - A pointer to the number of deleted entries on this bucket
  800. bStop - A pointer to a boolean variable returned to caller
  801. (TRUE if the filter function asks for action stop)
  802. --***************************************************************************/
  803. BOOLEAN
  804. UlpFilterFlushHashBucket(
  805. IN PHASHBUCKET pBucket,
  806. IN PUL_URI_FILTER pFilterRoutine,
  807. IN PVOID pContext,
  808. OUT PULONG pDeletedCount
  809. )
  810. {
  811. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  812. PUL_URI_CACHE_ENTRY pPrevUriCacheEntry;
  813. PUL_URI_CACHE_ENTRY pTmpUriCacheEntry;
  814. UL_CACHE_PREDICATE result;
  815. PHASHURIKEY pHashUriKey;
  816. ULONG i;
  817. LONG LastSlot;
  818. BOOLEAN bStop = FALSE;
  819. LONG_PTR UriCacheEntryPages;
  820. // Check if bucket exclusive lock is held
  821. ASSERT( UlRWSpinLockIsLockedExclusive(&pBucket->RWSpinLock) );
  822. ASSERT(UlpHashBucketIsCompact(pBucket));
  823. // Scan the single list first
  824. pUriCacheEntry = pBucket->pUriCacheEntry;
  825. pPrevUriCacheEntry = NULL;
  826. while (NULL != pUriCacheEntry)
  827. {
  828. BOOLEAN bDelete = FALSE;
  829. result = (*pFilterRoutine)(pUriCacheEntry, pContext);
  830. switch (result)
  831. {
  832. case ULC_ABORT:
  833. bStop = TRUE;
  834. goto end;
  835. case ULC_NO_ACTION:
  836. // nothing to do
  837. break;
  838. case ULC_DELETE:
  839. case ULC_DELETE_STOP:
  840. {
  841. // Delete this entry
  842. bDelete = TRUE;
  843. ASSERT(pBucket->Entries > 0);
  844. --pBucket->Entries;
  845. pTmpUriCacheEntry = pUriCacheEntry;
  846. if (NULL == pPrevUriCacheEntry)
  847. {
  848. // Delete First Entry
  849. pBucket->pUriCacheEntry
  850. = (PUL_URI_CACHE_ENTRY)
  851. pUriCacheEntry->BucketEntry.Next;
  852. pUriCacheEntry = pBucket->pUriCacheEntry;
  853. }
  854. else
  855. {
  856. pPrevUriCacheEntry->BucketEntry.Next
  857. = pUriCacheEntry->BucketEntry.Next;
  858. pUriCacheEntry
  859. = (PUL_URI_CACHE_ENTRY)
  860. pPrevUriCacheEntry->BucketEntry.Next;
  861. }
  862. ASSERT(UlpHashBucketIsCompact(pBucket));
  863. UriCacheEntryPages = pTmpUriCacheEntry->NumPages;
  864. UlpInterlockedAddPointer( &g_UlHashTablePages, -UriCacheEntryPages );
  865. DEREFERENCE_URI_CACHE_ENTRY(pTmpUriCacheEntry, FILTER);
  866. ++(*pDeletedCount);
  867. if (result == ULC_DELETE_STOP)
  868. {
  869. bStop = TRUE;
  870. goto end;
  871. }
  872. break;
  873. }
  874. default:
  875. break;
  876. }
  877. if (!bDelete)
  878. {
  879. pPrevUriCacheEntry = pUriCacheEntry;
  880. pUriCacheEntry
  881. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next;
  882. }
  883. }
  884. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  885. //
  886. // Now, scan the records array.
  887. //
  888. // Because we keep the records array compact, we need to keep
  889. // track of the last valid slot, so that we can move its contents
  890. // to the slot that's being deleted.
  891. //
  892. LastSlot = INVALID_SLOT_INDEX;
  893. if (NULL == pBucket->pUriCacheEntry)
  894. {
  895. for (i = g_UlNumOfHashUriKeys; i-- > 0; )
  896. {
  897. if (NULL != pHashUriKey[i].pUriCacheEntry)
  898. {
  899. ASSERT(HASH_INVALID_SIGNATURE != pHashUriKey[i].Hash);
  900. LastSlot = (LONG) i;
  901. break;
  902. }
  903. else
  904. {
  905. ASSERT(HASH_INVALID_SIGNATURE == pHashUriKey[i].Hash);
  906. }
  907. }
  908. // Is records array completely empty?
  909. if (LastSlot == INVALID_SLOT_INDEX)
  910. goto end;
  911. }
  912. else
  913. {
  914. // final slot cannot be empty
  915. ASSERT(HASH_INVALID_SIGNATURE
  916. != pHashUriKey[g_UlNumOfHashUriKeys-1].Hash);
  917. }
  918. // Walk through the records array
  919. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  920. {
  921. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  922. if (NULL == pUriCacheEntry)
  923. {
  924. ASSERT(HASH_INVALID_SIGNATURE == pHashUriKey[i].Hash);
  925. goto end;
  926. }
  927. else
  928. {
  929. ASSERT(HASH_INVALID_SIGNATURE != pHashUriKey[i].Hash);
  930. }
  931. result = (*pFilterRoutine)(pUriCacheEntry, pContext);
  932. switch (result)
  933. {
  934. case ULC_ABORT:
  935. bStop = TRUE;
  936. goto end;
  937. case ULC_NO_ACTION:
  938. // nothing to do
  939. break;
  940. case ULC_DELETE:
  941. case ULC_DELETE_STOP:
  942. {
  943. // Delete this entry
  944. ASSERT(pBucket->Entries > 0);
  945. --pBucket->Entries;
  946. if (NULL != pBucket->pUriCacheEntry)
  947. {
  948. // If there exists an entry in the single list,
  949. // move it to the array
  950. ASSERT(LastSlot == INVALID_SLOT_INDEX);
  951. pHashUriKey[i].Hash
  952. = pBucket->pUriCacheEntry->UriKey.Hash;
  953. pHashUriKey[i].pUriCacheEntry = pBucket->pUriCacheEntry;
  954. pBucket->pUriCacheEntry
  955. = (PUL_URI_CACHE_ENTRY)
  956. pBucket->pUriCacheEntry->BucketEntry.Next;
  957. if (NULL == pBucket->pUriCacheEntry)
  958. {
  959. LastSlot = g_UlNumOfHashUriKeys - 1;
  960. ASSERT(HASH_INVALID_SIGNATURE
  961. != pHashUriKey[LastSlot].Hash);
  962. }
  963. }
  964. else
  965. {
  966. // Move the entry in the last slot to this position,
  967. // zap the last slot, and move LastSlot backwards
  968. if (LastSlot != INVALID_SLOT_INDEX
  969. && (LONG) i < LastSlot)
  970. {
  971. ASSERT(HASH_INVALID_SIGNATURE
  972. != pHashUriKey[LastSlot].Hash);
  973. pHashUriKey[i].Hash = pHashUriKey[LastSlot].Hash;
  974. pHashUriKey[i].pUriCacheEntry
  975. = pHashUriKey[LastSlot].pUriCacheEntry;
  976. pHashUriKey[LastSlot].Hash = HASH_INVALID_SIGNATURE;
  977. pHashUriKey[LastSlot].pUriCacheEntry = NULL;
  978. if (--LastSlot == (LONG) i)
  979. LastSlot = INVALID_SLOT_INDEX;
  980. else
  981. ASSERT(HASH_INVALID_SIGNATURE
  982. != pHashUriKey[LastSlot].Hash);
  983. }
  984. else
  985. {
  986. // Just reset this array element
  987. pHashUriKey[i].Hash = HASH_INVALID_SIGNATURE;
  988. pHashUriKey[i].pUriCacheEntry = NULL;
  989. LastSlot = INVALID_SLOT_INDEX;
  990. }
  991. }
  992. ASSERT(UlpHashBucketIsCompact(pBucket));
  993. UriCacheEntryPages = pUriCacheEntry->NumPages;
  994. UlpInterlockedAddPointer( &g_UlHashTablePages,
  995. -UriCacheEntryPages );
  996. DEREFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, FILTER);
  997. ++(*pDeletedCount);
  998. if (result == ULC_DELETE_STOP)
  999. {
  1000. bStop = TRUE;
  1001. goto end;
  1002. }
  1003. break;
  1004. }
  1005. default:
  1006. break;
  1007. }
  1008. }
  1009. end:
  1010. ASSERT(UlpHashBucketIsCompact(pBucket));
  1011. return bStop;
  1012. } // UlpFilterFlushHashBucket
  1013. /***************************************************************************++
  1014. Routine Description:
  1015. Removes entries based on a caller-specified filter. The caller
  1016. provides a predicate function which takes a cache entry as a
  1017. parameter. The function will be called with each item in the cache.
  1018. If the function returns ULC_DELETE, the item will be removed.
  1019. Otherwise the item will remain in the cache.
  1020. Arguments:
  1021. pHashTable - The hash table
  1022. pFilterRoutine - A pointer to the filter function
  1023. pContext - A parameter to the filter function
  1024. Returns:
  1025. ULONG - Number of entries flushed from the table
  1026. --***************************************************************************/
  1027. ULONG
  1028. UlFilterFlushHashTable(
  1029. IN PHASHTABLE pHashTable,
  1030. IN PUL_URI_FILTER pFilterRoutine,
  1031. IN PVOID pContext
  1032. )
  1033. {
  1034. ULONG i;
  1035. BOOLEAN bStop = FALSE;
  1036. ULONG DeletedCount = 0;
  1037. HASH_PAGED_CODE(pHashTable);
  1038. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  1039. //
  1040. // Scan and delete (if matching the filter) each bucket
  1041. // of the cache table.
  1042. //
  1043. for (i = 0; !bStop && i < g_UlHashTableSize; i++)
  1044. {
  1045. PHASHBUCKET pBucket = UlpHashTableIndexedBucket(pHashTable, i);
  1046. #ifdef HASH_FULL_ASSERTS
  1047. BOOLEAN TestBucket = TRUE;
  1048. #else
  1049. // If the bucket has no entries, there's no point in locking it
  1050. // and flushing it.
  1051. BOOLEAN TestBucket = (BOOLEAN) (pBucket->Entries > 0);
  1052. #endif
  1053. if (TestBucket)
  1054. {
  1055. ULONG DeletedInBucket = 0;
  1056. UlAcquireRWSpinLockExclusive(&pBucket->RWSpinLock);
  1057. RANDOMLY_BLOCK_HASHTABLE();
  1058. bStop = UlpFilterFlushHashBucket(
  1059. pBucket,
  1060. pFilterRoutine,
  1061. pContext,
  1062. &DeletedInBucket
  1063. );
  1064. UlReleaseRWSpinLockExclusive(&pBucket->RWSpinLock);
  1065. DeletedCount += DeletedInBucket;
  1066. }
  1067. }
  1068. return DeletedCount;
  1069. } // UlFilterFlushHashTable
  1070. /***************************************************************************++
  1071. Routine Description:
  1072. Removes all entries on a bucket.
  1073. Assume bucket exclusive lock is held.
  1074. Arguments:
  1075. pBucket - The hash table bucket
  1076. --***************************************************************************/
  1077. VOID
  1078. UlpClearHashBucket(
  1079. IN PHASHBUCKET pBucket
  1080. )
  1081. {
  1082. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  1083. PUL_URI_CACHE_ENTRY pTmpUriCacheEntry;
  1084. PHASHURIKEY pHashUriKey;
  1085. ULONG i;
  1086. LONG_PTR UriCacheEntryPages;
  1087. // Check if bucket exclusive lock is held
  1088. ASSERT( UlRWSpinLockIsLockedExclusive(&pBucket->RWSpinLock) );
  1089. ASSERT(UlpHashBucketIsCompact(pBucket));
  1090. // Scan the single list first
  1091. pUriCacheEntry = pBucket->pUriCacheEntry;
  1092. while (NULL != pUriCacheEntry)
  1093. {
  1094. pTmpUriCacheEntry = pUriCacheEntry;
  1095. pBucket->pUriCacheEntry
  1096. = (PUL_URI_CACHE_ENTRY) pUriCacheEntry->BucketEntry.Next;
  1097. pUriCacheEntry = pBucket->pUriCacheEntry;
  1098. UriCacheEntryPages = pTmpUriCacheEntry->NumPages;
  1099. UlpInterlockedAddPointer( &g_UlHashTablePages,
  1100. -UriCacheEntryPages );
  1101. DEREFERENCE_URI_CACHE_ENTRY(pTmpUriCacheEntry, CLEAR);
  1102. }
  1103. ASSERT(NULL == pBucket->pUriCacheEntry);
  1104. pHashUriKey = UlpHashTableUriKeyFromBucket(pBucket);
  1105. // Scan the records array
  1106. for (i = 0; i < g_UlNumOfHashUriKeys; i++)
  1107. {
  1108. pUriCacheEntry = pHashUriKey[i].pUriCacheEntry;
  1109. if (NULL == pUriCacheEntry)
  1110. {
  1111. ASSERT(HASH_INVALID_SIGNATURE == pHashUriKey[i].Hash);
  1112. break;
  1113. }
  1114. else
  1115. {
  1116. ASSERT(HASH_INVALID_SIGNATURE != pHashUriKey[i].Hash);
  1117. }
  1118. UriCacheEntryPages = pUriCacheEntry->NumPages;
  1119. UlpInterlockedAddPointer( &g_UlHashTablePages,
  1120. (-UriCacheEntryPages) );
  1121. DEREFERENCE_URI_CACHE_ENTRY(pUriCacheEntry, CLEAR);
  1122. }
  1123. pBucket->Entries = 0;
  1124. ASSERT(UlpHashBucketIsCompact(pBucket));
  1125. } // UlpClearHashBucket
  1126. /***************************************************************************++
  1127. Routine Description:
  1128. Removes all entries of the hash table.
  1129. Arguments:
  1130. pHashTable - The hash table
  1131. --***************************************************************************/
  1132. VOID
  1133. UlClearHashTable(
  1134. IN PHASHTABLE pHashTable
  1135. )
  1136. {
  1137. ULONG i;
  1138. PHASHBUCKET pBucket;
  1139. HASH_PAGED_CODE(pHashTable);
  1140. ASSERT(IS_VALID_HASHTABLE(pHashTable));
  1141. for (i = 0; i < g_UlHashTableSize ;i++)
  1142. {
  1143. pBucket = UlpHashTableIndexedBucket(pHashTable, i);
  1144. UlAcquireRWSpinLockExclusive(&pBucket->RWSpinLock);
  1145. UlpClearHashBucket(pBucket);
  1146. UlReleaseRWSpinLockExclusive(&pBucket->RWSpinLock);
  1147. }
  1148. } // UlClearHashTable
  1149. /***************************************************************************++
  1150. Routine Description:
  1151. Return the number of pages occupied by cache entries in the hash table
  1152. Arguments:
  1153. NONE.
  1154. --***************************************************************************/
  1155. ULONG_PTR
  1156. UlGetHashTablePages()
  1157. {
  1158. PAGED_CODE();
  1159. return g_UlHashTablePages;
  1160. }