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.

554 lines
14 KiB

  1. //++
  2. //
  3. // Copyright (c) 1989 Microsoft Corporation
  4. //
  5. // Module Name:
  6. //
  7. // critsect.s
  8. //
  9. // Abstract:
  10. //
  11. // This module implements functions to support user mode critical sections.
  12. //
  13. // Author:
  14. //
  15. // William K. Cheung (wcheung) 18-Sep-95
  16. //
  17. // Revision History:
  18. //
  19. // 07-Jul-97 bl Updated to EAS2.3
  20. //
  21. // 08-Feb-96 Updated to EAS2.1
  22. //
  23. //--
  24. #include "ksia64.h"
  25. .file "critsect.s"
  26. //
  27. // intra-module functions to be called.
  28. //
  29. PublicFunction(RtlpWaitForCriticalSection)
  30. PublicFunction(RtlpUnWaitCriticalSection)
  31. //++
  32. //
  33. // NTSTATUS
  34. // RtlEnterCriticalSection(
  35. // IN PRTL_CRITICAL_SECTION CriticalSection
  36. // )
  37. //
  38. // Routine Description:
  39. //
  40. // This function enters a critical section.
  41. //
  42. // Arguments:
  43. //
  44. // CriticalSection (a0) - supplies a pointer to a critical section.
  45. //
  46. // Return Value:
  47. //
  48. // STATUS_SUCCESS or raises an exception if an error occured.
  49. //
  50. // Algorithm in C:
  51. //
  52. // NTSTATUS
  53. // RtlEnterCriticalSection(
  54. // IN PRTL_CRITICAL_SECTION CriticalSection
  55. // )
  56. // {
  57. // PTEB Teb;
  58. // LONG OriginalValue;
  59. // HANDLE CurrentThreadId;
  60. // DWORD SpinCount;
  61. //
  62. // Teb = NtCurrentTeb();
  63. // CurrentThreadId = Teb->ClientId.UniqueThread;
  64. // SpinCount = CriticalSection->SpinCount;
  65. //
  66. // if (SpinCount == 0) {
  67. // nospin:
  68. // OriginalValue = AtomicIncrement(CriticalSection->LockCount);
  69. //
  70. // if (OriginalValue != -1) {
  71. // if (CriticalSection->OwningThread != CurrentThreadId) {
  72. // RtlpWaitForCriticalSection(CriticalSection);
  73. // CriticalSection->OwningThread = CurrentThreadId;
  74. // CriticalSection->RecursionCount = 0;
  75. // } else {
  76. // CriticalSection->RecursionCount++;
  77. // }
  78. // } else {
  79. // CriticalSection->OwningThread = CurrentThreadId;
  80. // CriticalSection->RecursionCount = 0;
  81. // }
  82. //
  83. // return STATUS_SUCCESS;
  84. //
  85. // } else { /* spin count not 0 */
  86. //
  87. // if (CriticalSection->OwningThread == CurrentThread) {
  88. // AtomicIncrement(CriticalSection->LockCount);
  89. // CriticalSection->RecursionCount++;
  90. // return STATUS_SUCCESS;
  91. // } else {
  92. // do {
  93. // if (!CmpXchg(CriticalSection->LockCount,0,-1)) {
  94. // Lock acquired
  95. // CriticalSection->OwningThread = CurrentThreadId;
  96. // CriticalSection->RecursionCount = 0;
  97. // return STATUS_SUCCESS;
  98. // }
  99. // if (CriticalSection->LockCount >= 1) goto nospin;
  100. //
  101. // while (CriticalSection->LockCount == 0) {
  102. // if (!--SpinCount) goto nospin;
  103. // }
  104. // } while (1) // CriticalSection->LockCount == -1, not owned
  105. // }
  106. // }
  107. // }
  108. //
  109. //--
  110. NESTED_ENTRY(RtlEnterCriticalSection)
  111. //
  112. // register aliases
  113. //
  114. rpThreadId=t3
  115. rOldLockCount=t4
  116. rpLockCount=t5
  117. rRecursionCount=t6
  118. rOwnerId=t7
  119. rpSpinCount=t9
  120. rSpinCount=t10
  121. rT1=t11
  122. rT2=t12
  123. pSpin=pt0
  124. pNoSpin=pt1
  125. pNotOwn=pt2
  126. pNotAcq=pt3
  127. pFree=pt5
  128. pHeld=pt6
  129. pGo=pt7
  130. pWait=pt8
  131. //
  132. // alloc regs and save brp & pfs
  133. //
  134. NESTED_SETUP(1,5,1,0)
  135. add rpLockCount = CsLockCount, a0
  136. ;;
  137. //
  138. // more register aliases
  139. //
  140. rThreadId = loc2
  141. rpOwner = loc3
  142. rpCsRecursion = loc4
  143. PROLOGUE_END
  144. //
  145. // Swizzle pointers to address the RecursionCount and
  146. // LockCount fields in the critical section structure.
  147. //
  148. lfetch.nt1 [rpLockCount]
  149. nop.f 0
  150. add rpSpinCount = CsSpinCount, a0
  151. add rpCsRecursion = CsRecursionCount, a0
  152. nop.f 0
  153. add rpThreadId = TeClientId+CidUniqueThread, teb
  154. ;;
  155. //
  156. // Load the id of the currently running thread, anticipating
  157. // that it may be needed.
  158. //
  159. ld8 rThreadId = [rpThreadId]
  160. ld4 rSpinCount = [rpSpinCount]
  161. add rpOwner = CsOwningThread, a0
  162. ;;
  163. //
  164. // Branch out if spin count is non-zero
  165. //
  166. cmp4.ne pSpin, pNoSpin = rSpinCount, zero
  167. mov v0 = STATUS_SUCCESS
  168. (pSpin) br.spnt RecsSpin
  169. //
  170. // Atomically increment the lock count at location (rpLockCount)
  171. // and put the old value in register "rOldLockCount".
  172. // Swizzle a pointer to address the thread id field in teb structure.
  173. //
  174. RecsNoSpin:
  175. fetchadd4.acq rOldLockCount = [rpLockCount], 1
  176. ld4.nt1 rRecursionCount = [rpCsRecursion]
  177. ;;
  178. //
  179. // Check the original value of lock count to determine if the
  180. // lock is free. If the value is -1, it is free.
  181. //
  182. cmp4.eq pFree, pHeld = -1, rOldLockCount
  183. ;;
  184. //
  185. // if lock is not free, get the thread id of its current owner
  186. // otherwise, save the currently running thread's id.
  187. //
  188. (pHeld) ld8 rOwnerId = [rpOwner]
  189. (pFree) st8 [rpOwner] = rThreadId
  190. (pFree) st4 [rpCsRecursion] = zero
  191. (pFree) br.ret.sptk.clr brp // return
  192. ;;
  193. //
  194. // if lock is not free, compare the owner id of the critical section against
  195. // that of the thread to determine if this thread is the owner.
  196. // otherwise, return to caller.
  197. //
  198. cmp.eq pGo, pWait = rThreadId, rOwnerId
  199. mov out0 = a0
  200. add rRecursionCount = 1, rRecursionCount
  201. ;;
  202. //
  203. // if the thread has already owned the lock, save the updated
  204. // recursion count and return.
  205. // otherwise, wait for the critical section to be released.
  206. //
  207. (pGo) st4 [rpCsRecursion] = rRecursionCount
  208. (pGo) br.ret.sptk brp
  209. (pWait) br.call.spnt.many brp = RtlpWaitForCriticalSection
  210. ;;
  211. st8.rel [rpOwner] = rThreadId
  212. st4 [rpCsRecursion] = zero
  213. mov v0 = STATUS_SUCCESS
  214. NESTED_RETURN
  215. // A nonzero spin count is specified
  216. //
  217. RecsSpin:
  218. ld8 rOwnerId = [rpOwner]
  219. mov rT1 = -1
  220. ;;
  221. zxt4 rT1 = rT1 // zero extend for compare with
  222. ;; // 4 byte lock value
  223. mov ar.ccv = rT1 // compare value
  224. cmp.ne pNotOwn = rOwnerId, rThreadId
  225. (pNotOwn) br.spnt RecsNotOwn
  226. //
  227. // The critical section is owned by the current thread. Increment the lock
  228. // count and the recursion count.
  229. //
  230. ld4 rRecursionCount = [rpCsRecursion]
  231. fetchadd4.acq rOldLockCount = [rpLockCount], 1
  232. ;;
  233. add rRecursionCount = 1, rRecursionCount
  234. ;;
  235. st4 [rpCsRecursion] = rRecursionCount
  236. br.ret.sptk.clr brp // return
  237. //
  238. // A nonzero spin count is specified and the current thread is not the owner.
  239. //
  240. RecsNotOwn:
  241. cmpxchg4.acq rT1 = [rpLockCount], zero // try to acquire lock
  242. ;;
  243. cmp4.ne pNotAcq = -1, rT1
  244. (pNotAcq) br.spnt RecsNotAcq
  245. //
  246. // The critical section has been acquired. Set the owning thread and the initial
  247. // recursion count and return success.
  248. //
  249. st8 [rpOwner] = rThreadId
  250. st4 [rpCsRecursion] = zero
  251. br.ret.sptk.clr brp // return
  252. //
  253. // The critical section is currently owned. Spin until it is either unowned
  254. // or the spin count has reached zero.
  255. //
  256. // If LockCount > 0, then there are waiters. Don't spin because
  257. // the lock will not free.
  258. //
  259. RecsNotAcq:
  260. YIELD
  261. ld4 rOldLockCount = [rpLockCount]
  262. ;;
  263. cmp4.eq pNotOwn = -1, rOldLockCount
  264. cmp4.gt pNoSpin = rOldLockCount, zero
  265. (pNoSpin) br.spnt RecsNoSpin
  266. (pNotOwn) br.spnt RecsNotOwn
  267. add rSpinCount = -1, rSpinCount
  268. ;;
  269. cmp4.eq pNoSpin, pSpin = zero, rSpinCount
  270. (pNoSpin) br.spnt RecsNoSpin
  271. (pSpin) br.sptk RecsNotAcq
  272. ;;
  273. NESTED_EXIT(RtlEnterCriticalSection)
  274. //++
  275. //
  276. // NTSTATUS
  277. // RtlLeaveCriticalSection(
  278. // IN PRTL_CRITICAL_SECTION CriticalSection
  279. // )
  280. //
  281. // Routine Description:
  282. //
  283. // This function enters a critical section.
  284. //
  285. // N.B. This function is duplicated in the runtime library.
  286. //
  287. // Arguments:
  288. //
  289. // CriticalSection (a0) - Supplies a pointer to a critical section.
  290. //
  291. // Return Value:
  292. //
  293. // STATUS_SUCCESS is returned as the function value.
  294. //
  295. // Algorithm in C:
  296. //
  297. // NTSTATUS
  298. // RtlLeaveCriticalSection(
  299. // IN PRTL_CRITICAL_SECTION CriticalSection
  300. // )
  301. // {
  302. // LONG NewRecursionCount;
  303. // LONG OldLockCount;
  304. // BOOL ToRelease;
  305. //
  306. // ASSERT(CriticalSection->RecursionCount >= 0)
  307. //
  308. // if (CriticalSection->RecursionCount != 0) {
  309. // CriticalSection->RecursionCount -= 1;
  310. // AtomicDecrement(CriticalSection->LockCount);
  311. // return STATUS_SUCCESS;
  312. // }
  313. //
  314. // CriticalSection->OwningThread = 0;
  315. // OldLockCount = AtomicDecrement(CriticalSection->LockCount);
  316. //
  317. // if ( OldLockCount != 0 ) {
  318. // RtlpUnWaitCriticalSection(CriticalSection);
  319. // }
  320. //
  321. // return STATUS_SUCCESS;
  322. // }
  323. //
  324. //--
  325. //
  326. // register aliases
  327. //
  328. NESTED_ENTRY(RtlLeaveCriticalSection)
  329. rpOwner=t0
  330. rOldLockCount=t1
  331. rRecursionCount=t2
  332. rpLockCount=t5
  333. rpCsRecursion=t9
  334. pHold=pt0
  335. pRel=pt1
  336. pGo=pt7
  337. pWait=pt8
  338. NESTED_SETUP(1,2,1,0)
  339. add rpCsRecursion = CsRecursionCount, a0
  340. ;;
  341. PROLOGUE_END
  342. //
  343. // load recursion count
  344. // swizzle pointers to address the LockCount and OwningThread
  345. // fields in the critical section structure.
  346. //
  347. ld4 rRecursionCount = [rpCsRecursion]
  348. add rpOwner = CsOwningThread, a0
  349. add rpLockCount = CsLockCount, a0
  350. ;;
  351. //
  352. // check if the original value of the recursion count to determine
  353. // if the lock is to be released.
  354. //
  355. // decrement the register copy of recursion count by 1 and save
  356. // the new value in temp register
  357. //
  358. cmp.ne pHold, pRel = zero, rRecursionCount
  359. add rRecursionCount = -1, rRecursionCount
  360. add v0 = STATUS_SUCCESS, zero // return STATUS_SUCCESS
  361. ;;
  362. //
  363. // save the updated recursion count into the critical section structure.
  364. //
  365. // atomically decrement the lock count.
  366. //
  367. // if lock is still held, return to caller.
  368. //
  369. // Note: An atomic fetch & add with release form is used here
  370. // all previous memory accesses are visible at this point.
  371. //
  372. // Note: All updates to the Critical Section structure MUST be complete
  373. // prior to the lock count being decremented which releases the
  374. // lock.
  375. //
  376. (pHold) st4 [rpCsRecursion] = rRecursionCount
  377. (pRel) st8 [rpOwner] = zero // clear the owner field
  378. ;;
  379. fetchadd4.rel rOldLockCount = [rpLockCount], -1
  380. (pHold) br.ret.sptk.clr brp // return to caller
  381. ;;
  382. //
  383. // The lock is now free, check the original value of the lock count to
  384. // determine if any other thread is waiting for this critical section.
  385. // If no thread is waiting, return to caller immediately.
  386. //
  387. cmp4.ge pGo, pWait = zero, rOldLockCount
  388. (pGo) br.ret.sptk.clr brp // return to caller
  389. mov out0 = a0
  390. (pWait) br.call.spnt.many brp = RtlpUnWaitCriticalSection
  391. mov v0 = STATUS_SUCCESS // return STATUS_SUCCESS
  392. NESTED_RETURN
  393. ;;
  394. NESTED_EXIT(RtlLeaveCriticalSection)
  395. //++
  396. //
  397. // BOOL
  398. // RtlTryEnterCriticalSection(
  399. // IN PRTL_CRITICAL_SECTION CriticalSection
  400. // )
  401. //
  402. // Routine Description:
  403. //
  404. // This function attempts to enter a critical section without blocking.
  405. //
  406. // Arguments:
  407. //
  408. // CriticalSection (a0) - Supplies a pointer to a critical section.
  409. //
  410. // Return Value:
  411. //
  412. // If the critical section was successfully entered, then a value of TRUE
  413. // is returned as the function value. Otherwise, a value of FALSE is returned
  414. //
  415. //--
  416. LEAF_ENTRY(RtlTryEnterCriticalSection)
  417. //
  418. // register aliases
  419. //
  420. rT0=t0
  421. rT1=t1
  422. rpThreadId=t3
  423. rOldLockCount=t4
  424. rpLockCount=t5
  425. rRecursionCount=t6
  426. rOwnerId=t7
  427. rpCsRecursion=t8
  428. rThreadId=t9
  429. rpOwner=t10
  430. pFree=pt5
  431. pHeld=pt6
  432. pOwn=pt7
  433. pFail=pt8
  434. alloc rT0 = ar.pfs, 1, 0, 0, 0
  435. movl rT1 = 0xffffffff
  436. ;;
  437. mov ar.ccv = rT1
  438. add rpOwner = CsOwningThread, a0
  439. add rpLockCount = CsLockCount, a0
  440. ;;
  441. cmpxchg4.acq rOldLockCount = [rpLockCount], r0, ar.ccv
  442. add rpCsRecursion = CsRecursionCount, a0
  443. add rpThreadId = TeClientId+CidUniqueThread, teb
  444. ;;
  445. ld8 rOwnerId = [rpOwner]
  446. cmp4.eq pFree, pHeld = rT1, rOldLockCount
  447. ;;
  448. ld8.nt1 rThreadId = [rpThreadId]
  449. (pHeld) ld4.nt1 rRecursionCount = [rpCsRecursion]
  450. mov v0 = TRUE
  451. ;;
  452. (pFree) st4 [rpCsRecursion] = zero
  453. (pFree) st8 [rpOwner] = rThreadId
  454. (pHeld) cmp.eq pOwn, pFail = rThreadId, rOwnerId
  455. (pFree) br.ret.sptk.clr brp
  456. ;;
  457. (pOwn) fetchadd4.acq rT0 = [rpLockCount], 1
  458. (pOwn) add rRecursionCount = 1, rRecursionCount
  459. nop.i 0
  460. ;;
  461. (pOwn) st4.rel [rpCsRecursion] = rRecursionCount
  462. (pFail) mov v0 = FALSE
  463. br.ret.sptk.clr brp
  464. LEAF_EXIT(RtlTryEnterCriticalSection)