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.

553 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. ld4 rOldLockCount = [rpLockCount]
  261. ;;
  262. cmp4.eq pNotOwn = -1, rOldLockCount
  263. cmp4.gt pNoSpin = rOldLockCount, zero
  264. (pNoSpin) br.spnt RecsNoSpin
  265. (pNotOwn) br.spnt RecsNotOwn
  266. add rSpinCount = -1, rSpinCount
  267. ;;
  268. cmp4.eq pNoSpin, pSpin = zero, rSpinCount
  269. (pNoSpin) br.spnt RecsNoSpin
  270. (pSpin) br.sptk RecsNotAcq
  271. ;;
  272. NESTED_EXIT(RtlEnterCriticalSection)
  273. //++
  274. //
  275. // NTSTATUS
  276. // RtlLeaveCriticalSection(
  277. // IN PRTL_CRITICAL_SECTION CriticalSection
  278. // )
  279. //
  280. // Routine Description:
  281. //
  282. // This function enters a critical section.
  283. //
  284. // N.B. This function is duplicated in the runtime library.
  285. //
  286. // Arguments:
  287. //
  288. // CriticalSection (a0) - Supplies a pointer to a critical section.
  289. //
  290. // Return Value:
  291. //
  292. // STATUS_SUCCESS is returned as the function value.
  293. //
  294. // Algorithm in C:
  295. //
  296. // NTSTATUS
  297. // RtlLeaveCriticalSection(
  298. // IN PRTL_CRITICAL_SECTION CriticalSection
  299. // )
  300. // {
  301. // LONG NewRecursionCount;
  302. // LONG OldLockCount;
  303. // BOOL ToRelease;
  304. //
  305. // ASSERT(CriticalSection->RecursionCount >= 0)
  306. //
  307. // if (CriticalSection->RecursionCount != 0) {
  308. // CriticalSection->RecursionCount -= 1;
  309. // AtomicDecrement(CriticalSection->LockCount);
  310. // return STATUS_SUCCESS;
  311. // }
  312. //
  313. // CriticalSection->OwningThread = 0;
  314. // OldLockCount = AtomicDecrement(CriticalSection->LockCount);
  315. //
  316. // if ( OldLockCount != 0 ) {
  317. // RtlpUnWaitCriticalSection(CriticalSection);
  318. // }
  319. //
  320. // return STATUS_SUCCESS;
  321. // }
  322. //
  323. //--
  324. //
  325. // register aliases
  326. //
  327. NESTED_ENTRY(RtlLeaveCriticalSection)
  328. rpOwner=t0
  329. rOldLockCount=t1
  330. rRecursionCount=t2
  331. rpLockCount=t5
  332. rpCsRecursion=t9
  333. pHold=pt0
  334. pRel=pt1
  335. pGo=pt7
  336. pWait=pt8
  337. NESTED_SETUP(1,2,1,0)
  338. add rpCsRecursion = CsRecursionCount, a0
  339. ;;
  340. PROLOGUE_END
  341. //
  342. // load recursion count
  343. // swizzle pointers to address the LockCount and OwningThread
  344. // fields in the critical section structure.
  345. //
  346. ld4 rRecursionCount = [rpCsRecursion]
  347. add rpOwner = CsOwningThread, a0
  348. add rpLockCount = CsLockCount, a0
  349. ;;
  350. //
  351. // check if the original value of the recursion count to determine
  352. // if the lock is to be released.
  353. //
  354. // decrement the register copy of recursion count by 1 and save
  355. // the new value in temp register
  356. //
  357. cmp.ne pHold, pRel = zero, rRecursionCount
  358. add rRecursionCount = -1, rRecursionCount
  359. add v0 = STATUS_SUCCESS, zero // return STATUS_SUCCESS
  360. ;;
  361. //
  362. // save the updated recursion count into the critical section structure.
  363. //
  364. // atomically decrement the lock count.
  365. //
  366. // if lock is still held, return to caller.
  367. //
  368. // Note: An atomic fetch & add with release form is used here
  369. // all previous memory accesses are visible at this point.
  370. //
  371. // Note: All updates to the Critical Section structure MUST be complete
  372. // prior to the lock count being decremented which releases the
  373. // lock.
  374. //
  375. (pHold) st4 [rpCsRecursion] = rRecursionCount
  376. (pRel) st8 [rpOwner] = zero // clear the owner field
  377. ;;
  378. fetchadd4.rel rOldLockCount = [rpLockCount], -1
  379. (pHold) br.ret.sptk.clr brp // return to caller
  380. ;;
  381. //
  382. // The lock is now free, check the original value of the lock count to
  383. // determine if any other thread is waiting for this critical section.
  384. // If no thread is waiting, return to caller immediately.
  385. //
  386. cmp4.ge pGo, pWait = zero, rOldLockCount
  387. (pGo) br.ret.sptk.clr brp // return to caller
  388. mov out0 = a0
  389. (pWait) br.call.spnt.many brp = RtlpUnWaitCriticalSection
  390. mov v0 = STATUS_SUCCESS // return STATUS_SUCCESS
  391. NESTED_RETURN
  392. ;;
  393. NESTED_EXIT(RtlLeaveCriticalSection)
  394. //++
  395. //
  396. // BOOL
  397. // RtlTryEnterCriticalSection(
  398. // IN PRTL_CRITICAL_SECTION CriticalSection
  399. // )
  400. //
  401. // Routine Description:
  402. //
  403. // This function attempts to enter a critical section without blocking.
  404. //
  405. // Arguments:
  406. //
  407. // CriticalSection (a0) - Supplies a pointer to a critical section.
  408. //
  409. // Return Value:
  410. //
  411. // If the critical section was successfully entered, then a value of TRUE
  412. // is returned as the function value. Otherwise, a value of FALSE is returned
  413. //
  414. //--
  415. LEAF_ENTRY(RtlTryEnterCriticalSection)
  416. //
  417. // register aliases
  418. //
  419. rT0=t0
  420. rT1=t1
  421. rpThreadId=t3
  422. rOldLockCount=t4
  423. rpLockCount=t5
  424. rRecursionCount=t6
  425. rOwnerId=t7
  426. rpCsRecursion=t8
  427. rThreadId=t9
  428. rpOwner=t10
  429. pFree=pt5
  430. pHeld=pt6
  431. pOwn=pt7
  432. pFail=pt8
  433. alloc rT0 = ar.pfs, 1, 0, 0, 0
  434. movl rT1 = 0xffffffff
  435. ;;
  436. mov ar.ccv = rT1
  437. add rpOwner = CsOwningThread, a0
  438. add rpLockCount = CsLockCount, a0
  439. ;;
  440. cmpxchg4.acq rOldLockCount = [rpLockCount], r0, ar.ccv
  441. add rpCsRecursion = CsRecursionCount, a0
  442. add rpThreadId = TeClientId+CidUniqueThread, teb
  443. ;;
  444. ld8 rOwnerId = [rpOwner]
  445. cmp4.eq pFree, pHeld = rT1, rOldLockCount
  446. ;;
  447. ld8.nt1 rThreadId = [rpThreadId]
  448. (pHeld) ld4.nt1 rRecursionCount = [rpCsRecursion]
  449. mov v0 = TRUE
  450. ;;
  451. (pFree) st4 [rpCsRecursion] = zero
  452. (pFree) st8 [rpOwner] = rThreadId
  453. (pHeld) cmp.eq pOwn, pFail = rThreadId, rOwnerId
  454. (pFree) br.ret.sptk.clr brp
  455. ;;
  456. (pOwn) fetchadd4.acq rT0 = [rpLockCount], 1
  457. (pOwn) add rRecursionCount = 1, rRecursionCount
  458. nop.i 0
  459. ;;
  460. (pOwn) st4.rel [rpCsRecursion] = rRecursionCount
  461. (pFail) mov v0 = FALSE
  462. br.ret.sptk.clr brp
  463. LEAF_EXIT(RtlTryEnterCriticalSection)