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.

345 lines
11 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 "Spinlock.hpp"
  26. /********************************************************************/
  27. /* */
  28. /* Class constructor. */
  29. /* */
  30. /* Create a new lock and initialize it. This call is not */
  31. /* thread safe and should only be made in a single thread */
  32. /* environment. */
  33. /* */
  34. /********************************************************************/
  35. SPINLOCK::SPINLOCK( SBIT32 NewMaxSpins,SBIT32 NewMaxUsers )
  36. {
  37. //
  38. // Set the initial state.
  39. //
  40. MaxSpins = NewMaxSpins;
  41. MaxUsers = NewMaxUsers;
  42. #ifdef ENABLE_RECURSIVE_LOCKS
  43. Owner = NULL;
  44. Recursive = 0;
  45. #endif
  46. Spinlock = LockOpen;
  47. Semaphore = NULL;
  48. Waiting = 0;
  49. #ifdef ENABLE_LOCK_STATISTICS
  50. //
  51. // Zero the lock statistics.
  52. //
  53. TotalLocks = 0;
  54. TotalSleeps = 0;
  55. TotalSpins = 0;
  56. TotalTimeouts = 0;
  57. TotalWaits = 0;
  58. #endif
  59. }
  60. /********************************************************************/
  61. /* */
  62. /* Update the semahore. */
  63. /* */
  64. /* We only create the semaphore on first use. So when we need */
  65. /* need to create a new semaphore any thread that is trying */
  66. /* to sleep on it comes here. */
  67. /* */
  68. /********************************************************************/
  69. VOID SPINLOCK::UpdateSemaphore( VOID )
  70. {
  71. STATIC SBIT32 Active = 0;
  72. //
  73. // We verify that there is still no semaphore
  74. // otherwise we exit.
  75. //
  76. while ( Semaphore == NULL )
  77. {
  78. //
  79. // We increment the active count and if we
  80. // are first we are selected for special duty.
  81. //
  82. if ( (AtomicIncrement( & Active ) == 1) && (Semaphore == NULL) )
  83. {
  84. //
  85. // We try to create a new semaphore. If
  86. // we fail we still exit.
  87. //
  88. Semaphore = CreateSemaphore( NULL,0,MaxUsers,NULL );
  89. //
  90. // Decrement the active count and exit.
  91. //
  92. AtomicDecrement( & Active );
  93. return;
  94. }
  95. else
  96. {
  97. //
  98. // Decrement the active count and exit.
  99. //
  100. AtomicDecrement( & Active );
  101. Sleep( 1 );
  102. }
  103. }
  104. }
  105. /********************************************************************/
  106. /* */
  107. /* Wait for the spinlock. */
  108. /* */
  109. /* Wait for the spinlock to become free and then claim it. */
  110. /* */
  111. /********************************************************************/
  112. BOOLEAN SPINLOCK::WaitForLock( SBIT32 Sleep )
  113. {
  114. REGISTER LONG Cpus = ((LONG) NumberOfCpus());
  115. #ifdef ENABLE_LOCK_STATISTICS
  116. REGISTER SBIT32 Sleeps = 0;
  117. REGISTER SBIT32 Spins = 0;
  118. REGISTER SBIT32 Waits = 0;
  119. #endif
  120. do
  121. {
  122. REGISTER SBIT32 Count;
  123. //
  124. // If there are already more threads waiting
  125. // than the number of CPUs then the odds of
  126. // getting the lock by spinning are slim, when
  127. // there is only one CPU the chance is zero, so
  128. // just bypass this step.
  129. //
  130. if ( (Cpus > 1) && (Cpus > Waiting) )
  131. {
  132. //
  133. // Wait by spinning and repeatedly testing the
  134. // spinlock. We exit when the lock becomes free
  135. // or the spin limit is exceeded.
  136. //
  137. for
  138. (
  139. Count = MaxSpins;
  140. (Count > 0) && (Spinlock != LockOpen);
  141. Count --
  142. );
  143. #ifdef ENABLE_LOCK_STATISTICS
  144. //
  145. // Update the statistics.
  146. //
  147. Spins += (MaxSpins - Count);
  148. Waits ++;
  149. #endif
  150. }
  151. else
  152. { Count = 0; }
  153. //
  154. // We have exhusted our spin count so it is time to
  155. // sleep waiting for the lock to clear.
  156. //
  157. if ( Count == 0 )
  158. {
  159. //
  160. // We do not create the semaphore until
  161. // somebody tries to sleep on it for the
  162. // first time.
  163. //
  164. if ( Semaphore == NULL )
  165. { UpdateSemaphore(); }
  166. //
  167. // We would normally hope to find a semaphore
  168. // avaiable ready for a sleep but the OS may
  169. // decline the request. If this is the case
  170. // try the lock again.
  171. //
  172. if ( Semaphore != NULL )
  173. {
  174. //
  175. // The lock is still closed so lets go to sleep on
  176. // a semaphore. However, we must first increment
  177. // the waiting count and test the lock one last time
  178. // to make sure it is still busy and there is someone
  179. // to wake us up later.
  180. //
  181. (VOID) AtomicIncrement( & Waiting );
  182. if ( ! ClaimSpinlock( & Spinlock ) )
  183. {
  184. if
  185. (
  186. WaitForSingleObject( Semaphore, Sleep )
  187. !=
  188. WAIT_OBJECT_0
  189. )
  190. {
  191. #ifdef ENABLE_LOCK_STATISTICS
  192. //
  193. // Count the number of times we have
  194. // timed out on this lock.
  195. //
  196. (VOID) AtomicIncrement( & TotalTimeouts );
  197. #endif
  198. return False;
  199. }
  200. #ifdef ENABLE_LOCK_STATISTICS
  201. //
  202. // Update the statistics.
  203. //
  204. Sleeps ++;
  205. #endif
  206. }
  207. else
  208. {
  209. //
  210. // Lucky - got the lock on the last attempt.
  211. // Hence, lets decrement the sleep count and
  212. // exit.
  213. //
  214. (VOID) AtomicDecrement( & Waiting );
  215. break;
  216. }
  217. }
  218. }
  219. }
  220. while ( ! ClaimSpinlock( & Spinlock ) );
  221. #ifdef ENABLE_LOCK_STATISTICS
  222. //
  223. // Update the statistics.
  224. //
  225. TotalSleeps += Sleeps;
  226. TotalSpins += Spins;
  227. TotalWaits += Waits;
  228. #endif
  229. return True;
  230. }
  231. /********************************************************************/
  232. /* */
  233. /* Wake all sleepers. */
  234. /* */
  235. /* Wake all the sleepers who are waiting for the spinlock. */
  236. /* All sleepers are woken because this is much more efficent */
  237. /* and it is known that the lock latency is short. */
  238. /* */
  239. /********************************************************************/
  240. VOID SPINLOCK::WakeAllSleepers( VOID )
  241. {
  242. REGISTER LONG Wakeup = AtomicExchange( & Waiting, 0 );
  243. //
  244. // We make sure there is still someone to be woken
  245. // up if not we check that the count has not become
  246. // negative.
  247. //
  248. if ( Wakeup > 0 )
  249. {
  250. REGISTER LONG Cpus = ((LONG) NumberOfCpus());
  251. //
  252. // We will only wake enough threads to ensure that
  253. // there is one active thread per CPU. So if an
  254. // application has hundreds of threads we will try
  255. // prevent the system from becoming swampped.
  256. //
  257. if ( Wakeup > Cpus )
  258. {
  259. (VOID) AtomicAdd( & Waiting,(Wakeup - Cpus) );
  260. Wakeup = Cpus;
  261. }
  262. //
  263. // Wake up all sleepers as the lock has just been freed.
  264. // It is a straight race to decide who gets the lock next.
  265. //
  266. if ( ! ReleaseSemaphore( Semaphore, Wakeup, NULL ) )
  267. { Failure( "Wakeup failed in ReleaseLock()" ); }
  268. }
  269. else
  270. {
  271. //
  272. // When multiple threads pass through the critical
  273. // section it is possible for the 'Waiting' count
  274. // to become negative. This should be very rare but
  275. // such a negative value needs to be preserved.
  276. //
  277. if ( Wakeup < 0 )
  278. { (VOID) AtomicAdd( & Waiting, Wakeup ); }
  279. }
  280. }
  281. /********************************************************************/
  282. /* */
  283. /* Class destructor. */
  284. /* */
  285. /* Destory a lock. This call is not thread safe and should */
  286. /* only be made in a single thread environment. */
  287. /* */
  288. /********************************************************************/
  289. SPINLOCK::~SPINLOCK( VOID )
  290. {
  291. #ifdef ENABLE_LOCK_STATISTICS
  292. //
  293. // Print the lock statistics.
  294. //
  295. DebugPrint
  296. (
  297. "Spinlock: %d locks, %d timouts, "
  298. "%d locks per wait, %d spins per wait, %d waits per sleep.\n",
  299. TotalLocks,
  300. TotalTimeouts,
  301. (TotalLocks / ((TotalWaits <= 0) ? 1 : TotalWaits)),
  302. (TotalSpins / ((TotalWaits <= 0) ? 1 : TotalWaits)),
  303. (TotalWaits / ((TotalSleeps <= 0) ? 1 : TotalSleeps))
  304. );
  305. #endif
  306. //
  307. // Close the semaphore handle.
  308. //
  309. if ( (Semaphore != NULL) && (! CloseHandle( Semaphore )) )
  310. { Failure( "Close semaphore in destructor for SPINLOCK" ); }
  311. }