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.

903 lines
19 KiB

  1. /****************************************************************************
  2. *
  3. *
  4. * INTEL Corporation Prorietary Information
  5. *
  6. * This listing is supplied under the terms of a license agreement
  7. * with INTEL Corporation and may not be copied nor disclosed except
  8. * in accordance with the terms of that agreement.
  9. *
  10. * Copyright (c) 1996 Intel Corporation.
  11. *
  12. *
  13. * Abstract:
  14. *
  15. * Notes:
  16. *
  17. ***************************************************************************/
  18. #ifndef __TSTABLE_H
  19. #define __TSTABLE_H
  20. typedef struct _LOCK_ENTRY
  21. {
  22. HANDLE hLock;
  23. int iLockCount;
  24. BOOL bCleanup,
  25. bDeleted;
  26. WORD wNextFree,
  27. wUniqueID;
  28. } LOCK_ENTRY, *PLOCK_ENTRY;
  29. // definition of an invalid ID
  30. #define TSTABLE_INVALID_ID (DWORD) 0xFFFFFFFF
  31. // return codes that the callback function used in conjunction with EnumerateEntries can return
  32. const DWORD CALLBACK_CONTINUE = 1;
  33. const DWORD CALLBACK_ABORT = 2;
  34. const DWORD CALLBACK_DELETE_ENTRY = 3;
  35. const DWORD CALLBACK_DELETE_ENTRY_AND_OBJECT = 4;
  36. // used in call to Lock
  37. #define TSTABLE_INVALID_UNIQUE_ID (WORD) 0xFFFF
  38. #define TSTABLE_INVALID_INDEX (WORD) 0xFFFF
  39. // This is a compare function that we aren't using right now. It
  40. // will be useful in the future if there is a reason to search
  41. // the table
  42. typedef INT (*ENTRY_COMPARE) (LPVOID ptr1, LPVOID ptr2);
  43. template <class EntryData> class TSTable
  44. {
  45. typedef DWORD (*TABLE_CALLBACK) (EntryData* ptr, LPVOID context);
  46. public:
  47. TSTable (WORD _size);
  48. ~TSTable ();
  49. BOOL Resize (WORD wNewSize);
  50. BOOL CreateAndLock (EntryData* pEntryData,
  51. LPDWORD lpdwID);
  52. BOOL Validate (DWORD_PTR dwID);
  53. EntryData *Lock (DWORD_PTR dwID,
  54. DWORD timeout = INFINITE);
  55. BOOL Unlock (DWORD_PTR dwID);
  56. BOOL Delete (DWORD_PTR dwID,
  57. BOOL bCleanup = FALSE);
  58. EntryData *EnumerateEntries(TABLE_CALLBACK callBackFunc,
  59. void* context,
  60. BOOL bUnlockTable = FALSE);
  61. BOOL IsInitialized () {return bInitialized;}
  62. WORD GetSize () {return wNumUsed;}
  63. private:
  64. // data
  65. EntryData** pDataTable;
  66. PLOCK_ENTRY pLockTable;
  67. CRITICAL_SECTION csTableLock;
  68. WORD wSize,
  69. wNumUsed,
  70. wFirstFree,
  71. wLastFree,
  72. wUniqueID;
  73. BOOL bInitialized;
  74. // private methods
  75. BOOL LockEntry (WORD wIndex,
  76. DWORD timeout = INFINITE);
  77. BOOL UnLockEntry(WORD wIndex);
  78. void LockTable () { EnterCriticalSection(&csTableLock); };
  79. void UnLockTable() { LeaveCriticalSection(&csTableLock); };
  80. WORD GenerateUniqueID();
  81. DWORD MakeID(WORD wIndex, WORD wUniqueID)
  82. {
  83. DWORD theID = wUniqueID;
  84. theID = (theID << 16) & 0xFFFF0000;
  85. theID |= wIndex;
  86. return(theID);
  87. };
  88. void BreakID(DWORD_PTR theID, WORD* pwIndex, WORD* pwUID)
  89. {
  90. *pwIndex = (WORD) (theID & 0x0000FFFF);
  91. *pwUID = (WORD) ((theID >> 16) & 0x0000FFFF);
  92. };
  93. };
  94. /*
  95. ** TSTable::TSTable
  96. *
  97. * FILENAME: c:\msdev\projects\firewalls\inc\tstable.h
  98. *
  99. * PARAMETERS:
  100. *
  101. * DESCRIPTION:
  102. *
  103. * RETURNS:
  104. *
  105. */
  106. template <class EntryData>
  107. TSTable<EntryData>::TSTable(WORD _size) :
  108. wSize(_size),
  109. wNumUsed((WORD) 0),
  110. wFirstFree((WORD) 0),
  111. wLastFree((WORD) (_size - 1)),
  112. wUniqueID((WORD) 0),
  113. bInitialized(TRUE),
  114. pDataTable(NULL),
  115. pLockTable(NULL)
  116. {
  117. WORD wIndex;
  118. // Create the table lock
  119. InitializeCriticalSection(&csTableLock);
  120. // Lock the table
  121. LockTable();
  122. // Create the data table
  123. pDataTable = new EntryData*[wSize];
  124. if(pDataTable == NULL)
  125. {
  126. bInitialized = FALSE;
  127. return;
  128. }
  129. // Init the pointers
  130. for (wIndex = 0; wIndex < wSize; wIndex++)
  131. {
  132. pDataTable[wIndex] = NULL;
  133. }
  134. // Create the lock table
  135. pLockTable = new LOCK_ENTRY[wSize];
  136. if (pLockTable == NULL)
  137. {
  138. bInitialized = FALSE;
  139. return;
  140. }
  141. // Initialize the lock table entries...each entry begins with
  142. // a NULL mutex handle, a zero lock count and it's next free is
  143. // the next successive entry.
  144. for (wIndex = 0; wIndex < wSize; wIndex++ )
  145. {
  146. pLockTable[wIndex].hLock = NULL;
  147. pLockTable[wIndex].iLockCount = 0;
  148. pLockTable[wIndex].wNextFree = (WORD) (wIndex + 1);
  149. }
  150. // note: the wNextFree in the last table entry points to an invalid index, however,
  151. // this is OK since if the table ever fills, it is automatically resized making what
  152. // was an invalid index, the index into the first entry of newly added part of the
  153. // enlargened table. Trust me...
  154. // Unlock the table
  155. UnLockTable();
  156. }
  157. /*
  158. ** TSTable::~TSTable
  159. *
  160. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  161. *
  162. * PARAMETERS:
  163. *
  164. * DESCRIPTION:
  165. *
  166. * RETURNS:
  167. *
  168. */
  169. template <class EntryData>
  170. TSTable<EntryData>::~TSTable()
  171. {
  172. DWORD wIndex;
  173. // Lock the table
  174. LockTable();
  175. // Delete the data table
  176. if (pDataTable != NULL)
  177. {
  178. delete pDataTable;
  179. }
  180. // Delete the lock table
  181. if (pLockTable != NULL)
  182. {
  183. // Destroy the mutexes
  184. for (wIndex = 0; wIndex < wSize; wIndex++)
  185. {
  186. if (pLockTable[wIndex].hLock != NULL)
  187. {
  188. CloseHandle(pLockTable[wIndex].hLock);
  189. }
  190. }
  191. delete pLockTable;
  192. }
  193. // Unlock the table
  194. UnLockTable();
  195. // Destroy the table lock
  196. DeleteCriticalSection(&csTableLock);
  197. bInitialized = FALSE;
  198. }
  199. /*
  200. ** TSTable::CreateAndLock
  201. *
  202. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  203. *
  204. * PARAMETERS:
  205. *
  206. * DESCRIPTION:
  207. *
  208. * RETURNS:
  209. *
  210. */
  211. template <class EntryData>
  212. BOOL TSTable<EntryData>::CreateAndLock(EntryData* pEntryData,
  213. LPDWORD lpdwID)
  214. {
  215. BOOL bRetCode = FALSE;
  216. WORD wIndex;
  217. // If the pointer passed in is bad, then don't even try to do anything for them
  218. if (pEntryData == NULL || lpdwID == NULL)
  219. {
  220. goto EXIT;
  221. }
  222. // Lock the table
  223. LockTable();
  224. // If the table is full, then resize it.
  225. if (wNumUsed >= wSize - 1)
  226. {
  227. goto EXIT;
  228. }
  229. // Get the first free entry
  230. wIndex = wFirstFree;
  231. // Create the mutex for the object
  232. if ((pLockTable[wIndex].hLock = CreateMutexA(NULL, FALSE, NULL)) == NULL)
  233. {
  234. goto EXIT;
  235. }
  236. // Lock the entry (no need checking the return code as the entire
  237. // table is locked) - since this is a new entry, that means that nobody
  238. // could have locked the entry already.
  239. LockEntry(wIndex, 0);
  240. // Copy pointer to the data table
  241. pDataTable[wIndex] = pEntryData;
  242. // Init the corresponding lock table entry
  243. pLockTable[wIndex].bDeleted = FALSE;
  244. pLockTable[wIndex].iLockCount = 1;
  245. pLockTable[wIndex].wUniqueID = GenerateUniqueID();
  246. // Set the id for the caller
  247. *lpdwID = MakeID(wIndex, pLockTable[wIndex].wUniqueID);
  248. // Bump up the count of number used
  249. wNumUsed++;
  250. // Fix the next free index
  251. wFirstFree = pLockTable[wIndex].wNextFree;
  252. // Signal success
  253. bRetCode = TRUE;
  254. EXIT:
  255. // Unlock the table
  256. UnLockTable();
  257. return bRetCode;
  258. }
  259. /*
  260. ** TSTable::Lock
  261. *
  262. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  263. *
  264. * PARAMETERS:
  265. *
  266. * DESCRIPTION:
  267. *
  268. * RETURNS:
  269. *
  270. */
  271. template <class EntryData>
  272. EntryData* TSTable<EntryData>::Lock(DWORD_PTR dwID,
  273. DWORD timeout)
  274. {
  275. EntryData* pEntryData = NULL;
  276. WORD wIndex,
  277. wUID;
  278. BreakID(dwID, &wIndex, &wUID);
  279. // Lock the table
  280. LockTable();
  281. // Verify the index is within bounds
  282. if (wIndex >= wSize)
  283. {
  284. goto EXIT;
  285. }
  286. // Verify that the entry is actually valid (ie the lock in non-NULL,
  287. // the object status is valid, and the unique ID matches).
  288. if (pLockTable[wIndex].hLock == NULL ||
  289. pLockTable[wIndex].bDeleted == TRUE ||
  290. pLockTable[wIndex].wUniqueID != wUID)
  291. {
  292. goto EXIT;
  293. }
  294. // If the timeout is INFINITE, then try to lock the entry using a more
  295. // "thread friendly" method. If a timeout is specified, then don't do
  296. // the spin lock since it could be implemented at a higher level.
  297. if(timeout == INFINITE)
  298. {
  299. // simulate infinity with a pseudo "spin lock"
  300. // This is more "thread friendly" in that it unlocks the table allowing some
  301. // other thread that is trying to unlock the same entry to be able to lock the
  302. // table.
  303. while(LockEntry(wIndex, 0) == FALSE)
  304. {
  305. UnLockTable();
  306. // give up the rest of this thread quantum, allowing others to run and potentially
  307. // unlock the entry
  308. Sleep(0);
  309. LockTable();
  310. // If the entry has been replaced, deleted or marked for deletion then
  311. // bag it (give up)
  312. if((pLockTable[wIndex].wUniqueID != wUID) ||
  313. (pLockTable[wIndex].hLock == NULL) ||
  314. (pLockTable[wIndex].bDeleted == TRUE))
  315. {
  316. goto EXIT;
  317. }
  318. }
  319. // we got the lock
  320. pEntryData = pDataTable[wIndex];
  321. }
  322. // Otherwise, do a normal lock
  323. else
  324. {
  325. if (LockEntry(wIndex, timeout) == TRUE)
  326. {
  327. pEntryData = pDataTable[wIndex];
  328. }
  329. }
  330. EXIT:
  331. // Unlock the table
  332. UnLockTable();
  333. return pEntryData;
  334. }
  335. /*
  336. ** TSTable::Unlock
  337. *
  338. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  339. *
  340. * PARAMETERS:
  341. *
  342. * DESCRIPTION:
  343. *
  344. * RETURNS:
  345. *
  346. */
  347. template <class EntryData>
  348. BOOL TSTable<EntryData>::Unlock(DWORD_PTR dwID)
  349. {
  350. BOOL bRetCode = TRUE;
  351. WORD wIndex,
  352. wUID;
  353. BreakID(dwID, &wIndex, &wUID);
  354. // Lock the table
  355. LockTable();
  356. // Verify the id is within bounds
  357. if (wIndex >= wSize)
  358. {
  359. bRetCode = FALSE;
  360. goto EXIT;
  361. }
  362. // verify that the UID matches
  363. if (pLockTable[wIndex].wUniqueID != wUID)
  364. {
  365. bRetCode = FALSE;
  366. goto EXIT;
  367. }
  368. // Verify that the lock is actually valid and that the entry has not been
  369. // deleted
  370. if (pLockTable[wIndex].hLock == NULL)
  371. {
  372. goto EXIT;
  373. }
  374. // Make sure that that thread has the lock on the entry
  375. if ((bRetCode = LockEntry(wIndex, 0)) == TRUE)
  376. {
  377. // if this table entry is marked for delete and the lock count is less than 2
  378. // (since the thread could have called delete after unlocking the entry...although
  379. // this is a no-no) then clean up the table entry
  380. if (pLockTable[wIndex].bDeleted == TRUE &&
  381. pLockTable[wIndex].iLockCount <= 2)
  382. {
  383. // If the caller specifed cleanup on delete, then get rid of memory
  384. if (pLockTable[wIndex].bCleanup == TRUE)
  385. {
  386. delete pDataTable[wIndex];
  387. }
  388. // Set the pointer to NULL
  389. pDataTable[wIndex] = NULL;
  390. // Decrement the count of used entries
  391. wNumUsed--;
  392. // Fix the entry so that it's next free index is what is currently
  393. // the next free pointed to by the current last free entry.
  394. // Then update the last free entry's next pointer, and finally,
  395. // update the last free index to this entry
  396. pLockTable[wIndex].wNextFree = pLockTable[wLastFree].wNextFree;
  397. pLockTable[wLastFree].wNextFree = wIndex;
  398. wLastFree = wIndex;
  399. }
  400. // Do two unlocks on the entry ... one for the original lock and another for
  401. // the lock we obtained during the test
  402. UnLockEntry(wIndex);
  403. UnLockEntry(wIndex);
  404. // Since the entire table is locked, then we can get away with this. If
  405. // the code is ever changed so that the entire table is not locked during
  406. // these operations, then this will cause a race condition.
  407. // If we got rid of the data, then close the handle to the mutex and
  408. // set the handle to NULL
  409. if (pDataTable[wIndex] == NULL)
  410. {
  411. CloseHandle(pLockTable[wIndex].hLock);
  412. pLockTable[wIndex].hLock = NULL;
  413. }
  414. }
  415. EXIT:
  416. // Unlock the table
  417. UnLockTable();
  418. return bRetCode;
  419. }
  420. /*
  421. ** TSTable::Delete
  422. *
  423. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  424. *
  425. * PARAMETERS:
  426. *
  427. * DESCRIPTION:
  428. *
  429. * RETURNS:
  430. *
  431. */
  432. template <class EntryData>
  433. BOOL TSTable<EntryData>::Delete(DWORD_PTR dwID,
  434. BOOL bCleanup)
  435. {
  436. BOOL bRetCode = TRUE;
  437. WORD wIndex,
  438. wUID;
  439. BreakID(dwID, &wIndex, &wUID);
  440. // Lock the table
  441. LockTable();
  442. // Verify that the ID is within bounds
  443. if (wIndex >= wSize)
  444. {
  445. bRetCode = FALSE;
  446. goto EXIT;
  447. }
  448. // verify that the UID matches
  449. if (pLockTable[wIndex].wUniqueID != wUID)
  450. {
  451. bRetCode = FALSE;
  452. goto EXIT;
  453. }
  454. // Verify that the entry is valid
  455. if (pDataTable[wIndex] == NULL)
  456. {
  457. bRetCode = FALSE;
  458. goto EXIT;
  459. }
  460. // Try to lock the entry (ie check to see if we had the entry locked)
  461. if (LockEntry(wIndex, 0) == TRUE)
  462. {
  463. // mark it for deletion, set the cleanp flag and then unlock it
  464. pLockTable[wIndex].bDeleted = TRUE;
  465. pLockTable[wIndex].bCleanup = bCleanup;
  466. UnLockEntry(wIndex);
  467. // Note: this function does not call ::Unlock() on behalf of the user.
  468. // Thus, the entry is only marked as deleted at this point and can no
  469. // longer be locked by any threads (including the one that marked it for delete).
  470. // The thread that marked the entry as deleted must call ::Unlock() to actually
  471. // free up the entry.
  472. }
  473. EXIT:
  474. // Unlock the table
  475. UnLockTable();
  476. return bRetCode;
  477. }
  478. /*
  479. ** TSTable::Lock
  480. *
  481. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  482. *
  483. * PARAMETERS:
  484. *
  485. * DESCRIPTION: Validates that an object still exists. Can be called
  486. * regardless if caller has entry locked or not.
  487. *
  488. * RETURNS:
  489. *
  490. */
  491. template <class EntryData>
  492. BOOL TSTable<EntryData>::Validate(DWORD_PTR dwID)
  493. {
  494. BOOL bRetCode = TRUE;
  495. WORD wIndex,
  496. wUID;
  497. BreakID(dwID, &wIndex, &wUID);
  498. // Lock the table
  499. LockTable();
  500. // Verify the index is within bounds
  501. if (wIndex >= wSize)
  502. {
  503. bRetCode = FALSE;
  504. goto EXIT;
  505. }
  506. // Verify that the entry is actually valid (ie the lock in non-NULL,
  507. // the object status is valid, the unique ID matches, and the data ptr is not null).
  508. if (pLockTable[wIndex].hLock == NULL ||
  509. pLockTable[wIndex].bDeleted == TRUE ||
  510. pLockTable[wIndex].wUniqueID != wUID ||
  511. pDataTable[wIndex] == NULL)
  512. {
  513. bRetCode = FALSE;
  514. goto EXIT;
  515. }
  516. EXIT:
  517. // Unlock the table
  518. UnLockTable();
  519. return bRetCode;
  520. }
  521. /*
  522. ** TSTable::EnumerateEntries
  523. *
  524. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  525. *
  526. * PARAMETERS:
  527. *
  528. * DESCRIPTION:
  529. *
  530. * RETURNS:
  531. *
  532. */
  533. template <class EntryData>
  534. EntryData* TSTable<EntryData>::EnumerateEntries(TABLE_CALLBACK callbackFunc,
  535. LPVOID context,
  536. BOOL bUnlockTable)
  537. {
  538. DWORD dwAction;
  539. WORD wIndex = wSize;
  540. EntryData* pEntryData = NULL;
  541. DWORD dwEntryID;
  542. // Make sure they passed a good function
  543. if (callbackFunc == NULL)
  544. {
  545. goto EXIT;
  546. }
  547. // Lock the table
  548. LockTable();
  549. // Run through the data table and pass the data to the callback function
  550. for (wIndex = 0; wIndex < wSize; wIndex++)
  551. {
  552. // Verify that there is actually data in the entry and that the entry has not
  553. // been marked for deletion.
  554. if (pDataTable[wIndex] == NULL ||
  555. pLockTable[wIndex].bDeleted == TRUE)
  556. {
  557. continue;
  558. }
  559. // Try to lock the entry...if we cannot, then we don't have the lock and
  560. // we will only report entries that we have locked (or are unlocked)
  561. if (LockEntry(wIndex, 0) == FALSE)
  562. {
  563. continue;
  564. }
  565. // build and remember the "full" entry ID so we can use it to unlock the entry
  566. dwEntryID = MakeID(wIndex, pLockTable[wIndex].wUniqueID);
  567. // Save the pointer to the object.
  568. pEntryData = pDataTable[wIndex];
  569. // note: only unlock the table during the callback if we are explicitly asked to (the
  570. // default is not to unlock the table).
  571. if(bUnlockTable == TRUE)
  572. UnLockTable();
  573. // Call their function
  574. dwAction = callbackFunc(pDataTable[wIndex], context);
  575. if(bUnlockTable == TRUE)
  576. LockTable();
  577. // If the action says to delete the entry, then do so...if we are also to delete
  578. // the object, pass in a TRUE.
  579. if (dwAction == CALLBACK_DELETE_ENTRY ||
  580. dwAction == CALLBACK_DELETE_ENTRY_AND_OBJECT)
  581. {
  582. Delete(dwEntryID, (dwAction == CALLBACK_DELETE_ENTRY ? FALSE : TRUE));
  583. }
  584. // If the action says abort, then break the loop...notice that means that
  585. // the entry is still locked
  586. else if (dwAction == CALLBACK_ABORT)
  587. {
  588. goto EXIT;
  589. }
  590. // Unlock the entry...notice we don't use UnLockEntry. The reason is that
  591. // if the entry has been marked as deleted, then we need to have
  592. // it destroyed and UnLockEntry doesn't do that.
  593. Unlock(dwEntryID);
  594. }
  595. EXIT:
  596. // Unlock the table
  597. UnLockTable();
  598. // Return NULL if we processed the entire table...if we were told to abort,
  599. // return a pointer to the entry we stopped on.
  600. return (wIndex == wSize ? NULL : pEntryData);
  601. }
  602. // helper functions - these assume table is locked and index is good
  603. /*
  604. ** TSTable::LockEntry
  605. *
  606. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  607. *
  608. * PARAMETERS:
  609. *
  610. * DESCRIPTION:
  611. *
  612. * RETURNS:
  613. *
  614. */
  615. template <class EntryData>
  616. BOOL TSTable<EntryData>::LockEntry(WORD wIndex,
  617. DWORD timeout)
  618. {
  619. BOOL bRetCode = TRUE;
  620. DWORD dwRetCode;
  621. // Try to lock the entry. If it succeeds, we'll bump up the lock count. If
  622. // the wait ended because another thread abandoned the mutex, then set the count
  623. // to one.
  624. dwRetCode = WaitForSingleObject(pLockTable[wIndex].hLock, timeout);
  625. if (dwRetCode == WAIT_OBJECT_0)
  626. {
  627. pLockTable[wIndex].iLockCount++;
  628. }
  629. else if (dwRetCode == WAIT_ABANDONED)
  630. {
  631. pLockTable[wIndex].iLockCount = 1;
  632. }
  633. else
  634. {
  635. bRetCode = FALSE;
  636. }
  637. return bRetCode;
  638. }
  639. /*
  640. ** TSTable::UnLockEntry
  641. *
  642. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  643. *
  644. * PARAMETERS:
  645. *
  646. * DESCRIPTION:
  647. *
  648. * RETURNS:
  649. *
  650. */
  651. template <class EntryData>
  652. BOOL TSTable<EntryData>::UnLockEntry(WORD wIndex)
  653. {
  654. BOOL bRetCode;
  655. // Release the mutex...if that succeeds, reduce the count
  656. if((bRetCode = ReleaseMutex(pLockTable[wIndex].hLock)) == TRUE)
  657. {
  658. pLockTable[wIndex].iLockCount--;
  659. }
  660. return bRetCode;
  661. }
  662. /*
  663. ** TSTable::GenerateUniqueID
  664. *
  665. * FILENAME: c:\msdev\projects\firewalls\inc\table.h
  666. *
  667. * PARAMETERS:
  668. *
  669. * DESCRIPTION: table should be locked before calling this function.
  670. *
  671. * RETURNS:
  672. *
  673. */
  674. template <class EntryData>
  675. WORD TSTable<EntryData>::GenerateUniqueID()
  676. {
  677. // table must be locked
  678. if(++wUniqueID == TSTABLE_INVALID_UNIQUE_ID)
  679. wUniqueID++;
  680. return(wUniqueID);
  681. }
  682. #endif
  683.