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.

416 lines
10 KiB

  1. // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  2. //
  3. // SYNCHRO.CPP
  4. //
  5. // Copyright 1986-1997 Microsoft Corporation, All Rights Reserved
  6. //
  7. #include "_synchro.h"
  8. #include <ex\synchro.h>
  9. // ========================================================================
  10. //
  11. // CLASS CMRWLock
  12. //
  13. // EnterRead()/LeaveRead() respectively lets a reader enter or leave
  14. // the lock. If there is a writer or promotable reader in the lock,
  15. // entry is delayed until the writer/promotable reader leaves.
  16. //
  17. // EnterWrite()/LeaveWrite() respectively lets a single writer enter
  18. // or leave the lock. If there are any readers in the lock, entry
  19. // is delayed until they leave. If there is another writer or a
  20. // promotable reader in the lock, entry is delayed until it leaves.
  21. //
  22. // EnterPromote()/LeavePromote() respectively lets a single promotable
  23. // reader enter or leave the lock. If there is a writer or another
  24. // promotable reader in the lock, entry is delayed until the
  25. // writer/promotable reader leaves. Otherwise entry is immediate,
  26. // even if there are other (non-promotable) readers in the lock.
  27. // Promote() promotes the promotable reader to a writer. If there
  28. // are readers in the lock, promotion is delayed until they leave.
  29. //
  30. // Once a writer or promotable reader has entered the lock, it may
  31. // reenter the lock as a reader, writer or promotable reader without
  32. // delay. A reader cannot reenter the lock as a writer or promotable.
  33. //
  34. // ------------------------------------------------------------------------
  35. //
  36. // CMRWLock::CMRWLock()
  37. //
  38. CMRWLock::CMRWLock() :
  39. m_lcReaders(0),
  40. m_dwWriteLockOwner(0),
  41. m_dwPromoterRecursion(0)
  42. {
  43. }
  44. // ------------------------------------------------------------------------
  45. //
  46. // CMRWLock::FInitialize()
  47. //
  48. BOOL
  49. CMRWLock::FInitialize()
  50. {
  51. return m_evtEnableReaders.FCreate( NULL, // default security
  52. TRUE, // manual-reset
  53. FALSE, // initially non-signalled
  54. NULL ) // unnamed
  55. && m_evtEnableWriter.FCreate( NULL, // default security
  56. FALSE, // auto-reset
  57. FALSE, // initially non-signalled
  58. NULL ); // unnamed
  59. }
  60. // ------------------------------------------------------------------------
  61. //
  62. // CMRWLock::EnterRead()
  63. //
  64. void
  65. CMRWLock::EnterRead()
  66. {
  67. (void) FAcquireReadLock(TRUE); // fBlock
  68. }
  69. // ------------------------------------------------------------------------
  70. //
  71. // CMRWLock::FTryEnterRead()
  72. //
  73. BOOL
  74. CMRWLock::FTryEnterRead()
  75. {
  76. return FAcquireReadLock(FALSE); // fBlock
  77. }
  78. // ------------------------------------------------------------------------
  79. //
  80. // CMRWLock::FAcquireReadLock()
  81. //
  82. BOOL
  83. CMRWLock::FAcquireReadLock(BOOL fAllowCallToBlock)
  84. {
  85. //
  86. // Loop around trying to enter the lock until successful
  87. //
  88. for ( ;; )
  89. {
  90. //
  91. // Poll the reader count/write lock
  92. //
  93. LONG lcReaders = m_lcReaders;
  94. //
  95. // If the write lock is held ...
  96. //
  97. if ( lcReaders & WRITE_LOCKED )
  98. {
  99. //
  100. // ... check whether the writer is on this thread.
  101. // If it is, then let this thread reenter the
  102. // lock as a reader. Do not update the reader
  103. // count in this case.
  104. //
  105. if ( m_dwWriteLockOwner == GetCurrentThreadId() )
  106. break;
  107. //
  108. // If the writer is not on this thread, then wait
  109. // until the writer leaves, then re-poll the
  110. // reader count/write lock and try again.
  111. //
  112. // We only block if the caller allows us to block. If this is
  113. // a FTryEnterRead call, we return FALSE right away.
  114. //
  115. if ( fAllowCallToBlock )
  116. {
  117. m_evtEnableReaders.Wait();
  118. }
  119. else
  120. {
  121. return FALSE;
  122. }
  123. }
  124. //
  125. // Otherwise, the write lock was not held, so
  126. // try to enter the lock as a reader. This only
  127. // succeeds when no readers or writers enter or leave
  128. // the lock between the time the reader count/
  129. // write lock is polled above and now. If what is in
  130. // the lock has changed, the whole operation is retried
  131. // until the lock state doesn't change.
  132. //
  133. else
  134. {
  135. if ( lcReaders == /*reinterpret_cast<LONG>*/(
  136. InterlockedCompareExchange(
  137. (&m_lcReaders),
  138. (lcReaders + 1),
  139. (lcReaders))) )
  140. #ifdef NEVER
  141. reinterpret_cast<PVOID *>(&m_lcReaders),
  142. reinterpret_cast<PVOID>(lcReaders + 1),
  143. reinterpret_cast<PVOID>(lcReaders))) )
  144. #endif // NEVER
  145. {
  146. break;
  147. }
  148. }
  149. }
  150. // If we made it to this point, we have acquired the read lock.
  151. //
  152. return TRUE;
  153. }
  154. // ------------------------------------------------------------------------
  155. //
  156. // CMRWLock::LeaveRead()
  157. //
  158. void
  159. CMRWLock::LeaveRead()
  160. {
  161. //
  162. // If the thread on which the reader is leaving also owns
  163. // the write lock, then the reader leaving has no effect,
  164. // as did entering.
  165. //
  166. if ( m_dwWriteLockOwner == GetCurrentThreadId() )
  167. return;
  168. //
  169. // Otherwise, atomically decrement the reader count and
  170. // check if a writer is waiting to enter the lock.
  171. // If the reader count goes to 0 and a writer is waiting
  172. // to enter the lock, then notify the writer that
  173. // it is safe to enter.
  174. //
  175. if ( WRITE_LOCKED == InterlockedDecrement(&m_lcReaders) )
  176. m_evtEnableWriter.Set();
  177. }
  178. // ------------------------------------------------------------------------
  179. //
  180. // CMRWLock::EnterWrite()
  181. //
  182. void
  183. CMRWLock::EnterWrite()
  184. {
  185. //
  186. // A writer is just a promotable reader that promotes immediately
  187. //
  188. EnterPromote();
  189. Promote();
  190. }
  191. // ------------------------------------------------------------------------
  192. //
  193. // CMRWLock::FTryEnterWrite()
  194. //
  195. BOOL
  196. CMRWLock::FTryEnterWrite()
  197. {
  198. BOOL fSuccess;
  199. //
  200. // Try to enter the lock as a promotable reader.
  201. // Promote to a writer immediately if successful
  202. // and return the status of the operation.
  203. //
  204. fSuccess = FTryEnterPromote();
  205. if ( fSuccess )
  206. Promote();
  207. return fSuccess;
  208. }
  209. // ------------------------------------------------------------------------
  210. //
  211. // CMRWLock::LeaveWrite()
  212. //
  213. void
  214. CMRWLock::LeaveWrite()
  215. {
  216. LeavePromote();
  217. }
  218. // ------------------------------------------------------------------------
  219. //
  220. // CMRWLock::EnterPromote()
  221. //
  222. void
  223. CMRWLock::EnterPromote()
  224. {
  225. //
  226. // Grab the writer critical section to ensure that no other thread
  227. // is already in the lock as a writer or promotable reader.
  228. //
  229. m_csWriter.Enter();
  230. //
  231. // Bump the promoter recursion count
  232. //
  233. ++m_dwPromoterRecursion;
  234. }
  235. // ------------------------------------------------------------------------
  236. //
  237. // CMRWLock::FTryEnterPromote()
  238. //
  239. BOOL
  240. CMRWLock::FTryEnterPromote()
  241. {
  242. BOOL fSuccess;
  243. //
  244. // Try to enter the writer critical section.
  245. // Bump the recursion count if successful and
  246. // return the status of the operation.
  247. //
  248. fSuccess = m_csWriter.FTryEnter();
  249. if ( fSuccess )
  250. ++m_dwPromoterRecursion;
  251. return fSuccess;
  252. }
  253. // ------------------------------------------------------------------------
  254. //
  255. // CMRWLock::Promote()
  256. //
  257. void
  258. CMRWLock::Promote()
  259. {
  260. //
  261. // If the promotable reader has already been promoted
  262. // then don't promote it again
  263. //
  264. if ( GetCurrentThreadId() == m_dwWriteLockOwner )
  265. return;
  266. //
  267. // Assert that no other writer owns the lock.
  268. //
  269. Assert( 0 == m_dwWriteLockOwner );
  270. Assert( !(m_lcReaders & WRITE_LOCKED) );
  271. //
  272. // Claim the lock
  273. //
  274. m_dwWriteLockOwner = GetCurrentThreadId();
  275. //
  276. // Stop readers from entering the lock
  277. //
  278. m_evtEnableReaders.Reset();
  279. //
  280. // If there are any readers in the lock
  281. // then wait for them to leave. The InterlockedExchangeOr()
  282. // is used to ensure that the test is atomic.
  283. //
  284. if ( InterlockedExchangeOr( &m_lcReaders, WRITE_LOCKED ) )
  285. m_evtEnableWriter.Wait();
  286. //
  287. // Assert that the (promoted) writer is now the only thing in the lock
  288. //
  289. Assert( WRITE_LOCKED == m_lcReaders );
  290. }
  291. // ------------------------------------------------------------------------
  292. //
  293. // CMRWLock::LeavePromote()
  294. //
  295. void
  296. CMRWLock::LeavePromote()
  297. {
  298. //
  299. // No one should attempt to leave a promote block
  300. // that they never entered.
  301. //
  302. Assert( m_dwPromoterRecursion > 0 );
  303. //
  304. // If the promotable reader promoted to a writer
  305. // then start allowing readers back into the lock
  306. // once the promoter recursion count reaches 0
  307. //
  308. if ( --m_dwPromoterRecursion == 0 &&
  309. GetCurrentThreadId() == m_dwWriteLockOwner )
  310. {
  311. //
  312. // Clear the write flag to allow new readers
  313. // to start entering the lock.
  314. //
  315. m_lcReaders = 0;
  316. //
  317. // Unblock any threads with readers that are
  318. // already waiting to enter the lock.
  319. //
  320. m_evtEnableReaders.Set();
  321. //
  322. // Release ownership of the write lock
  323. //
  324. m_dwWriteLockOwner = 0;
  325. }
  326. //
  327. // Release the writer's/promoter's critical section reference.
  328. // When this the last such reference is released, a new
  329. // promoter/writer may enter the lock.
  330. //
  331. m_csWriter.Leave();
  332. }
  333. // ========================================================================
  334. //
  335. // FREE FUNCTIONS
  336. //
  337. // ------------------------------------------------------------------------
  338. //
  339. // InterlockedExchangeOr()
  340. //
  341. // This function performs an atomic logical OR of a value to a variable.
  342. // The function prevents more than one thread from using the same
  343. // variable simultaneously. (Well, actually, it spins until it
  344. // gets a consistent result, but who's counting...)
  345. //
  346. // Returns the value of the variable before the logical OR was performed
  347. //
  348. LONG InterlockedExchangeOr( LONG * plVariable, LONG lOrBits )
  349. {
  350. // The rather cryptic way this works is:
  351. //
  352. // Get the instantaneous value of the variable. Stuff it into a
  353. // local variable so that it cannot be changed by another thread.
  354. // Then try to replace the variable with this value OR'd together
  355. // with the OR bits. But only replace if the variable's value
  356. // is still the same as the local variable. If it isn't, then
  357. // another thread must have changed the value between the two
  358. // operations, so keep trying until they both succeed as if
  359. // they had executed as one. Once the operation succeeds in
  360. // changing the value atomically, return the previous value.
  361. //
  362. for ( ;; )
  363. {
  364. LONG lValue = *plVariable;
  365. if ( lValue == /*reinterpret_cast<LONG>*/(
  366. InterlockedCompareExchange(
  367. (plVariable),
  368. (lValue | lOrBits),
  369. (lValue))) )
  370. #ifdef NEVER
  371. reinterpret_cast<PVOID *>(plVariable),
  372. reinterpret_cast<PVOID>(lValue | lOrBits),
  373. reinterpret_cast<PVOID>(lValue))) )
  374. #endif // NEVER
  375. return lValue;
  376. }
  377. }