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.

596 lines
18 KiB

  1. // Ruler
  2. // 1 2 3 4 5 6 7 8
  3. //345678901234567890123456789012345678901234567890123456789012345678901234567890
  4. /********************************************************************/
  5. /* */
  6. /* The standard layout. */
  7. /* */
  8. /* The standard layout for 'cpp' files in this code is as */
  9. /* follows: */
  10. /* */
  11. /* 1. Include files. */
  12. /* 2. Constants local to the class. */
  13. /* 3. Data structures local to the class. */
  14. /* 4. Data initializations. */
  15. /* 5. Static functions. */
  16. /* 6. Class functions. */
  17. /* */
  18. /* The constructor is typically the first function, class */
  19. /* member functions appear in alphabetical order with the */
  20. /* destructor appearing at the end of the file. Any section */
  21. /* or function this is not required is simply omitted. */
  22. /* */
  23. /********************************************************************/
  24. #include "LibraryPCH.hpp"
  25. #include "Sharelock.hpp"
  26. /********************************************************************/
  27. /* */
  28. /* Constants local to the class. */
  29. /* */
  30. /* The Windows NT kernel requires a maximum wakeup count when */
  31. /* creating a semaphore. */
  32. /* */
  33. /********************************************************************/
  34. CONST SBIT32 MaxShareLockUsers = 256;
  35. /********************************************************************/
  36. /* */
  37. /* Class constructor. */
  38. /* */
  39. /* Create a new lock and initialize it. This call is not */
  40. /* thread safe and should only be made in a single thread */
  41. /* environment. */
  42. /* */
  43. /********************************************************************/
  44. SHARELOCK::SHARELOCK( SBIT32 NewMaxSpins, SBIT32 NewMaxUsers )
  45. {
  46. //
  47. // Check the configurable values.
  48. //
  49. if ( NewMaxSpins > 0 )
  50. { MaxSpins = NewMaxSpins; }
  51. else
  52. { Failure( "Maximum spins invalid in constructor for SHARELOCK" ); }
  53. if ( (NewMaxUsers > 0) && (NewMaxUsers <= MaxShareLockUsers) )
  54. { MaxUsers = NewMaxUsers; }
  55. else
  56. { Failure( "Maximum share invalid in constructor for SHARELOCK" ); }
  57. //
  58. // Set the initial state.
  59. //
  60. Exclusive = 0;
  61. TotalUsers = 0;
  62. #ifdef ENABLE_RECURSIVE_LOCKS
  63. Owner = NULL;
  64. Recursive = 0;
  65. #endif
  66. Semaphore = NULL;
  67. Waiting = 0;
  68. #ifdef ENABLE_LOCK_STATISTICS
  69. //
  70. // Zero the statistics.
  71. //
  72. TotalExclusiveLocks = 0;
  73. TotalShareLocks = 0;
  74. TotalSleeps = 0;
  75. TotalSpins = 0;
  76. TotalTimeouts = 0;
  77. TotalWaits = 0;
  78. #endif
  79. }
  80. /********************************************************************/
  81. /* */
  82. /* Sleep waiting for the lock. */
  83. /* */
  84. /* We have decided it is time to sleep waiting for the lock */
  85. /* to become free. */
  86. /* */
  87. /********************************************************************/
  88. BOOLEAN SHARELOCK::SleepWaitingForLock( SBIT32 Sleep )
  89. {
  90. //
  91. // We do not create the semaphore until somebody tries
  92. // to sleep on it for the first time.
  93. //
  94. if ( Semaphore == NULL )
  95. { UpdateSemaphore(); }
  96. //
  97. // We would normally hope to find a semaphore avaiable
  98. // ready for a sleep but the OS may decline the request
  99. // If this is the case we exit without sleeping.
  100. //
  101. if ( Semaphore != NULL )
  102. {
  103. //
  104. // We have been spinning waiting for the lock but it
  105. // has not become free. Hence, it is now time to
  106. // give up and sleep for a while.
  107. //
  108. (VOID) AtomicIncrement( & Waiting );
  109. //
  110. // Just before we go to sleep we do one final check
  111. // to make sure that the lock is still busy and that
  112. // there is someone to wake us up when it becomes free.
  113. //
  114. if ( TotalUsers > 0 )
  115. {
  116. #ifdef ENABLE_LOCK_STATISTICS
  117. //
  118. // Count the number of times we have slept on this lock.
  119. //
  120. (VOID) AtomicIncrement( & TotalSleeps );
  121. #endif
  122. //
  123. // When we sleep we awoken when the lock becomes free
  124. // or when we timeout. If we timeout we simply exit
  125. // after decrementing various counters.
  126. //
  127. if
  128. (
  129. WaitForSingleObject( Semaphore, Sleep )
  130. !=
  131. WAIT_OBJECT_0
  132. )
  133. {
  134. #ifdef ENABLE_LOCK_STATISTICS
  135. //
  136. // Count the number of times we have timed out
  137. // on this lock.
  138. //
  139. (VOID) AtomicIncrement( & TotalTimeouts );
  140. #endif
  141. return False;
  142. }
  143. }
  144. else
  145. {
  146. //
  147. // Lucky - the lock was just freed so lets
  148. // decrement the sleep count and exit without
  149. // sleeping.
  150. //
  151. (VOID) AtomicDecrement( & Waiting );
  152. }
  153. }
  154. return True;
  155. }
  156. /********************************************************************/
  157. /* */
  158. /* Update the spin limit. */
  159. /* */
  160. /* Update the maximum number of spins while waiting for the lock. */
  161. /* */
  162. /********************************************************************/
  163. BOOLEAN SHARELOCK::UpdateMaxSpins( SBIT32 NewMaxSpins )
  164. {
  165. if ( NewMaxSpins > 0 )
  166. {
  167. MaxSpins = NewMaxSpins;
  168. return True;
  169. }
  170. else
  171. { return False; }
  172. }
  173. /********************************************************************/
  174. /* */
  175. /* Update the sharing limit. */
  176. /* */
  177. /* Update the maximum number of users that can share the lock. */
  178. /* */
  179. /********************************************************************/
  180. BOOLEAN SHARELOCK::UpdateMaxUsers( SBIT32 NewMaxUsers )
  181. {
  182. //
  183. // We need to verify the new value makes sense.
  184. //
  185. if ( (NewMaxUsers > 0) && (NewMaxUsers <= MaxShareLockUsers) )
  186. {
  187. ClaimExclusiveLock();
  188. //
  189. // Update the maximum number of users.
  190. //
  191. MaxUsers = NewMaxUsers;
  192. ReleaseExclusiveLock();
  193. return True;
  194. }
  195. else
  196. { return False; }
  197. }
  198. /********************************************************************/
  199. /* */
  200. /* Update the semahore. */
  201. /* */
  202. /* We only create the semaphore on first use. So when we need */
  203. /* need to create a new semaphore any thread that is trying */
  204. /* to sleep on it comes here. */
  205. /* */
  206. /********************************************************************/
  207. VOID SHARELOCK::UpdateSemaphore( VOID )
  208. {
  209. STATIC SBIT32 Active = 0;
  210. //
  211. // We verify that there is still no semaphore
  212. // otherwise we exit.
  213. //
  214. while ( Semaphore == NULL )
  215. {
  216. //
  217. // We increment the active count and if we
  218. // are first we are selected for special duty.
  219. //
  220. if ( (AtomicIncrement( & Active ) == 1) && (Semaphore == NULL) )
  221. {
  222. //
  223. // We try to create a new semaphore. If
  224. // we fail we still exit.
  225. //
  226. Semaphore = CreateSemaphore( NULL,0,MaxShareLockUsers,NULL );
  227. //
  228. // Decrement the active count and exit.
  229. //
  230. AtomicDecrement( & Active );
  231. return;
  232. }
  233. else
  234. {
  235. //
  236. // Decrement the active count and exit.
  237. //
  238. AtomicDecrement( & Active );
  239. Sleep( 1 );
  240. }
  241. }
  242. }
  243. /********************************************************************/
  244. /* */
  245. /* Wait for an exclusive lock. */
  246. /* */
  247. /* Wait for the spinlock to become free and then claim it. */
  248. /* */
  249. /********************************************************************/
  250. BOOLEAN SHARELOCK::WaitForExclusiveLock( SBIT32 Sleep )
  251. {
  252. REGISTER LONG Cpus = ((LONG) NumberOfCpus());
  253. #ifdef ENABLE_LOCK_STATISTICS
  254. REGISTER SBIT32 Spins = 0;
  255. REGISTER SBIT32 Waits = 0;
  256. #endif
  257. //
  258. // We will loop round in this function until the
  259. // following condition becomes false.
  260. //
  261. while ( TotalUsers != 1 )
  262. {
  263. //
  264. // The lock is busy so release it and spin waiting
  265. // for it to become free.
  266. //
  267. (VOID) AtomicDecrement( & TotalUsers );
  268. //
  269. // We will only try spinning and sleeping if we
  270. // are permitted to do so by the parameters.
  271. //
  272. if ( Sleep != 0 )
  273. {
  274. REGISTER SBIT32 Count;
  275. //
  276. // If there are already more threads waiting
  277. // than the number of CPUs then the odds of
  278. // getting the lock by spinning are slim, when
  279. // there is only one CPU the chance is zero, so
  280. // just bypass this step.
  281. //
  282. if ( (Cpus > 1) && (Cpus > Waiting) )
  283. {
  284. //
  285. // Wait by spinning and repeatedly testing the
  286. // spinlock. We exit when the lock becomes free
  287. // or the spin limit is exceeded.
  288. //
  289. for
  290. (
  291. Count = MaxSpins;
  292. (Count > 0) && (TotalUsers > 0);
  293. Count --
  294. );
  295. #ifdef ENABLE_LOCK_STATISTICS
  296. //
  297. // Update the statistics.
  298. //
  299. Spins += (MaxSpins - Count);
  300. Waits ++;
  301. #endif
  302. }
  303. else
  304. { Count = 0; }
  305. //
  306. // We have exhusted our spin count so it is time to
  307. // sleep waiting for the lock to clear.
  308. //
  309. if ( Count == 0 )
  310. {
  311. //
  312. // We have decide that we need to sleep but are
  313. // still holding an exclusive lock so lets drop it
  314. // before sleeping.
  315. //
  316. (VOID) AtomicDecrement( & Exclusive );
  317. //
  318. // We have decied that it is time to go to sleep
  319. // when we wake up the lock should be available
  320. // (or just aquired) unless we have timed out in
  321. // wich case we exit.
  322. //
  323. if ( ! SleepWaitingForLock( Sleep ) )
  324. { return False; }
  325. //
  326. // We have woken up again so lets reclaim the
  327. // exclusive lock we had earlier.
  328. //
  329. (VOID) AtomicIncrement( & Exclusive );
  330. }
  331. }
  332. else
  333. {
  334. //
  335. // We have decide that we need to exit but are still
  336. // holding an exclusive lock. so lets drop it and leave.
  337. //
  338. (VOID) AtomicDecrement( & Exclusive );
  339. return False;
  340. }
  341. //
  342. // Lets test the lock again.
  343. //
  344. (VOID) AtomicIncrement( & TotalUsers );
  345. }
  346. #ifdef ENABLE_LOCK_STATISTICS
  347. //
  348. // Update the statistics.
  349. //
  350. (VOID) AtomicAdd( & TotalSpins, Spins );
  351. (VOID) AtomicAdd( & TotalWaits, Waits );
  352. #endif
  353. return True;
  354. }
  355. /********************************************************************/
  356. /* */
  357. /* Wait for a shared lock. */
  358. /* */
  359. /* Wait for the lock to become free and then claim it. */
  360. /* */
  361. /********************************************************************/
  362. BOOLEAN SHARELOCK::WaitForShareLock( SBIT32 Sleep )
  363. {
  364. REGISTER LONG Cpus = ((LONG) NumberOfCpus());
  365. #ifdef ENABLE_LOCK_STATISTICS
  366. REGISTER SBIT32 Spins = 0;
  367. REGISTER SBIT32 Waits = 0;
  368. #endif
  369. //
  370. // We will loop round in this function until the
  371. // following condition becomes false.
  372. //
  373. while ( (Exclusive > 0) || (TotalUsers > MaxUsers) )
  374. {
  375. //
  376. // The lock is busy so release it and spin waiting
  377. // for it to become free.
  378. //
  379. (VOID) AtomicDecrement( & TotalUsers );
  380. //
  381. // We will only try spinning and sleeping if we
  382. // are permitted to do so by the parameters.
  383. //
  384. if ( Sleep != 0 )
  385. {
  386. REGISTER SBIT32 Count;
  387. //
  388. // If there are already more threads waiting
  389. // than the number of CPUs then the odds of
  390. // getting the lock by spinning are slim, when
  391. // there is only one CPU the chance is zero, so
  392. // just bypass this step.
  393. //
  394. if ( (Cpus > 1) && (Cpus > Waiting) )
  395. {
  396. //
  397. // Wait by spinning and repeatedly testing the
  398. // spinlock. We exit when the lock becomes free
  399. // or the spin limit is exceeded.
  400. //
  401. for
  402. (
  403. Count = MaxSpins;
  404. (Count > 0)
  405. &&
  406. ((Exclusive > 0) || (TotalUsers >= MaxUsers));
  407. Count --
  408. );
  409. #ifdef ENABLE_LOCK_STATISTICS
  410. //
  411. // Update the statistics.
  412. //
  413. Spins += (MaxSpins - Count);
  414. Waits ++;
  415. #endif
  416. }
  417. else
  418. { Count = 0; }
  419. //
  420. // We have exhusted our spin count so it is time to
  421. // sleep waiting for the lock to clear.
  422. //
  423. if ( Count == 0 )
  424. {
  425. //
  426. // We have decied that it is time to go to sleep
  427. // when we wake up the lock should be available
  428. // (or just aquired) unless we have timed out in
  429. // wich case we exit.
  430. //
  431. if ( ! SleepWaitingForLock( Sleep ) )
  432. { return False; }
  433. }
  434. }
  435. else
  436. { return False; }
  437. //
  438. // Lets test the lock again.
  439. //
  440. (VOID) AtomicIncrement( & TotalUsers );
  441. }
  442. #ifdef ENABLE_LOCK_STATISTICS
  443. //
  444. // Update the statistics.
  445. //
  446. (VOID) AtomicAdd( & TotalSpins, Spins );
  447. (VOID) AtomicAdd( & TotalWaits, Waits );
  448. #endif
  449. return True;
  450. }
  451. /********************************************************************/
  452. /* */
  453. /* Wake all sleepers. */
  454. /* */
  455. /* Wake all the sleepers who are waiting for the spinlock. */
  456. /* All sleepers are woken because this is much more efficent */
  457. /* and it is known that the lock latency is short. */
  458. /* */
  459. /********************************************************************/
  460. VOID SHARELOCK::WakeAllSleepers( VOID )
  461. {
  462. REGISTER LONG Wakeup = AtomicExchange( & Waiting, 0 );
  463. //
  464. // We make sure there is still someone to be woken
  465. // up if not we check that the count has not become
  466. // negative.
  467. //
  468. if ( Wakeup > 0 )
  469. {
  470. REGISTER LONG Cpus = ((LONG) NumberOfCpus());
  471. //
  472. // We will only wake enough threads to ensure that
  473. // there is one active thread per CPU. So if an
  474. // application has hundreds of threads we will try
  475. // prevent the system from becoming swampped.
  476. //
  477. if ( Wakeup > Cpus )
  478. {
  479. (VOID) AtomicAdd( & Waiting,(Wakeup - Cpus) );
  480. Wakeup = Cpus;
  481. }
  482. //
  483. // Wake up all sleepers as the lock has just been freed.
  484. // It is a straight race to decide who gets the lock next.
  485. //
  486. if ( ! ReleaseSemaphore( Semaphore, Wakeup, NULL ) )
  487. { Failure( "Wakeup failed in ReleaseLock()" ); }
  488. }
  489. else
  490. {
  491. //
  492. // When multiple threads pass through the critical
  493. // section it is possible for the 'Waiting' count
  494. // to become negative. This should be very rare but
  495. // such a negative value needs to be preserved.
  496. //
  497. if ( Wakeup < 0 )
  498. { (VOID) AtomicAdd( & Waiting, Wakeup ); }
  499. }
  500. }
  501. /********************************************************************/
  502. /* */
  503. /* Class destructor. */
  504. /* */
  505. /* Destory a lock. This call is not thread safe and should */
  506. /* only be made in a single thread environment. */
  507. /* */
  508. /********************************************************************/
  509. SHARELOCK::~SHARELOCK( VOID )
  510. {
  511. #ifdef ENABLE_LOCK_STATISTICS
  512. //
  513. // Print the lock statistics.
  514. //
  515. DebugPrint
  516. (
  517. "Sharelock: %d exclusive, %d shared, %d timeouts, "
  518. "%d locks per wait, %d spins per wait, %d waits per sleep.\n",
  519. TotalExclusiveLocks,
  520. TotalShareLocks,
  521. TotalTimeouts,
  522. ((TotalExclusiveLocks + TotalShareLocks) / ((TotalWaits <= 0) ? 1 : TotalWaits)),
  523. (TotalSpins / ((TotalWaits <= 0) ? 1 : TotalWaits)),
  524. (TotalWaits / ((TotalSleeps <= 0) ? 1 : TotalSleeps))
  525. );
  526. #endif
  527. //
  528. // Close the semaphore handle.
  529. //
  530. if ( (Semaphore != NULL) && (! CloseHandle( Semaphore )) )
  531. { Failure( "Close semaphore in destructor for SHARELOCK" ); }
  532. }