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.

714 lines
23 KiB

  1. /*++
  2. Copyright (c) 1998-1999 Microsoft Corporation
  3. Module Name :
  4. locks.cpp
  5. Abstract:
  6. A collection of locks for multithreaded access to data structures
  7. Author:
  8. George V. Reilly (GeorgeRe) 06-Jan-1998
  9. Environment:
  10. Win32 - User Mode
  11. Project:
  12. Internet Information Server RunTime Library
  13. Revision History:
  14. --*/
  15. #include "precomp.hxx"
  16. #define DLL_IMPLEMENTATION
  17. #define IMPLEMENTATION_EXPORT
  18. #include <locks.h>
  19. #ifdef __LOCKS_NAMESPACE__
  20. namespace Locks {
  21. #endif // __LOCKS_NAMESPACE__
  22. #define LOCK_DEFAULT_SPIN_DATA(CLASS) \
  23. WORD CLASS::sm_wDefaultSpinCount = LOCK_DEFAULT_SPINS; \
  24. double CLASS::sm_dblDfltSpinAdjFctr = 0.5
  25. #ifdef LOCK_INSTRUMENTATION
  26. # define LOCK_STATISTICS_DATA(CLASS) \
  27. LONG CLASS::sm_cTotalLocks = 0; \
  28. LONG CLASS::sm_cContendedLocks = 0; \
  29. LONG CLASS::sm_nSleeps = 0; \
  30. LONGLONG CLASS::sm_cTotalSpins = 0; \
  31. LONG CLASS::sm_nReadLocks = 0; \
  32. LONG CLASS::sm_nWriteLocks = 0
  33. # define LOCK_STATISTICS_DUMMY_IMPLEMENTATION(CLASS) \
  34. CLockStatistics CLASS::Statistics() const \
  35. {return CLockStatistics();} \
  36. CGlobalLockStatistics CLASS::GlobalStatistics() \
  37. {return CGlobalLockStatistics();} \
  38. void CLASS::ResetGlobalStatistics() \
  39. {}
  40. # define LOCK_STATISTICS_REAL_IMPLEMENTATION(CLASS) \
  41. \
  42. /* Per-lock statistics */ \
  43. CLockStatistics \
  44. CLASS::Statistics() const \
  45. { \
  46. CLockStatistics ls; \
  47. \
  48. ls.m_nContentions = m_nContentions; \
  49. ls.m_nSleeps = m_nSleeps; \
  50. ls.m_nContentionSpins = m_nContentionSpins; \
  51. if (m_nContentions > 0) \
  52. ls.m_nAverageSpins = m_nContentionSpins / m_nContentions;\
  53. else \
  54. ls.m_nAverageSpins = 0; \
  55. ls.m_nReadLocks = m_nReadLocks; \
  56. ls.m_nWriteLocks = m_nWriteLocks; \
  57. strcpy(ls.m_szName, m_szName); \
  58. \
  59. return ls; \
  60. } \
  61. \
  62. \
  63. /* Global statistics for CLASS */ \
  64. CGlobalLockStatistics \
  65. CLASS::GlobalStatistics() \
  66. { \
  67. CGlobalLockStatistics gls; \
  68. \
  69. gls.m_cTotalLocks = sm_cTotalLocks; \
  70. gls.m_cContendedLocks = sm_cContendedLocks; \
  71. gls.m_nSleeps = sm_nSleeps; \
  72. gls.m_cTotalSpins = sm_cTotalSpins; \
  73. if (sm_cContendedLocks > 0) \
  74. gls.m_nAverageSpins = static_cast<LONG>(sm_cTotalSpins / \
  75. sm_cContendedLocks);\
  76. else \
  77. gls.m_nAverageSpins = 0; \
  78. gls.m_nReadLocks = sm_nReadLocks; \
  79. gls.m_nWriteLocks = sm_nWriteLocks; \
  80. \
  81. return gls; \
  82. } \
  83. \
  84. \
  85. /* Reset global statistics for CLASS */ \
  86. void \
  87. CLASS::ResetGlobalStatistics() \
  88. { \
  89. sm_cTotalLocks = 0; \
  90. sm_cContendedLocks = 0; \
  91. sm_nSleeps = 0; \
  92. sm_cTotalSpins = 0; \
  93. sm_nReadLocks = 0; \
  94. sm_nWriteLocks = 0; \
  95. }
  96. // Note: we are not using Interlocked operations for the shared
  97. // statistical counters. We'll lose perfect accuracy, but we'll
  98. // gain by reduced bus synchronization traffic.
  99. # define LOCK_INSTRUMENTATION_PROLOG() \
  100. ++sm_cContendedLocks; \
  101. LONG cTotalSpins = 0; \
  102. WORD cSleeps = 0
  103. // Don't need InterlockedIncrement or InterlockedExchangeAdd for
  104. // member variables, as the lock is now locked by this thread.
  105. # define LOCK_INSTRUMENTATION_EPILOG() \
  106. ++m_nContentions; \
  107. m_nSleeps += cSleeps; \
  108. m_nContentionSpins += cTotalSpins; \
  109. sm_nSleeps += cSleeps; \
  110. sm_cTotalSpins += cTotalSpins
  111. #else // !LOCK_INSTRUMENTATION
  112. # define LOCK_STATISTICS_DATA(CLASS)
  113. # define LOCK_STATISTICS_DUMMY_IMPLEMENTATION(CLASS)
  114. # define LOCK_STATISTICS_REAL_IMPLEMENTATION(CLASS)
  115. # define LOCK_INSTRUMENTATION_PROLOG()
  116. # define LOCK_INSTRUMENTATION_EPILOG()
  117. #endif // !LOCK_INSTRUMENTATION
  118. //------------------------------------------------------------------------
  119. // Function: RandomBackoffFactor
  120. // Synopsis: A fudge factor to help avoid synchronization problems
  121. //------------------------------------------------------------------------
  122. double
  123. RandomBackoffFactor()
  124. {
  125. static const double s_aFactors[] = {
  126. 1.020, 0.965, 0.890, 1.065,
  127. 1.025, 1.115, 0.940, 0.995,
  128. 1.050, 1.080, 0.915, 0.980,
  129. 1.010,
  130. };
  131. const int nFactors = sizeof(s_aFactors) / sizeof(s_aFactors[0]);
  132. // Alternatives for nRand include a static counter
  133. // or the low DWORD of QueryPerformanceCounter().
  134. DWORD nRand = ::GetCurrentThreadId();
  135. return s_aFactors[nRand % nFactors];
  136. }
  137. // CSmallSpinLock static member variables
  138. LOCK_DEFAULT_SPIN_DATA(CSmallSpinLock);
  139. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  140. LOCK_STATISTICS_DATA(CSmallSpinLock);
  141. LOCK_STATISTICS_REAL_IMPLEMENTATION(CSmallSpinLock);
  142. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  143. //------------------------------------------------------------------------
  144. // Function: CSmallSpinLock::_LockSpin
  145. // Synopsis: Acquire an exclusive lock. Blocks until acquired.
  146. //------------------------------------------------------------------------
  147. void
  148. CSmallSpinLock::_LockSpin()
  149. {
  150. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  151. LOCK_INSTRUMENTATION_PROLOG();
  152. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  153. DWORD dwSleepTime = 0;
  154. LONG cBaseSpins = sm_wDefaultSpinCount;
  155. LONG cBaseSpins2 = static_cast<LONG>(cBaseSpins * RandomBackoffFactor());
  156. // This lock cannot be acquired recursively. Attempting to do so will
  157. // deadlock this thread forever. Use CSpinLock instead if you need that
  158. // kind of lock.
  159. if (m_lTid == (LONG) ::GetCurrentThreadId())
  160. {
  161. IRTLASSERT(
  162. !"CSmallSpinLock: Illegally attempted to acquire lock recursively");
  163. }
  164. while (!_TryLock())
  165. {
  166. // Only spin on a multiprocessor machine and then only if
  167. // spinning is enabled
  168. if (NumProcessors() > 1 && cBaseSpins != LOCK_DONT_SPIN)
  169. {
  170. LONG cSpins = cBaseSpins2;
  171. // Check no more than cBaseSpins2 times then yield.
  172. // It is important not to use the InterlockedExchange in the
  173. // inner loop in order to minimize system memory bus traffic.
  174. while (m_lTid != 0)
  175. {
  176. if (--cSpins < 0)
  177. {
  178. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  179. cTotalSpins += cBaseSpins2;
  180. ++cSleeps;
  181. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  182. Sleep(dwSleepTime) ;
  183. // Backoff algorithm: reduce (or increase) busy wait time
  184. cBaseSpins2 = (int) (cBaseSpins2 * sm_dblDfltSpinAdjFctr);
  185. // LOCK_MINIMUM_SPINS <= cBaseSpins2 <= LOCK_MAXIMUM_SPINS
  186. cBaseSpins2 = min(LOCK_MAXIMUM_SPINS, cBaseSpins2);
  187. cBaseSpins2 = max(cBaseSpins2, LOCK_MINIMUM_SPINS);
  188. cSpins = cBaseSpins2;
  189. // Using Sleep(0) leads to the possibility of priority
  190. // inversion. Sleep(0) only yields the processor if
  191. // there's another thread of the same priority that's
  192. // ready to run. If a high-priority thread is trying to
  193. // acquire the lock, which is held by a low-priority
  194. // thread, then the low-priority thread may never get
  195. // scheduled and hence never free the lock. NT attempts
  196. // to avoid priority inversions by temporarily boosting
  197. // the priority of low-priority runnable threads, but the
  198. // problem can still occur if there's a medium-priority
  199. // thread that's always runnable. If Sleep(1) is used,
  200. // then the thread unconditionally yields the CPU. We
  201. // only do this for the second and subsequent even
  202. // iterations, since a millisecond is a long time to wait
  203. // if the thread can be scheduled in again sooner
  204. // (~100,000 instructions).
  205. // Avoid priority inversion: 0, 1, 0, 1,...
  206. dwSleepTime = !dwSleepTime;
  207. }
  208. else
  209. {
  210. Lock_Yield();
  211. }
  212. }
  213. // Lock is now available, but we still need to do the
  214. // InterlockedExchange to atomically grab it for ourselves.
  215. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  216. cTotalSpins += cBaseSpins2 - cSpins;
  217. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  218. }
  219. // On a 1P machine, busy waiting is a waste of time
  220. else
  221. {
  222. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  223. ++cSleeps;
  224. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  225. Sleep(dwSleepTime);
  226. // Avoid priority inversion: 0, 1, 0, 1,...
  227. dwSleepTime = !dwSleepTime;
  228. }
  229. }
  230. #ifdef LOCK_SMALL_SPIN_INSTRUMENTATION
  231. LOCK_INSTRUMENTATION_EPILOG();
  232. #endif // LOCK_SMALL_SPIN_INSTRUMENTATION
  233. }
  234. // CSpinLock static member variables
  235. LOCK_DEFAULT_SPIN_DATA(CSpinLock);
  236. LOCK_STATISTICS_DATA(CSpinLock);
  237. LOCK_STATISTICS_REAL_IMPLEMENTATION(CSpinLock);
  238. //------------------------------------------------------------------------
  239. // Function: CSpinLock::_LockSpin
  240. // Synopsis: Acquire an exclusive lock. Blocks until acquired.
  241. //------------------------------------------------------------------------
  242. void
  243. CSpinLock::_LockSpin()
  244. {
  245. LOCK_INSTRUMENTATION_PROLOG();
  246. DWORD dwSleepTime = 0;
  247. bool fAcquiredLock = false;
  248. LONG cBaseSpins = sm_wDefaultSpinCount;
  249. cBaseSpins = static_cast<LONG>(cBaseSpins * RandomBackoffFactor());
  250. while (!fAcquiredLock)
  251. {
  252. // Only spin on a multiprocessor machine and then only if
  253. // spinning is enabled
  254. if (NumProcessors() > 1 && sm_wDefaultSpinCount != LOCK_DONT_SPIN)
  255. {
  256. LONG cSpins = cBaseSpins;
  257. // Check no more than cBaseSpins times then yield
  258. while (m_lTid != 0)
  259. {
  260. if (--cSpins < 0)
  261. {
  262. #ifdef LOCK_INSTRUMENTATION
  263. cTotalSpins += cBaseSpins;
  264. ++cSleeps;
  265. #endif // LOCK_INSTRUMENTATION
  266. Sleep(dwSleepTime) ;
  267. // Backoff algorithm: reduce (or increase) busy wait time
  268. cBaseSpins = (int) (cBaseSpins * sm_dblDfltSpinAdjFctr);
  269. // LOCK_MINIMUM_SPINS <= cBaseSpins <= LOCK_MAXIMUM_SPINS
  270. cBaseSpins = min(LOCK_MAXIMUM_SPINS, cBaseSpins);
  271. cBaseSpins = max(cBaseSpins, LOCK_MINIMUM_SPINS);
  272. cSpins = cBaseSpins;
  273. // Avoid priority inversion: 0, 1, 0, 1,...
  274. dwSleepTime = !dwSleepTime;
  275. }
  276. else
  277. {
  278. Lock_Yield();
  279. }
  280. }
  281. // Lock is now available, but we still need to atomically
  282. // update m_cOwners and m_nThreadId to grab it for ourselves.
  283. #ifdef LOCK_INSTRUMENTATION
  284. cTotalSpins += cBaseSpins - cSpins;
  285. #endif // LOCK_INSTRUMENTATION
  286. }
  287. // on a 1P machine, busy waiting is a waste of time
  288. else
  289. {
  290. #ifdef LOCK_INSTRUMENTATION
  291. ++cSleeps;
  292. #endif // LOCK_INSTRUMENTATION
  293. Sleep(dwSleepTime);
  294. // Avoid priority inversion: 0, 1, 0, 1,...
  295. dwSleepTime = !dwSleepTime;
  296. }
  297. // Is the lock unowned?
  298. if (_TryLock())
  299. fAcquiredLock = true; // got the lock
  300. }
  301. IRTLASSERT((m_lTid & OWNER_MASK) > 0
  302. && (m_lTid & THREAD_MASK) == _CurrentThreadId());
  303. LOCK_INSTRUMENTATION_EPILOG();
  304. }
  305. // CFakeLock static member variables
  306. LOCK_DEFAULT_SPIN_DATA(CFakeLock);
  307. LOCK_STATISTICS_DATA(CFakeLock);
  308. LOCK_STATISTICS_DUMMY_IMPLEMENTATION(CFakeLock);
  309. // CCritSec static member variables
  310. LOCK_DEFAULT_SPIN_DATA(CCritSec);
  311. LOCK_STATISTICS_DATA(CCritSec);
  312. LOCK_STATISTICS_DUMMY_IMPLEMENTATION(CCritSec);
  313. //------------------------------------------------------------------------
  314. // SetCriticalSectionSpinCount hackery
  315. typedef
  316. DWORD
  317. (WINAPI * PFN_SET_CRITICAL_SECTION_SPIN_COUNT)(
  318. LPCRITICAL_SECTION lpCriticalSection,
  319. DWORD dwSpinCount
  320. );
  321. static PFN_SET_CRITICAL_SECTION_SPIN_COUNT g_pfnSetCSSpinCount = NULL;
  322. //------------------------------------------------------------------------
  323. // Function: FakeSetCriticalSectionSpinCount
  324. // Synopsis: This function fakes setting critical section spin count.
  325. // See CCritSec::SetSpinCount() for details
  326. // Returns: 0 - since we are faking the set of cs with spin count
  327. //------------------------------------------------------------------------
  328. static DWORD WINAPI
  329. FakeSetCriticalSectionSpinCount(
  330. LPCRITICAL_SECTION /*lpCriticalSection*/,
  331. DWORD /*dwSpinCount*/)
  332. {
  333. // For faked critical sections, the previous spin count is just ZERO!
  334. return 0;
  335. }
  336. //------------------------------------------------------------------------
  337. // Function: CCritSec::SetSpinCount
  338. // Synopsis: This function is used to call the appropriate underlying
  339. // functions to set the spin count for the supplied critical
  340. // section. The original function is supposed to be exported out
  341. // of kernel32.dll from NT 4.0 SP3. If the func is not available
  342. // from the dll, we will use a fake function.
  343. //
  344. // Arguments:
  345. // lpCriticalSection
  346. // Points to the critical section object.
  347. //
  348. // dwSpinCount
  349. // Supplies the spin count for the critical section object. For UP
  350. // systems, the spin count is ignored and the critical section spin
  351. // count is set to 0. For MP systems, if contention occurs, instead of
  352. // waiting on a semaphore associated with the critical section, the
  353. // calling thread will spin for spin count iterations before doing the
  354. // hard wait. If the critical section becomes free during the spin, a
  355. // wait is avoided.
  356. //
  357. // Returns:
  358. // The previous spin count for the critical section is returned.
  359. //------------------------------------------------------------------------
  360. DWORD
  361. CCritSec::SetSpinCount(
  362. LPCRITICAL_SECTION pcs,
  363. DWORD dwSpinCount)
  364. {
  365. if (g_pfnSetCSSpinCount == NULL)
  366. {
  367. PFN_SET_CRITICAL_SECTION_SPIN_COUNT pfn = NULL;
  368. //
  369. // load kernel32 and get NT specific entry points
  370. //
  371. HINSTANCE tmpInstance = LoadLibrary("kernel32.dll");
  372. if (tmpInstance != NULL)
  373. {
  374. pfn = (PFN_SET_CRITICAL_SECTION_SPIN_COUNT)
  375. GetProcAddress(tmpInstance, "SetCriticalSectionSpinCount");
  376. //
  377. // We can free this because we are statically linked to it
  378. //
  379. FreeLibrary(tmpInstance);
  380. }
  381. // fallback to thunking to the dummy routine
  382. g_pfnSetCSSpinCount = ((pfn != NULL)
  383. ? pfn
  384. : FakeSetCriticalSectionSpinCount);
  385. }
  386. // Pass the inputs to the global function pointer which is already setup.
  387. return g_pfnSetCSSpinCount(pcs, dwSpinCount);
  388. }
  389. // CRtlResource static member variables
  390. LOCK_DEFAULT_SPIN_DATA(CRtlResource);
  391. LOCK_STATISTICS_DATA(CRtlResource);
  392. LOCK_STATISTICS_DUMMY_IMPLEMENTATION(CRtlResource);
  393. // CShareLock static member variables
  394. LOCK_DEFAULT_SPIN_DATA(CShareLock);
  395. LOCK_STATISTICS_DATA(CShareLock);
  396. LOCK_STATISTICS_DUMMY_IMPLEMENTATION(CShareLock);
  397. // CReaderWriterLock static member variables
  398. LOCK_DEFAULT_SPIN_DATA(CReaderWriterLock);
  399. LOCK_STATISTICS_DATA(CReaderWriterLock);
  400. LOCK_STATISTICS_REAL_IMPLEMENTATION(CReaderWriterLock);
  401. void
  402. CReaderWriterLock::_LockSpin(
  403. bool fWrite)
  404. {
  405. LOCK_INSTRUMENTATION_PROLOG();
  406. DWORD dwSleepTime = 0;
  407. LONG cBaseSpins = static_cast<LONG>(sm_wDefaultSpinCount
  408. * RandomBackoffFactor());
  409. LONG cSpins = cBaseSpins;
  410. for (;;)
  411. {
  412. if (NumProcessors() < 2 || sm_wDefaultSpinCount == LOCK_DONT_SPIN)
  413. cSpins = 1; // must loop once to call _TryRWLock
  414. for (int i = cSpins; --i >= 0; )
  415. {
  416. bool fLock = fWrite ? _TryWriteLock() : _TryReadLock();
  417. if (fLock)
  418. {
  419. #ifdef LOCK_INSTRUMENTATION
  420. cTotalSpins += (cSpins - i - 1);
  421. #endif // LOCK_INSTRUMENTATION
  422. goto locked;
  423. }
  424. Lock_Yield();
  425. }
  426. #ifdef LOCK_INSTRUMENTATION
  427. cTotalSpins += cBaseSpins;
  428. ++cSleeps;
  429. #endif // LOCK_INSTRUMENTATION
  430. Sleep(dwSleepTime) ;
  431. dwSleepTime = !dwSleepTime; // Avoid priority inversion: 0, 1, 0, 1,...
  432. // Backoff algorithm: reduce (or increase) busy wait time
  433. cBaseSpins = (int) (cBaseSpins * sm_dblDfltSpinAdjFctr);
  434. // LOCK_MINIMUM_SPINS <= cBaseSpins <= LOCK_MAXIMUM_SPINS
  435. cBaseSpins = min(LOCK_MAXIMUM_SPINS, cBaseSpins);
  436. cBaseSpins = max(cBaseSpins, LOCK_MINIMUM_SPINS);
  437. cSpins = cBaseSpins;
  438. }
  439. locked:
  440. IRTLASSERT(fWrite ? IsWriteLocked() : IsReadLocked());
  441. LOCK_INSTRUMENTATION_EPILOG();
  442. }
  443. // CReaderWriterLock2 static member variables
  444. LOCK_DEFAULT_SPIN_DATA(CReaderWriterLock2);
  445. LOCK_STATISTICS_DATA(CReaderWriterLock2);
  446. LOCK_STATISTICS_REAL_IMPLEMENTATION(CReaderWriterLock2);
  447. void
  448. CReaderWriterLock2::_WriteLockSpin()
  449. {
  450. // Add ourselves to the queue of waiting writers
  451. for (LONG l = m_lRW; !_CmpExch(l + SL_WRITER_INCR, l); l = m_lRW)
  452. {
  453. Lock_Yield();
  454. }
  455. _LockSpin(true);
  456. }
  457. void
  458. CReaderWriterLock2::_LockSpin(
  459. bool fWrite)
  460. {
  461. LOCK_INSTRUMENTATION_PROLOG();
  462. DWORD dwSleepTime = 0;
  463. LONG cBaseSpins = static_cast<LONG>(sm_wDefaultSpinCount
  464. * RandomBackoffFactor());
  465. LONG cSpins = cBaseSpins;
  466. for (;;)
  467. {
  468. if (NumProcessors() < 2 || sm_wDefaultSpinCount == LOCK_DONT_SPIN)
  469. cSpins = 1; // must loop once to call _TryRWLock
  470. for (int i = cSpins; --i >= 0; )
  471. {
  472. bool fLock = fWrite ? _TryWriteLock(0) : _TryReadLock();
  473. if (fLock)
  474. {
  475. #ifdef LOCK_INSTRUMENTATION
  476. cTotalSpins += (cSpins - i - 1);
  477. #endif // LOCK_INSTRUMENTATION
  478. goto locked;
  479. }
  480. Lock_Yield();
  481. }
  482. #ifdef LOCK_INSTRUMENTATION
  483. cTotalSpins += cBaseSpins;
  484. ++cSleeps;
  485. #endif // LOCK_INSTRUMENTATION
  486. Sleep(dwSleepTime) ;
  487. dwSleepTime = !dwSleepTime; // Avoid priority inversion: 0, 1, 0, 1,...
  488. // Backoff algorithm: reduce (or increase) busy wait time
  489. cBaseSpins = (int) (cBaseSpins * sm_dblDfltSpinAdjFctr);
  490. // LOCK_MINIMUM_SPINS <= cBaseSpins <= LOCK_MAXIMUM_SPINS
  491. cBaseSpins = min(LOCK_MAXIMUM_SPINS, cBaseSpins);
  492. cBaseSpins = max(cBaseSpins, LOCK_MINIMUM_SPINS);
  493. cSpins = cBaseSpins;
  494. }
  495. locked:
  496. IRTLASSERT(fWrite ? IsWriteLocked() : IsReadLocked());
  497. LOCK_INSTRUMENTATION_EPILOG();
  498. }
  499. // CReaderWriterLock3 static member variables
  500. LOCK_DEFAULT_SPIN_DATA(CReaderWriterLock3);
  501. LOCK_STATISTICS_DATA(CReaderWriterLock3);
  502. LOCK_STATISTICS_REAL_IMPLEMENTATION(CReaderWriterLock3);
  503. void
  504. CReaderWriterLock3::_WriteLockSpin()
  505. {
  506. // Add ourselves to the queue of waiting writers
  507. for (LONG l = m_lRW; !_CmpExch(l + SL_WRITER_INCR, l); l = m_lRW)
  508. {
  509. Lock_Yield();
  510. }
  511. _LockSpin(true);
  512. }
  513. void
  514. CReaderWriterLock3::_LockSpin(
  515. bool fWrite)
  516. {
  517. LOCK_INSTRUMENTATION_PROLOG();
  518. DWORD dwSleepTime = 0;
  519. LONG cBaseSpins = static_cast<LONG>(sm_wDefaultSpinCount
  520. * RandomBackoffFactor());
  521. LONG cSpins = cBaseSpins;
  522. for (;;)
  523. {
  524. if (NumProcessors() < 2 || sm_wDefaultSpinCount == LOCK_DONT_SPIN)
  525. cSpins = 1; // must loop once to call _TryRWLock
  526. for (int i = cSpins; --i >= 0; )
  527. {
  528. bool fLock = fWrite ? _TryWriteLock(0) : _TryReadLock();
  529. if (fLock)
  530. {
  531. #ifdef LOCK_INSTRUMENTATION
  532. cTotalSpins += (cSpins - i - 1);
  533. #endif // LOCK_INSTRUMENTATION
  534. goto locked;
  535. }
  536. Lock_Yield();
  537. }
  538. #ifdef LOCK_INSTRUMENTATION
  539. cTotalSpins += cBaseSpins;
  540. ++cSleeps;
  541. #endif // LOCK_INSTRUMENTATION
  542. Sleep(dwSleepTime) ;
  543. dwSleepTime = !dwSleepTime; // Avoid priority inversion: 0, 1, 0, 1,...
  544. // Backoff algorithm: reduce (or increase) busy wait time
  545. cBaseSpins = (int) (cBaseSpins * sm_dblDfltSpinAdjFctr);
  546. // LOCK_MINIMUM_SPINS <= cBaseSpins <= LOCK_MAXIMUM_SPINS
  547. cBaseSpins = min(LOCK_MAXIMUM_SPINS, cBaseSpins);
  548. cBaseSpins = max(cBaseSpins, LOCK_MINIMUM_SPINS);
  549. cSpins = cBaseSpins;
  550. }
  551. locked:
  552. IRTLASSERT(fWrite ? IsWriteLocked() : IsReadLocked());
  553. LOCK_INSTRUMENTATION_EPILOG();
  554. }
  555. #ifdef __LOCKS_NAMESPACE__
  556. }
  557. #endif // __LOCKS_NAMESPACE__