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.

1839 lines
60 KiB

  1. //+-------------------------------------------------------------------
  2. //
  3. // File: RWLock.cxx
  4. //
  5. // Contents: Reader writer lock implementation that supports the
  6. // following features
  7. // 1. Cheap enough to be used in large numbers
  8. // such as per object synchronization.
  9. // 2. Supports timeout. This is a valuable feature
  10. // to detect deadlocks
  11. // 3. Supports caching of events. This allows
  12. // the events to be moved from least contentious
  13. // regions to the most contentious regions.
  14. // In other words, the number of events needed by
  15. // Reader-Writer lockls is bounded by the number
  16. // of threads in the process.
  17. // 4. Supports nested locks by readers and writers
  18. // 5. Supports spin counts for avoiding context switches
  19. // on multi processor machines.
  20. // 6. Supports functionality for upgrading to a writer
  21. // lock with a return argument that indicates
  22. // intermediate writes. Downgrading from a writer
  23. // lock restores the state of the lock.
  24. // 7. Supports functionality to Release Lock for calling
  25. // app code. RestoreLock restores the lock state and
  26. // indicates intermediate writes.
  27. // 8. Recovers from most common failures such as creation of
  28. // events. In other words, the lock mainitains consistent
  29. // internal state and remains usable
  30. //
  31. //
  32. // Classes: CRWLock
  33. // CStaticRWLock
  34. //
  35. // History: 19-Aug-98 Gopalk Created
  36. //
  37. //--------------------------------------------------------------------
  38. #include <ole2int.h>
  39. #include "RWLock.hxx"
  40. // Reader increment
  41. #define READER 0x00000001
  42. // Max number of readers
  43. #define READERS_MASK 0x000003FF
  44. // Reader being signaled
  45. #define READER_SIGNALED 0x00000400
  46. // Writer being signaled
  47. #define WRITER_SIGNALED 0x00000800
  48. #define WRITER 0x00001000
  49. // Waiting reader increment
  50. #define WAITING_READER 0x00002000
  51. // Note size of waiting readers must be less
  52. // than or equal to size of readers
  53. #define WAITING_READERS_MASK 0x007FE000
  54. #define WAITING_READERS_SHIFT 13
  55. // Waiting writer increment
  56. #define WAITING_WRITER 0x00800000
  57. // Max number of waiting writers
  58. #define WAITING_WRITERS_MASK 0xFF800000
  59. // Events are being cached
  60. #define CACHING_EVENTS (READER_SIGNALED | WRITER_SIGNALED)
  61. // Reader lock was upgraded
  62. #define INVALID_COOKIE 0x01
  63. #define UPGRADE_COOKIE 0x02
  64. #define RELEASE_COOKIE 0x04
  65. #define COOKIE_NONE 0x10
  66. #define COOKIE_WRITER 0x20
  67. #define COOKIE_READER 0x40
  68. DWORD gdwDefaultTimeout = INFINITE;
  69. DWORD gdwDefaultSpinCount = 0;
  70. DWORD gdwNumberOfProcessors = 1;
  71. DWORD gdwLockSeqNum = 0;
  72. BOOL fBreakOnErrors = FALSE;
  73. const DWORD gdwReasonableTimeout = 120000;
  74. const DWORD gdwMaxReaders = READERS_MASK;
  75. const DWORD gdwMaxWaitingReaders = (WAITING_READERS_MASK >> WAITING_READERS_SHIFT);
  76. #ifdef __NOOLETLS__
  77. DWORD gLockTlsIdx;
  78. #endif
  79. #define RWLOCK_FATALFAILURE 1000
  80. #define HEAP_SERIALIZE 0
  81. //+-------------------------------------------------------------------
  82. //
  83. // Method: CRWLock::InitDefaults public
  84. //
  85. // Synopsis: Reads default values from registry
  86. //
  87. // History: 21-Aug-98 Gopalk Created
  88. //
  89. //+-------------------------------------------------------------------
  90. void CRWLock::InitDefaults()
  91. {
  92. SYSTEM_INFO system;
  93. // Obtain number of processors on the system
  94. GetSystemInfo(&system);
  95. gdwNumberOfProcessors = system.dwNumberOfProcessors;
  96. gdwDefaultSpinCount = (gdwNumberOfProcessors > 1) ? 500 : 0;
  97. // Obtain system wide timeout value
  98. HKEY hKey;
  99. LONG lRetVal = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
  100. "SYSTEM\\CurrentControlSet\\Control\\Session Manager",
  101. NULL,
  102. KEY_READ,
  103. &hKey);
  104. if(lRetVal == ERROR_SUCCESS)
  105. {
  106. DWORD dwTimeout, dwSize = sizeof(dwTimeout);
  107. lRetVal = RegQueryValueExA(hKey,
  108. "CriticalSectionTimeout",
  109. NULL,
  110. NULL,
  111. (LPBYTE) &dwTimeout,
  112. &dwSize);
  113. if(lRetVal == ERROR_SUCCESS)
  114. {
  115. gdwDefaultTimeout = dwTimeout * 2000;
  116. }
  117. RegCloseKey(hKey);
  118. }
  119. return;
  120. }
  121. //+-------------------------------------------------------------------
  122. //
  123. // Method: CRWLock::Cleanup public
  124. //
  125. // Synopsis: Cleansup state
  126. //
  127. // History: 21-Aug-98 Gopalk Created
  128. //
  129. //+-------------------------------------------------------------------
  130. void CRWLock::Cleanup()
  131. {
  132. #if DBG==1
  133. if (g_fDllState != DLL_STATE_PROCESS_DETACH)
  134. {
  135. // Perform sanity checks if we're not shutting down
  136. Win4Assert(_dwState == 0);
  137. Win4Assert(_dwWriterID == 0);
  138. Win4Assert(_wWriterLevel == 0);
  139. }
  140. #endif
  141. if(_hWriterEvent)
  142. CloseHandle(_hWriterEvent);
  143. if(_hReaderEvent)
  144. CloseHandle(_hReaderEvent);
  145. #if LOCK_PERF==1
  146. gLockTracker.ReportContention(this,
  147. _dwWriterEntryCount,
  148. _dwWriterContentionCount,
  149. _dwReaderEntryCount,
  150. _dwReaderContentionCount);
  151. #endif
  152. return;
  153. }
  154. //+-------------------------------------------------------------------
  155. //
  156. // Method: CRWLock::AssertWriterLockHeld public
  157. //
  158. // Synopsis: Asserts that writer lock is held
  159. //
  160. // History: 21-Aug-98 Gopalk Created
  161. //
  162. //+-------------------------------------------------------------------
  163. #if DBG==1
  164. BOOL CRWLock::AssertWriterLockHeld()
  165. {
  166. DWORD dwThreadID = GetCurrentThreadId();
  167. if(_dwWriterID != dwThreadID)
  168. Win4Assert(!"Writer lock not held by the current thread");
  169. return(_dwWriterID == dwThreadID);
  170. }
  171. #endif
  172. //+-------------------------------------------------------------------
  173. //
  174. // Method: CRWLock::AssertWriterLockNotHeld public
  175. //
  176. // Synopsis: Asserts that writer lock is not held
  177. //
  178. // History: 21-Aug-98 Gopalk Created
  179. //
  180. //+-------------------------------------------------------------------
  181. #if DBG==1
  182. BOOL CRWLock::AssertWriterLockNotHeld()
  183. {
  184. DWORD dwThreadID = GetCurrentThreadId();
  185. if(_dwWriterID == dwThreadID)
  186. Win4Assert(!"Writer lock held by the current thread");
  187. return(_dwWriterID != dwThreadID);
  188. }
  189. #endif
  190. //+-------------------------------------------------------------------
  191. //
  192. // Method: CRWLock::AssertReaderLockHeld public
  193. //
  194. // Synopsis: Asserts that reader lock is held
  195. //
  196. // History: 21-Aug-98 Gopalk Created
  197. //
  198. //+-------------------------------------------------------------------
  199. #if DBG==1
  200. BOOL CRWLock::AssertReaderLockHeld()
  201. {
  202. HRESULT hr;
  203. WORD *pwReaderLevel;
  204. BOOL fLockHeld = FALSE;
  205. hr = GetTLSLockData(&pwReaderLevel);
  206. if((hr == S_OK) && (*pwReaderLevel != 0))
  207. fLockHeld = TRUE;
  208. if(fLockHeld == FALSE)
  209. Win4Assert(!"Reader lock not held by the current thread");
  210. return(fLockHeld);
  211. }
  212. #endif
  213. //+-------------------------------------------------------------------
  214. //
  215. // Method: CRWLock::AssertReaderLockNotHeld public
  216. //
  217. // Synopsis: Asserts that writer lock is not held
  218. //
  219. // History: 21-Aug-98 Gopalk Created
  220. //
  221. //+-------------------------------------------------------------------
  222. #if DBG==1
  223. BOOL CRWLock::AssertReaderLockNotHeld()
  224. {
  225. HRESULT hr;
  226. WORD *pwReaderLevel;
  227. BOOL fLockHeld = FALSE;
  228. hr = GetTLSLockData(&pwReaderLevel);
  229. if((hr == S_OK) && (*pwReaderLevel != 0))
  230. fLockHeld = TRUE;
  231. if(fLockHeld == TRUE)
  232. Win4Assert(!"Reader lock held by the current thread");
  233. return(fLockHeld == FALSE);
  234. }
  235. #endif
  236. //+-------------------------------------------------------------------
  237. //
  238. // Method: CRWLock::AssertReaderOrWriterLockHeld public
  239. //
  240. // Synopsis: Asserts that writer lock is not held
  241. //
  242. // History: 21-Aug-98 Gopalk Created
  243. //
  244. //+-------------------------------------------------------------------
  245. #if DBG==1
  246. BOOL CRWLock::AssertReaderOrWriterLockHeld()
  247. {
  248. BOOL fLockHeld;
  249. if(_dwWriterID == GetCurrentThreadId())
  250. {
  251. fLockHeld = TRUE;
  252. }
  253. else
  254. {
  255. HRESULT hr;
  256. WORD *pwReaderLevel;
  257. hr = GetTLSLockData(&pwReaderLevel);
  258. if((hr == S_OK) && (*pwReaderLevel != 0))
  259. fLockHeld = TRUE;
  260. }
  261. Win4Assert(fLockHeld && "Neither Reader nor Writer lock held");
  262. return(fLockHeld);
  263. }
  264. #endif
  265. //+-------------------------------------------------------------------
  266. //
  267. // Method: CRWLock::ModifyState public
  268. //
  269. // Synopsis: Helper function for updating the state inside the lock
  270. //
  271. // History: 21-Aug-98 Gopalk Created
  272. //
  273. //+-------------------------------------------------------------------
  274. inline DWORD CRWLock::ModifyState(DWORD dwModifyState)
  275. {
  276. return(InterlockedExchangeAdd((LONG *) &_dwState, dwModifyState));
  277. }
  278. //+-------------------------------------------------------------------
  279. //
  280. // Method: CRWLock::RWSetEvent public
  281. //
  282. // Synopsis: Helper function for setting an event
  283. //
  284. // History: 21-Aug-98 Gopalk Created
  285. //
  286. //+-------------------------------------------------------------------
  287. inline void CRWLock::RWSetEvent(HANDLE event)
  288. {
  289. if(!SetEvent(event))
  290. {
  291. Win4Assert(!"SetEvent failed");
  292. if(fBreakOnErrors)
  293. DebugBreak();
  294. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  295. }
  296. }
  297. //+-------------------------------------------------------------------
  298. //
  299. // Method: CRWLock::RWResetEvent public
  300. //
  301. // Synopsis: Helper function for resetting an event
  302. //
  303. // History: 21-Aug-98 Gopalk Created
  304. //
  305. //+-------------------------------------------------------------------
  306. inline void CRWLock::RWResetEvent(HANDLE event)
  307. {
  308. if(!ResetEvent(event))
  309. {
  310. Win4Assert(!"ResetEvent failed");
  311. if(fBreakOnErrors)
  312. DebugBreak();
  313. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  314. }
  315. }
  316. //+-------------------------------------------------------------------
  317. //
  318. // Method: CRWLock::RWSleep public
  319. //
  320. // Synopsis: Helper function for calling Sleep. Useful for debugging
  321. //
  322. // History: 21-Aug-98 Gopalk Created
  323. //
  324. //+-------------------------------------------------------------------
  325. inline void CRWLock::RWSleep(DWORD dwTime)
  326. {
  327. Sleep(dwTime);
  328. }
  329. //+-------------------------------------------------------------------
  330. //
  331. // Method: CRWLock::ReleaseEvents public
  332. //
  333. // Synopsis: Helper function for caching events
  334. //
  335. // History: 21-Aug-98 Gopalk Created
  336. //
  337. //+-------------------------------------------------------------------
  338. #ifdef RWLOCK_FULL_FUNCTIONALITY
  339. void CRWLock::ReleaseEvents()
  340. {
  341. // Sanity check
  342. Win4Assert(_wFlags & RWLOCKFLAG_CACHEEVENTS);
  343. // Ensure that reader and writers have been stalled
  344. Win4Assert((_dwState & CACHING_EVENTS) == CACHING_EVENTS);
  345. // Save writer event
  346. HANDLE hWriterEvent = _hWriterEvent;
  347. _hWriterEvent = NULL;
  348. // Save reader event
  349. HANDLE hReaderEvent = _hReaderEvent;
  350. _hReaderEvent = NULL;
  351. // Allow readers and writers to continue
  352. ModifyState(-(CACHING_EVENTS));
  353. // Cache events
  354. // REVIEW: I am closing events for now. What is needed
  355. // is an event cache to which the events are
  356. // released using InterlockedCompareExchange64
  357. if(hWriterEvent)
  358. {
  359. ComDebOut((DEB_TRACE, "Releasing writer event\n"));
  360. CloseHandle(hWriterEvent);
  361. }
  362. if(hReaderEvent)
  363. {
  364. ComDebOut((DEB_TRACE, "Releasing reader event\n"));
  365. CloseHandle(hReaderEvent);
  366. }
  367. return;
  368. }
  369. #endif
  370. //+-------------------------------------------------------------------
  371. //
  372. // Method: CRWLock::GetWriterEvent public
  373. //
  374. // Synopsis: Helper function for obtaining a auto reset event used
  375. // for serializing writers. It utilizes event cache
  376. //
  377. // History: 21-Aug-98 Gopalk Created
  378. //
  379. //+-------------------------------------------------------------------
  380. HANDLE CRWLock::GetWriterEvent()
  381. {
  382. if(_hWriterEvent == NULL)
  383. {
  384. HANDLE hWriterEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  385. if(hWriterEvent)
  386. {
  387. if(InterlockedCompareExchangePointer(&_hWriterEvent, hWriterEvent, NULL))
  388. {
  389. CloseHandle(hWriterEvent);
  390. }
  391. }
  392. }
  393. return(_hWriterEvent);
  394. }
  395. //+-------------------------------------------------------------------
  396. //
  397. // Method: CRWLock::GetReaderEvent public
  398. //
  399. // Synopsis: Helper function for obtaining a manula reset event used
  400. // by readers to wait when a writer holds the lock.
  401. // It utilizes event cache
  402. //
  403. // History: 21-Aug-98 Gopalk Created
  404. //
  405. //+-------------------------------------------------------------------
  406. HANDLE CRWLock::GetReaderEvent()
  407. {
  408. if(_hReaderEvent == NULL)
  409. {
  410. HANDLE hReaderEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  411. if(hReaderEvent)
  412. {
  413. if(InterlockedCompareExchangePointer(&_hReaderEvent, hReaderEvent, NULL))
  414. {
  415. CloseHandle(hReaderEvent);
  416. }
  417. }
  418. }
  419. return(_hReaderEvent);
  420. }
  421. //+-------------------------------------------------------------------
  422. //
  423. // Method: CRWLock::AcquireReaderLock public
  424. //
  425. // Synopsis: Makes the thread a reader. Supports nested reader locks.
  426. //
  427. // History: 21-Aug-98 Gopalk Created
  428. //
  429. //+-------------------------------------------------------------------
  430. HRESULT CRWLock::AcquireReaderLock(
  431. #ifdef RWLOCK_FULL_FUNCTIONALITY
  432. BOOL fReturnErrors,
  433. DWORD dwDesiredTimeout
  434. #if LOCK_PERF==1
  435. ,
  436. #endif
  437. #endif
  438. #if LOCK_PERF==1
  439. const char *pszFile,
  440. DWORD dwLine,
  441. const char *pszLockName
  442. #endif
  443. )
  444. {
  445. HRESULT hr;
  446. #ifndef RWLOCK_FULL_FUNCTIONALITY
  447. DWORD dwDesiredTimeout = gdwDefaultTimeout;
  448. #endif
  449. // Ensure that the lock was initialized
  450. if(!IsInitialized())
  451. Initialize();
  452. // Check if the thread already has writer lock
  453. if(_dwWriterID == GetCurrentThreadId())
  454. {
  455. hr = AcquireWriterLock();
  456. }
  457. else
  458. {
  459. WORD *pwReaderLevel;
  460. hr = GetTLSLockData(&pwReaderLevel);
  461. if(SUCCEEDED(hr))
  462. {
  463. if(*pwReaderLevel != 0)
  464. {
  465. ++(*pwReaderLevel);
  466. }
  467. else
  468. {
  469. DWORD dwCurrentState, dwKnownState;
  470. DWORD dwSpinCount;
  471. // Initialize
  472. hr = S_OK;
  473. dwSpinCount = 0;
  474. dwCurrentState = _dwState;
  475. do
  476. {
  477. dwKnownState = dwCurrentState;
  478. // Reader need not wait if there are only readers and no writer
  479. if((dwKnownState < READERS_MASK) ||
  480. (((dwKnownState & READER_SIGNALED) && ((dwKnownState & WRITER) == 0)) &&
  481. (((dwKnownState & READERS_MASK) +
  482. ((dwKnownState & WAITING_READERS_MASK) >> WAITING_READERS_SHIFT)) <=
  483. (READERS_MASK - 2))))
  484. {
  485. // Add to readers
  486. dwCurrentState = InterlockedCompareExchange((LONG *) &_dwState,
  487. (dwKnownState + READER),
  488. dwKnownState);
  489. if(dwCurrentState == dwKnownState)
  490. {
  491. // One more reader
  492. break;
  493. }
  494. }
  495. // Check for too many Readers, or waiting readers
  496. else if(((dwKnownState & READERS_MASK) == READERS_MASK) ||
  497. ((dwKnownState & WAITING_READERS_MASK) == WAITING_READERS_MASK) ||
  498. ((dwKnownState & CACHING_EVENTS) == READER_SIGNALED))
  499. {
  500. RWSleep(1000);
  501. dwSpinCount = 0;
  502. dwCurrentState = _dwState;
  503. }
  504. // Check if events are being cached
  505. #ifdef RWLOCK_FULL_FUNCTIONALITY
  506. else if((dwKnownState & CACHING_EVENTS) == CACHING_EVENTS)
  507. {
  508. if(++dwSpinCount > gdwDefaultSpinCount)
  509. {
  510. RWSleep(10);
  511. dwSpinCount = 0;
  512. }
  513. dwCurrentState = _dwState;
  514. }
  515. #endif
  516. // Check spin count
  517. else if(++dwSpinCount > gdwDefaultSpinCount)
  518. {
  519. // Add to waiting readers
  520. dwCurrentState = InterlockedCompareExchange((LONG *) &_dwState,
  521. (dwKnownState + WAITING_READER),
  522. dwKnownState);
  523. if(dwCurrentState == dwKnownState)
  524. {
  525. HANDLE hReaderEvent;
  526. DWORD dwStatus;
  527. DWORD dwModifyState;
  528. // One more waiting reader
  529. #ifdef RWLOCK_STATISTICS
  530. InterlockedIncrement((LONG *) &_dwReaderContentionCount);
  531. #endif
  532. #if LOCK_PERF==1
  533. gLockTracker.ReaderWaiting(pszFile, dwLine, pszLockName, this);
  534. #endif
  535. hReaderEvent = GetReaderEvent();
  536. if(hReaderEvent)
  537. {
  538. dwStatus = WaitForSingleObject(hReaderEvent, dwDesiredTimeout);
  539. }
  540. else
  541. {
  542. ComDebOut((DEB_WARN,
  543. "AcquireReaderLock failed to create reader "
  544. "event for RWLock 0x%x\n", this));
  545. dwStatus = WAIT_FAILED;
  546. hr = RPC_E_OUT_OF_RESOURCES;
  547. }
  548. if(dwStatus == WAIT_OBJECT_0)
  549. {
  550. Win4Assert(_dwState & READER_SIGNALED);
  551. Win4Assert((_dwState & READERS_MASK) < READERS_MASK);
  552. dwModifyState = READER - WAITING_READER;
  553. }
  554. else
  555. {
  556. dwModifyState = -WAITING_READER;
  557. if(dwStatus == WAIT_TIMEOUT)
  558. {
  559. ComDebOut((DEB_WARN,
  560. "Timed out trying to acquire reader lock "
  561. "for RWLock 0x%x\n", this));
  562. hr = RPC_E_TIMEOUT;
  563. }
  564. else if(SUCCEEDED(hr))
  565. {
  566. ComDebOut((DEB_ERROR,
  567. "WaitForSingleObject Failed for "
  568. "RWLock 0x%x\n", this));
  569. hr = HRESULT_FROM_WIN32(GetLastError());
  570. }
  571. }
  572. // One less waiting reader and he may have become a reader
  573. dwKnownState = ModifyState(dwModifyState);
  574. // Check for last signaled waiting reader
  575. if(((dwKnownState & WAITING_READERS_MASK) == WAITING_READER) &&
  576. (dwKnownState & READER_SIGNALED))
  577. {
  578. dwModifyState = -READER_SIGNALED;
  579. if(dwStatus != WAIT_OBJECT_0)
  580. {
  581. if(hReaderEvent == NULL)
  582. hReaderEvent = GetReaderEvent();
  583. Win4Assert(hReaderEvent);
  584. dwStatus = WaitForSingleObject(hReaderEvent, INFINITE);
  585. Win4Assert(dwStatus == WAIT_OBJECT_0);
  586. Win4Assert((_dwState & READERS_MASK) < READERS_MASK);
  587. dwModifyState += READER;
  588. hr = S_OK;
  589. }
  590. RWResetEvent(hReaderEvent);
  591. dwKnownState = ModifyState(dwModifyState);
  592. }
  593. // Check if the thread became a reader
  594. if(dwStatus == WAIT_OBJECT_0)
  595. {
  596. break;
  597. }
  598. }
  599. }
  600. else
  601. {
  602. dwCurrentState = _dwState;
  603. }
  604. } while(SUCCEEDED(hr));
  605. // Sanity checks
  606. if(SUCCEEDED(hr))
  607. {
  608. Win4Assert((_dwState & WRITER) == 0);
  609. Win4Assert(_dwState & READERS_MASK);
  610. *pwReaderLevel = 1;
  611. #ifdef RWLOCK_STATISTICS
  612. InterlockedIncrement((LONG *) &_dwReaderEntryCount);
  613. #endif
  614. #if LOCK_PERF==1
  615. gLockTracker.ReaderEntered(pszFile, dwLine, pszLockName, this);
  616. #endif
  617. }
  618. }
  619. }
  620. // Check failure return
  621. #if RWLOCK_FULL_FUNCTIONALITY
  622. if(FAILED(hr) && (fReturnErrors == FALSE))
  623. #else
  624. if(FAILED(hr))
  625. #endif
  626. {
  627. Win4Assert(!"Failed to acquire reader lock");
  628. if(fBreakOnErrors)
  629. DebugBreak();
  630. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  631. }
  632. }
  633. return(hr);
  634. }
  635. //+-------------------------------------------------------------------
  636. //
  637. // Method: CRWLock::AcquireWriterLock public
  638. //
  639. // Synopsis: Makes the thread a writer. Supports nested writer
  640. // locks
  641. //
  642. // History: 21-Aug-98 Gopalk Created
  643. //
  644. //+-------------------------------------------------------------------
  645. HRESULT CRWLock::AcquireWriterLock(
  646. #ifdef RWLOCK_FULL_FUNCTIONALITY
  647. BOOL fReturnErrors,
  648. DWORD dwDesiredTimeout
  649. #if LOCK_PERF==1
  650. ,
  651. #endif
  652. #endif
  653. #if LOCK_PERF==1
  654. const char *pszFile,
  655. DWORD dwLine,
  656. const char *pszLockName
  657. #endif
  658. )
  659. {
  660. HRESULT hr = S_OK;
  661. DWORD dwThreadID = GetCurrentThreadId();
  662. #ifndef RWLOCK_FULL_FUNCTIONALITY
  663. DWORD dwDesiredTimeout = gdwDefaultTimeout;
  664. #endif
  665. // Ensure that the lock was initialized
  666. if(!IsInitialized())
  667. Initialize();
  668. // Check if the thread already has writer lock
  669. if(_dwWriterID == dwThreadID)
  670. {
  671. ++_wWriterLevel;
  672. }
  673. else
  674. {
  675. DWORD dwCurrentState, dwKnownState;
  676. DWORD dwSpinCount = 0;
  677. dwCurrentState = _dwState;
  678. do
  679. {
  680. dwKnownState = dwCurrentState;
  681. // Writer need not wait if there are no readers and writer
  682. if(dwKnownState == 0)
  683. {
  684. // Can be a writer
  685. dwCurrentState = InterlockedCompareExchange((LONG *) &_dwState,
  686. WRITER,
  687. dwKnownState);
  688. if(dwCurrentState == dwKnownState)
  689. {
  690. // Only writer
  691. break;
  692. }
  693. }
  694. // Check for too many waiting writers
  695. else if(((dwKnownState & WAITING_WRITERS_MASK) == WAITING_WRITERS_MASK))
  696. {
  697. RWSleep(1000);
  698. dwSpinCount = 0;
  699. dwCurrentState = _dwState;
  700. }
  701. // Check if events are being cached
  702. #ifdef RWLOCK_FULL_FUNCTIONALITY
  703. else if((dwKnownState & CACHING_EVENTS) == CACHING_EVENTS)
  704. {
  705. if(++dwSpinCount > gdwDefaultSpinCount)
  706. {
  707. RWSleep(10);
  708. dwSpinCount = 0;
  709. }
  710. dwCurrentState = _dwState;
  711. }
  712. #endif
  713. // Check spin count
  714. else if(++dwSpinCount > gdwDefaultSpinCount)
  715. {
  716. // Add to waiting writers
  717. dwCurrentState = InterlockedCompareExchange((LONG *) &_dwState,
  718. (dwKnownState + WAITING_WRITER),
  719. dwKnownState);
  720. if(dwCurrentState == dwKnownState)
  721. {
  722. HANDLE hWriterEvent;
  723. DWORD dwStatus;
  724. DWORD dwModifyState;
  725. BOOL fLoopback;
  726. // One more waiting writer
  727. #ifdef RWLOCK_STATISTICS
  728. InterlockedIncrement((LONG *) &_dwWriterContentionCount);
  729. #endif
  730. #if LOCK_PERF==1
  731. gLockTracker.WriterWaiting(pszFile, dwLine, pszLockName, this);
  732. #endif
  733. do
  734. {
  735. fLoopback = FALSE;
  736. hr = S_OK;
  737. hWriterEvent = GetWriterEvent();
  738. if(hWriterEvent)
  739. {
  740. dwStatus = WaitForSingleObject(hWriterEvent, dwDesiredTimeout);
  741. }
  742. else
  743. {
  744. ComDebOut((DEB_WARN,
  745. "AcquireWriterLock failed to create writer "
  746. "event for RWLock 0x%x\n", this));
  747. dwStatus = WAIT_FAILED;
  748. hr = RPC_E_OUT_OF_RESOURCES;
  749. }
  750. if(dwStatus == WAIT_OBJECT_0)
  751. {
  752. Win4Assert(_dwState & WRITER_SIGNALED);
  753. dwModifyState = WRITER - WAITING_WRITER - WRITER_SIGNALED;
  754. }
  755. else
  756. {
  757. dwModifyState = -WAITING_WRITER;
  758. if(dwStatus == WAIT_TIMEOUT)
  759. {
  760. ComDebOut((DEB_WARN,
  761. "Timed out trying to acquire writer "
  762. "lock for RWLock 0x%x\n", this));
  763. hr = RPC_E_TIMEOUT;
  764. }
  765. else if(SUCCEEDED(hr))
  766. {
  767. ComDebOut((DEB_ERROR,
  768. "WaitForSingleObject Failed for "
  769. "RWLock 0x%x\n", this));
  770. hr = HRESULT_FROM_WIN32(GetLastError());
  771. }
  772. }
  773. // One less waiting writer and he may have become a writer
  774. dwKnownState = ModifyState(dwModifyState);
  775. // Check for last timing out signaled waiting writer
  776. if((dwStatus != WAIT_OBJECT_0) &&
  777. (dwKnownState & WRITER_SIGNALED) &&
  778. ((dwKnownState & WAITING_WRITERS_MASK) == WAITING_WRITER))
  779. {
  780. fLoopback = TRUE;
  781. dwCurrentState = _dwState;
  782. do
  783. {
  784. dwKnownState = dwCurrentState;
  785. if(((dwKnownState & WAITING_WRITERS_MASK) == 0) &&
  786. (dwKnownState & WRITER_SIGNALED))
  787. {
  788. dwCurrentState = InterlockedCompareExchange((LONG *) &_dwState,
  789. (dwKnownState + WAITING_WRITER),
  790. dwKnownState);
  791. }
  792. else
  793. {
  794. Win4Assert(FAILED(hr));
  795. fLoopback = FALSE;
  796. break;
  797. }
  798. } while(dwCurrentState != dwKnownState);
  799. }
  800. if(fLoopback)
  801. {
  802. ComDebOut((DEB_WARN,
  803. "Retry of timing out writer for RWLock 0x%x\n",
  804. this));
  805. // Reduce the timeout value for retries
  806. dwDesiredTimeout = 100;
  807. }
  808. } while(fLoopback);
  809. // Check if the thread became a writer
  810. if(dwStatus == WAIT_OBJECT_0)
  811. break;
  812. }
  813. }
  814. else
  815. {
  816. dwCurrentState = _dwState;
  817. }
  818. } while(SUCCEEDED(hr));
  819. // Sanity checks
  820. if(SUCCEEDED(hr))
  821. {
  822. Win4Assert(_dwState & WRITER);
  823. Win4Assert((_dwState & WRITER_SIGNALED) == 0);
  824. Win4Assert((_dwState & READERS_MASK) == 0);
  825. Win4Assert(_dwWriterID == 0);
  826. // Save threadid of the writer
  827. _dwWriterID = dwThreadID;
  828. _wWriterLevel = 1;
  829. ++_dwWriterSeqNum;
  830. #ifdef RWLOCK_STATISTICS
  831. ++_dwWriterEntryCount;
  832. #endif
  833. #if LOCK_PERF==1
  834. gLockTracker.WriterEntered(pszFile, dwLine, pszLockName, this);
  835. #endif
  836. }
  837. #ifdef RWLOCK_FULL_FUNCTIONALITY
  838. else if(fReturnErrors == FALSE)
  839. #else
  840. else
  841. #endif
  842. {
  843. Win4Assert(!"Failed to acquire writer lock");
  844. if(fBreakOnErrors)
  845. DebugBreak();
  846. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  847. }
  848. }
  849. return(hr);
  850. }
  851. //+-------------------------------------------------------------------
  852. //
  853. // Method: CRWLock::ReleaseWriterLock public
  854. //
  855. // Synopsis: Removes the thread as a writer if not a nested
  856. // call to release the lock
  857. //
  858. // History: 21-Aug-98 Gopalk Created
  859. //
  860. //+-------------------------------------------------------------------
  861. HRESULT CRWLock::ReleaseWriterLock()
  862. {
  863. HRESULT hr = S_OK;
  864. DWORD dwThreadID = GetCurrentThreadId();
  865. // Check validity of caller
  866. if(_dwWriterID == dwThreadID)
  867. {
  868. // Sanity check
  869. Win4Assert(IsInitialized());
  870. // Check for nested release
  871. if(--_wWriterLevel == 0)
  872. {
  873. DWORD dwCurrentState, dwKnownState, dwModifyState;
  874. BOOL fCacheEvents;
  875. HANDLE hReaderEvent = NULL, hWriterEvent = NULL;
  876. // Not a writer any more
  877. #if LOCK_PERF==1
  878. gLockTracker.WriterLeaving(this);
  879. #endif
  880. _dwWriterID = 0;
  881. dwCurrentState = _dwState;
  882. do
  883. {
  884. dwKnownState = dwCurrentState;
  885. dwModifyState = -WRITER;
  886. fCacheEvents = FALSE;
  887. if(dwKnownState & WAITING_READERS_MASK)
  888. {
  889. hReaderEvent = GetReaderEvent();
  890. if(hReaderEvent == NULL)
  891. {
  892. ComDebOut((DEB_WARN,
  893. "ReleaseWriterLock failed to create "
  894. "reader event for RWLock 0x%x\n", this));
  895. RWSleep(100);
  896. dwCurrentState = _dwState;
  897. dwKnownState = 0;
  898. Win4Assert(dwCurrentState != dwKnownState);
  899. continue;
  900. }
  901. dwModifyState += READER_SIGNALED;
  902. }
  903. else if(dwKnownState & WAITING_WRITERS_MASK)
  904. {
  905. hWriterEvent = GetWriterEvent();
  906. if(hWriterEvent == NULL)
  907. {
  908. ComDebOut((DEB_WARN,
  909. "ReleaseWriterLock failed to create "
  910. "writer event for RWLock 0x%x\n", this));
  911. RWSleep(100);
  912. dwCurrentState = _dwState;
  913. dwKnownState = 0;
  914. Win4Assert(dwCurrentState != dwKnownState);
  915. continue;
  916. }
  917. dwModifyState += WRITER_SIGNALED;
  918. }
  919. #ifdef RWLOCK_FULL_FUNCTIONALITY
  920. else if((_wFlags & RWLOCKFLAG_CACHEEVENTS) &&
  921. (dwKnownState == WRITER) &&
  922. (_hReaderEvent || _hWriterEvent) &&
  923. ((dwKnownState & CACHING_EVENTS) == 0))
  924. {
  925. fCacheEvents = TRUE;
  926. dwModifyState += CACHING_EVENTS;
  927. }
  928. #endif
  929. // Sanity checks
  930. Win4Assert((dwKnownState & CACHING_EVENTS) == 0);
  931. Win4Assert((dwKnownState & READERS_MASK) == 0);
  932. dwCurrentState = InterlockedCompareExchange((LONG *) &_dwState,
  933. (dwKnownState + dwModifyState),
  934. dwKnownState);
  935. } while(dwCurrentState != dwKnownState);
  936. // Check for waiting readers
  937. if(dwKnownState & WAITING_READERS_MASK)
  938. {
  939. Win4Assert(_dwState & READER_SIGNALED);
  940. Win4Assert(hReaderEvent);
  941. RWSetEvent(hReaderEvent);
  942. }
  943. // Check for waiting writers
  944. else if(dwKnownState & WAITING_WRITERS_MASK)
  945. {
  946. Win4Assert(_dwState & WRITER_SIGNALED);
  947. Win4Assert(hWriterEvent);
  948. RWSetEvent(hWriterEvent);
  949. }
  950. #ifdef RWLOCK_FULL_FUNCTIONALITY
  951. // Check for the need to release events
  952. else if(fCacheEvents)
  953. {
  954. ReleaseEvents();
  955. }
  956. #endif
  957. }
  958. }
  959. else
  960. {
  961. hr = HRESULT_FROM_WIN32(ERROR_NOT_OWNER);
  962. Win4Assert(!"Attempt to release writer lock on a wrong thread");
  963. #ifdef RWLOCK_FULL_FUNCTIONALITY
  964. if((_wFlags & RWLOCKFLAG_RETURNERRORS) == 0)
  965. #endif
  966. {
  967. if(fBreakOnErrors)
  968. DebugBreak();
  969. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  970. }
  971. }
  972. return(hr);
  973. }
  974. //+-------------------------------------------------------------------
  975. //
  976. // Method: CRWLock::ReleaseReaderLock public
  977. //
  978. // Synopsis: Removes the thread as a reader
  979. //
  980. // History: 21-Aug-98 Gopalk Created
  981. //
  982. //+-------------------------------------------------------------------
  983. HRESULT CRWLock::ReleaseReaderLock()
  984. {
  985. HRESULT hr = S_OK;
  986. // Check if the thread has writer lock
  987. if(_dwWriterID == GetCurrentThreadId())
  988. {
  989. hr = ReleaseWriterLock();
  990. }
  991. else
  992. {
  993. WORD *pwReaderLevel;
  994. hr = GetTLSLockData(&pwReaderLevel);
  995. if(SUCCEEDED(hr))
  996. {
  997. if(*pwReaderLevel > 1)
  998. {
  999. --(*pwReaderLevel);
  1000. }
  1001. else if(*pwReaderLevel == 1)
  1002. {
  1003. DWORD dwCurrentState, dwKnownState, dwModifyState;
  1004. BOOL fLastReader, fCacheEvents;
  1005. HANDLE hReaderEvent = NULL, hWriterEvent = NULL;
  1006. // Sanity checks
  1007. Win4Assert((_dwState & WRITER) == 0);
  1008. Win4Assert(_dwState & READERS_MASK);
  1009. // Not a reader any more
  1010. *pwReaderLevel = 0;
  1011. dwCurrentState = _dwState;
  1012. do
  1013. {
  1014. dwKnownState = dwCurrentState;
  1015. dwModifyState = -READER;
  1016. if((dwKnownState & (READERS_MASK | READER_SIGNALED)) == READER)
  1017. {
  1018. fLastReader = TRUE;
  1019. fCacheEvents = FALSE;
  1020. if(dwKnownState & WAITING_WRITERS_MASK)
  1021. {
  1022. hWriterEvent = GetWriterEvent();
  1023. if(hWriterEvent == NULL)
  1024. {
  1025. ComDebOut((DEB_WARN,
  1026. "ReleaseReaderLock failed to create "
  1027. "writer event for RWLock 0x%x\n", this));
  1028. RWSleep(100);
  1029. dwCurrentState = _dwState;
  1030. dwKnownState = 0;
  1031. Win4Assert(dwCurrentState != dwKnownState);
  1032. continue;
  1033. }
  1034. dwModifyState += WRITER_SIGNALED;
  1035. }
  1036. else if(dwKnownState & WAITING_READERS_MASK)
  1037. {
  1038. hReaderEvent = GetReaderEvent();
  1039. if(hReaderEvent == NULL)
  1040. {
  1041. ComDebOut((DEB_WARN,
  1042. "ReleaseReaderLock failed to create "
  1043. "reader event\n", this));
  1044. RWSleep(100);
  1045. dwCurrentState = _dwState;
  1046. dwKnownState = 0;
  1047. Win4Assert(dwCurrentState != dwKnownState);
  1048. continue;
  1049. }
  1050. dwModifyState += READER_SIGNALED;
  1051. }
  1052. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1053. else if((_wFlags & RWLOCKFLAG_CACHEEVENTS) &&
  1054. (dwKnownState == READER) &&
  1055. (_hReaderEvent || _hWriterEvent))
  1056. {
  1057. fCacheEvents = TRUE;
  1058. dwModifyState += (WRITER_SIGNALED + READER_SIGNALED);
  1059. }
  1060. #endif
  1061. }
  1062. else
  1063. {
  1064. fLastReader = FALSE;
  1065. }
  1066. // Sanity checks
  1067. Win4Assert((dwKnownState & WRITER) == 0);
  1068. Win4Assert(dwKnownState & READERS_MASK);
  1069. dwCurrentState = InterlockedCompareExchange((LONG *) &_dwState,
  1070. (dwKnownState + dwModifyState),
  1071. dwKnownState);
  1072. } while(dwCurrentState != dwKnownState);
  1073. #if LOCK_PERF==1
  1074. gLockTracker.ReaderLeaving(this);
  1075. #endif
  1076. // Check for last reader
  1077. if(fLastReader)
  1078. {
  1079. // Check for waiting writers
  1080. if(dwKnownState & WAITING_WRITERS_MASK)
  1081. {
  1082. Win4Assert(_dwState & WRITER_SIGNALED);
  1083. Win4Assert(hWriterEvent);
  1084. RWSetEvent(hWriterEvent);
  1085. }
  1086. // Check for waiting readers
  1087. else if(dwKnownState & WAITING_READERS_MASK)
  1088. {
  1089. Win4Assert(_dwState & READER_SIGNALED);
  1090. Win4Assert(hReaderEvent);
  1091. RWSetEvent(hReaderEvent);
  1092. }
  1093. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1094. // Check for the need to release events
  1095. else if(fCacheEvents)
  1096. {
  1097. ReleaseEvents();
  1098. }
  1099. #endif
  1100. }
  1101. }
  1102. else
  1103. {
  1104. hr = HRESULT_FROM_WIN32(ERROR_NOT_OWNER);
  1105. }
  1106. }
  1107. else
  1108. {
  1109. hr = HRESULT_FROM_WIN32(ERROR_NOT_OWNER);
  1110. }
  1111. if(FAILED(hr))
  1112. {
  1113. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1114. if((_wFlags & RWLOCKFLAG_RETURNERRORS) == 0)
  1115. #endif
  1116. {
  1117. Win4Assert(!"Attempt to release reader lock on a wrong thread");
  1118. if(fBreakOnErrors)
  1119. DebugBreak();
  1120. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  1121. }
  1122. }
  1123. }
  1124. return(hr);
  1125. }
  1126. //+-------------------------------------------------------------------
  1127. //
  1128. // Method: CRWLock::UpgradeToWriterLock public
  1129. //
  1130. // Synopsis: Upgrades to a writer lock. It returns a BOOL that
  1131. // indicates intervening writes.
  1132. //
  1133. // History: 21-Aug-98 Gopalk Created
  1134. //
  1135. //+-------------------------------------------------------------------
  1136. HRESULT CRWLock::UpgradeToWriterLock(LockCookie *pLockCookie,
  1137. BOOL *pfInterveningWrites
  1138. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1139. ,
  1140. BOOL fReturnErrors,
  1141. DWORD dwDesiredTimeout
  1142. #endif
  1143. #if LOCK_PERF==1
  1144. ,
  1145. const char *pszFile,
  1146. DWORD dwLine,
  1147. const char *pszLockName
  1148. #endif
  1149. )
  1150. {
  1151. HRESULT hr;
  1152. DWORD dwThreadID;
  1153. // Initialize the cookie
  1154. memset(pLockCookie, 0, sizeof(LockCookie));
  1155. if(pfInterveningWrites)
  1156. *pfInterveningWrites = TRUE;
  1157. dwThreadID = GetCurrentThreadId();
  1158. // Check if the thread is already a writer
  1159. if(_dwWriterID == dwThreadID)
  1160. {
  1161. // Update cookie state
  1162. pLockCookie->dwFlags = UPGRADE_COOKIE | COOKIE_WRITER;
  1163. pLockCookie->wWriterLevel = _wWriterLevel;
  1164. // No intevening writes
  1165. if(pfInterveningWrites)
  1166. *pfInterveningWrites = FALSE;
  1167. // Acquire the writer lock again
  1168. hr = AcquireWriterLock();
  1169. }
  1170. else
  1171. {
  1172. WORD *pwReaderLevel;
  1173. // Ensure that the lock was initialized
  1174. if(!IsInitialized())
  1175. Initialize();
  1176. hr = GetTLSLockData(&pwReaderLevel);
  1177. if(SUCCEEDED(hr))
  1178. {
  1179. BOOL fAcquireWriterLock;
  1180. DWORD dwWriterSeqNum = 0;
  1181. // Check if the thread is a reader
  1182. if(*pwReaderLevel != 0)
  1183. {
  1184. // Sanity check
  1185. Win4Assert(_dwState & READERS_MASK);
  1186. // Save lock state in the cookie
  1187. pLockCookie->dwFlags = UPGRADE_COOKIE | COOKIE_READER;
  1188. pLockCookie->pwReaderLevel = pwReaderLevel;
  1189. pLockCookie->wReaderLevel = *pwReaderLevel;
  1190. // If there is only one reader, try to convert reader to a writer
  1191. DWORD dwKnownState = InterlockedCompareExchange((LONG *) &_dwState,
  1192. WRITER,
  1193. READER);
  1194. if(dwKnownState == READER)
  1195. {
  1196. // Thread is no longer a reader
  1197. *pwReaderLevel = 0;
  1198. // Save threadid of the writer
  1199. _dwWriterID = dwThreadID;
  1200. _wWriterLevel = 1;
  1201. ++_dwWriterSeqNum;
  1202. fAcquireWriterLock = FALSE;
  1203. // No intevening writes
  1204. if(pfInterveningWrites)
  1205. *pfInterveningWrites = FALSE;
  1206. #if RWLOCK_STATISTICS
  1207. ++_dwWriterEntryCount;
  1208. #endif
  1209. #if LOCK_PERF==1
  1210. gLockTracker.ReaderLeaving(this);
  1211. gLockTracker.WriterEntered(pszFile, dwLine, pszLockName, this);
  1212. #endif
  1213. }
  1214. else
  1215. {
  1216. // Note the current sequence number of the writer lock
  1217. dwWriterSeqNum = _dwWriterSeqNum;
  1218. // Release the reader lock
  1219. *pwReaderLevel = 1;
  1220. hr = ReleaseReaderLock();
  1221. Win4Assert(SUCCEEDED(hr));
  1222. fAcquireWriterLock = TRUE;
  1223. }
  1224. }
  1225. else
  1226. {
  1227. fAcquireWriterLock = TRUE;
  1228. pLockCookie->dwFlags = UPGRADE_COOKIE | COOKIE_NONE;
  1229. }
  1230. // Check for the need to acquire the writer lock
  1231. if(fAcquireWriterLock)
  1232. {
  1233. hr = AcquireWriterLock(
  1234. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1235. TRUE, dwDesiredTimeout
  1236. #if LOCK_PERF==1
  1237. ,
  1238. #endif
  1239. #endif
  1240. #if LOCK_PERF==1
  1241. pszFile, dwLine, pszLockName
  1242. #endif
  1243. );
  1244. if(SUCCEEDED(hr))
  1245. {
  1246. // Check for intevening writes
  1247. if((_dwWriterSeqNum == (dwWriterSeqNum + 1)) &&
  1248. pfInterveningWrites)
  1249. *pfInterveningWrites = FALSE;
  1250. }
  1251. else
  1252. {
  1253. if(pLockCookie->dwFlags & COOKIE_READER)
  1254. {
  1255. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1256. DWORD dwTimeout = (dwDesiredTimeout > gdwReasonableTimeout)
  1257. ? dwDesiredTimeout
  1258. : gdwReasonableTimeout;
  1259. #endif
  1260. HRESULT hr1 = AcquireReaderLock(
  1261. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1262. FALSE, dwTimeout
  1263. #if LOCK_PERF==1
  1264. ,
  1265. #endif
  1266. #endif
  1267. #if LOCK_PERF==1
  1268. pszFile, dwLine, pszLockName
  1269. #endif
  1270. );
  1271. if(SUCCEEDED(hr1))
  1272. {
  1273. *pwReaderLevel = pLockCookie->wReaderLevel;
  1274. }
  1275. else
  1276. {
  1277. Win4Assert(!"Failed to reacquire reader lock");
  1278. if(fBreakOnErrors)
  1279. DebugBreak();
  1280. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  1281. }
  1282. }
  1283. }
  1284. }
  1285. }
  1286. }
  1287. // Check failure return
  1288. if(FAILED(hr))
  1289. {
  1290. pLockCookie->dwFlags = INVALID_COOKIE;
  1291. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1292. if(fReturnErrors == FALSE)
  1293. {
  1294. Win4Assert(!"Failed to upgrade the lock");
  1295. if(fBreakOnErrors)
  1296. DebugBreak();
  1297. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  1298. }
  1299. #endif
  1300. }
  1301. return(hr);
  1302. }
  1303. //+-------------------------------------------------------------------
  1304. //
  1305. // Method: CRWLock::DowngradeFromWriterLock public
  1306. //
  1307. // Synopsis: Downgrades from a writer lock.
  1308. //
  1309. // History: 21-Aug-98 Gopalk Created
  1310. //
  1311. //+-------------------------------------------------------------------
  1312. HRESULT CRWLock::DowngradeFromWriterLock(LockCookie *pLockCookie
  1313. #if LOCK_PERF==1
  1314. ,
  1315. const char *pszFile,
  1316. DWORD dwLine,
  1317. const char *pszLockName
  1318. #endif
  1319. )
  1320. {
  1321. HRESULT hr = S_OK;
  1322. // Ensure that the cookie is valid
  1323. if(pLockCookie->dwFlags & UPGRADE_COOKIE)
  1324. {
  1325. DWORD dwThreadID = GetCurrentThreadId();
  1326. // Sanity check
  1327. Win4Assert((pLockCookie->pwReaderLevel == NULL) ||
  1328. (*pLockCookie->pwReaderLevel == 0));
  1329. // Ensure that the thread is a writer
  1330. if(_dwWriterID == dwThreadID)
  1331. {
  1332. // Release the writer lock
  1333. hr = ReleaseWriterLock();
  1334. if(SUCCEEDED(hr))
  1335. {
  1336. // Check if the thread was a writer
  1337. if(_dwWriterID == dwThreadID)
  1338. {
  1339. // Ensure that the thread was a writer and that
  1340. // nesting level was restored to the previous
  1341. // value
  1342. if(((pLockCookie->dwFlags & COOKIE_WRITER) == 0) ||
  1343. (pLockCookie->wWriterLevel != _wWriterLevel))
  1344. {
  1345. Win4Assert(!"Writer lock incorrectly nested");
  1346. hr = E_FAIL;
  1347. }
  1348. }
  1349. // Check if the thread was a reader
  1350. else if(pLockCookie->dwFlags & COOKIE_READER)
  1351. {
  1352. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1353. DWORD dwTimeout = (gdwDefaultTimeout > gdwReasonableTimeout)
  1354. ? gdwDefaultTimeout
  1355. : gdwReasonableTimeout;
  1356. #endif
  1357. hr = AcquireReaderLock(
  1358. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1359. TRUE, dwTimeout
  1360. #if LOCK_PERF==1
  1361. ,
  1362. #endif
  1363. #endif
  1364. #if LOCK_PERF==1
  1365. pszFile, dwLine, pszLockName
  1366. #endif
  1367. );
  1368. if(SUCCEEDED(hr))
  1369. {
  1370. *pLockCookie->pwReaderLevel = pLockCookie->wReaderLevel;
  1371. }
  1372. else
  1373. {
  1374. Win4Assert(!"Failed to reacquire reader lock");
  1375. }
  1376. }
  1377. else
  1378. {
  1379. Win4Assert(pLockCookie->dwFlags & COOKIE_NONE);
  1380. }
  1381. }
  1382. }
  1383. else
  1384. {
  1385. Win4Assert(!"Attempt to downgrade writer lock on a wrong thread");
  1386. hr = HRESULT_FROM_WIN32(ERROR_NOT_OWNER);
  1387. }
  1388. // Check failure return
  1389. if(FAILED(hr))
  1390. {
  1391. if(fBreakOnErrors)
  1392. DebugBreak();
  1393. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  1394. }
  1395. }
  1396. return(hr);
  1397. }
  1398. //+-------------------------------------------------------------------
  1399. //
  1400. // Method: CRWLock::ReleaseLock public
  1401. //
  1402. // Synopsis: Releases the lock held by the current thread
  1403. //
  1404. // History: 21-Aug-98 Gopalk Created
  1405. //
  1406. //+-------------------------------------------------------------------
  1407. HRESULT CRWLock::ReleaseLock(LockCookie *pLockCookie)
  1408. {
  1409. HRESULT hr;
  1410. // Initialize the cookie
  1411. memset(pLockCookie, 0, sizeof(LockCookie));
  1412. // Check if the thread is a writer
  1413. if(_dwWriterID == GetCurrentThreadId())
  1414. {
  1415. // Save lock state in the cookie
  1416. pLockCookie->dwFlags = RELEASE_COOKIE | COOKIE_WRITER;
  1417. pLockCookie->dwWriterSeqNum = _dwWriterSeqNum;
  1418. pLockCookie->wWriterLevel = _wWriterLevel;
  1419. // Release the writer lock
  1420. _wWriterLevel = 1;
  1421. hr = ReleaseWriterLock();
  1422. Win4Assert(SUCCEEDED(hr));
  1423. }
  1424. else
  1425. {
  1426. WORD *pwReaderLevel;
  1427. // Ensure that the lock was initialized
  1428. if(!IsInitialized())
  1429. Initialize();
  1430. hr = GetTLSLockData(&pwReaderLevel);
  1431. if(SUCCEEDED(hr))
  1432. {
  1433. // Check if the thread is a reader
  1434. if(*pwReaderLevel != 0)
  1435. {
  1436. // Save lock state in the cookie
  1437. pLockCookie->dwFlags = RELEASE_COOKIE | COOKIE_READER;
  1438. pLockCookie->pwReaderLevel = pwReaderLevel;
  1439. pLockCookie->wReaderLevel = *pwReaderLevel;
  1440. pLockCookie->dwWriterSeqNum = _dwWriterSeqNum;
  1441. // Release the reader lock
  1442. *pwReaderLevel = 1;
  1443. hr = ReleaseReaderLock();
  1444. Win4Assert(SUCCEEDED(hr));
  1445. }
  1446. else
  1447. {
  1448. pLockCookie->dwFlags = RELEASE_COOKIE | COOKIE_NONE;
  1449. }
  1450. }
  1451. else
  1452. {
  1453. hr = S_OK;
  1454. pLockCookie->dwFlags = RELEASE_COOKIE | COOKIE_NONE;
  1455. }
  1456. }
  1457. return(hr);
  1458. }
  1459. //+-------------------------------------------------------------------
  1460. //
  1461. // Method: CRWLock::RestoreLock public
  1462. //
  1463. // Synopsis: Restore the lock held by the current thread
  1464. //
  1465. // History: 21-Aug-98 Gopalk Created
  1466. //
  1467. //+-------------------------------------------------------------------
  1468. HRESULT CRWLock::RestoreLock(LockCookie *pLockCookie,
  1469. BOOL *pfInterveningWrites
  1470. #if LOCK_PERF==1
  1471. ,
  1472. const char *pszFile,
  1473. DWORD dwLine,
  1474. const char *pszLockName
  1475. #endif
  1476. )
  1477. {
  1478. HRESULT hr = S_OK;
  1479. // Initialize
  1480. if(pfInterveningWrites)
  1481. *pfInterveningWrites = TRUE;
  1482. // Ensure that the cookie is valid
  1483. if(pLockCookie->dwFlags & RELEASE_COOKIE)
  1484. {
  1485. DWORD dwThreadID = GetCurrentThreadId();
  1486. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1487. DWORD dwTimeout = (gdwDefaultTimeout > gdwReasonableTimeout)
  1488. ? gdwDefaultTimeout
  1489. : gdwReasonableTimeout;
  1490. #endif
  1491. // Check if the thread holds reader or writer lock
  1492. if(((pLockCookie->pwReaderLevel != NULL) && (*pLockCookie->pwReaderLevel > 0)) ||
  1493. (_dwWriterID == dwThreadID))
  1494. {
  1495. Win4Assert(!"Thread holds reader or writer lock");
  1496. if(fBreakOnErrors)
  1497. DebugBreak();
  1498. TerminateProcess(GetCurrentProcess(), RWLOCK_FATALFAILURE);
  1499. }
  1500. // Check if the thread was a writer
  1501. else if(pLockCookie->dwFlags & COOKIE_WRITER)
  1502. {
  1503. // Acquire writer lock
  1504. hr = AcquireWriterLock(
  1505. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1506. FALSE, dwTimeout
  1507. #if LOCK_PERF==1
  1508. ,
  1509. #endif
  1510. #endif
  1511. #if LOCK_PERF==1
  1512. pszFile, dwLine, pszLockName
  1513. #endif
  1514. );
  1515. Win4Assert(SUCCEEDED(hr));
  1516. _wWriterLevel = pLockCookie->wWriterLevel;
  1517. if((_dwWriterSeqNum == (pLockCookie->dwWriterSeqNum + 1)) &&
  1518. pfInterveningWrites)
  1519. *pfInterveningWrites = FALSE;
  1520. }
  1521. // Check if the thread was a reader
  1522. else if(pLockCookie->dwFlags & COOKIE_READER)
  1523. {
  1524. hr = AcquireReaderLock(
  1525. #ifdef RWLOCK_FULL_FUNCTIONALITY
  1526. FALSE, dwTimeout
  1527. #if LOCK_PERF==1
  1528. ,
  1529. #endif
  1530. #endif
  1531. #if LOCK_PERF==1
  1532. pszFile, dwLine, pszLockName
  1533. #endif
  1534. );
  1535. Win4Assert(SUCCEEDED(hr));
  1536. *pLockCookie->pwReaderLevel = pLockCookie->wReaderLevel;
  1537. if((_dwWriterSeqNum == (pLockCookie->dwWriterSeqNum + 1)) &&
  1538. pfInterveningWrites)
  1539. *pfInterveningWrites = FALSE;
  1540. }
  1541. else
  1542. {
  1543. Win4Assert(pLockCookie->dwFlags & COOKIE_NONE);
  1544. }
  1545. }
  1546. return(hr);
  1547. }
  1548. //+-------------------------------------------------------------------
  1549. //
  1550. // Method: CStaticRWLock::Initialize public
  1551. //
  1552. // Synopsis: Initializes state. It is important that the
  1553. // default constructor only Zero out the memory
  1554. //
  1555. // History: 21-Aug-98 Gopalk Created
  1556. //
  1557. //+-------------------------------------------------------------------
  1558. extern CRITICAL_SECTION g_OleMutexCreationSem;
  1559. void CStaticRWLock::Initialize()
  1560. {
  1561. // Acquire lock creation critical section
  1562. EnterCriticalSection (&g_OleMutexCreationSem);
  1563. // Prevent second initialization
  1564. if(!IsInitialized())
  1565. {
  1566. _dwLockNum = gdwLockSeqNum++;
  1567. #if LOCK_PERF==1
  1568. gLockTracker.RegisterLock(this, TRUE);
  1569. #endif
  1570. // The initialization should be complete
  1571. // before delegating to the base class
  1572. CRWLock::Initialize();
  1573. }
  1574. // Release lock creation critical section
  1575. LeaveCriticalSection (&g_OleMutexCreationSem);
  1576. return;
  1577. }
  1578. LockEntry * GetLockEntryFromTLS()
  1579. {
  1580. LockEntry *pLockEntry = NULL;
  1581. #ifdef __NOOLETLS__
  1582. pLockEntry = (LockEntry *) TlsGetValue(gLockTlsIdx);
  1583. if (!pLockEntry)
  1584. {
  1585. pLockEntry = (LockEntry *) PrivMemAlloc(sizeof(LockEntry));
  1586. if (pLockEntry)
  1587. {
  1588. memset(pLockEntry, 0, sizeof(LockEntry));
  1589. TlsSetValue(gLockTlsIdx, pLockEntry);
  1590. }
  1591. }
  1592. #else
  1593. HRESULT hr;
  1594. COleTls Tls(hr);
  1595. if(SUCCEEDED(hr))
  1596. {
  1597. pLockEntry = &(Tls->lockEntry);
  1598. }
  1599. #endif
  1600. return pLockEntry;
  1601. }
  1602. //+-------------------------------------------------------------------
  1603. //
  1604. // Method: CStaticRWLock::GetTLSLockData private
  1605. //
  1606. // Synopsis: Obtains the data mainitained in TLS for the lock
  1607. //
  1608. // History: 21-Aug-98 Gopalk Created
  1609. //
  1610. //+-------------------------------------------------------------------
  1611. HRESULT CStaticRWLock::GetTLSLockData(WORD **ppwReaderLevel)
  1612. {
  1613. HRESULT hr = E_OUTOFMEMORY;
  1614. // Ensure that the lock was initialized
  1615. if(IsInitialized())
  1616. {
  1617. LockEntry *pLockEntry = GetLockEntryFromTLS();
  1618. if (pLockEntry)
  1619. {
  1620. // Compute the quotient and remainder
  1621. DWORD dwSkip = _dwLockNum / LOCKS_PER_ENTRY;
  1622. DWORD dwIndex = _dwLockNum % LOCKS_PER_ENTRY;
  1623. // Skip quotient entries
  1624. while(dwSkip && pLockEntry)
  1625. {
  1626. // Allocate the lock entries if needed
  1627. if(pLockEntry->pNext == NULL)
  1628. {
  1629. LockEntry *pEntry;
  1630. pEntry = (LockEntry *) HeapAlloc(g_hHeap, HEAP_SERIALIZE, sizeof(LockEntry));
  1631. if(pEntry)
  1632. {
  1633. memset(pEntry, 0 , sizeof(LockEntry));
  1634. pEntry = (LockEntry *) InterlockedCompareExchangePointer((void **) &(pLockEntry->pNext),
  1635. pEntry,
  1636. NULL);
  1637. if(pEntry)
  1638. HeapFree(g_hHeap, HEAP_SERIALIZE, pEntry);
  1639. }
  1640. }
  1641. // Skip to next lock entry
  1642. pLockEntry = pLockEntry->pNext;
  1643. --dwSkip;
  1644. }
  1645. // Check for OOM
  1646. if(pLockEntry)
  1647. {
  1648. *ppwReaderLevel = &(pLockEntry->wReaderLevel[dwIndex]);
  1649. hr = S_OK;
  1650. }
  1651. else
  1652. {
  1653. *ppwReaderLevel = NULL;
  1654. hr = E_OUTOFMEMORY;
  1655. }
  1656. }
  1657. }
  1658. else
  1659. {
  1660. *ppwReaderLevel = NULL;
  1661. hr = S_FALSE;
  1662. }
  1663. return(hr);
  1664. }