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.

424 lines
15 KiB

  1. // TITLE("Enter and Leave Critical Section")
  2. //++
  3. //
  4. // Copyright (c) 1991 Microsoft Corporation
  5. // Copyright (c) 1992 Digital Equipment Corporation
  6. //
  7. // Module Name:
  8. //
  9. // critsect.s
  10. //
  11. // Abstract:
  12. //
  13. // This module implements functions to support user mode critical sections.
  14. //
  15. // Author:
  16. //
  17. // David N. Cutler 1-May-1992
  18. //
  19. // Environment:
  20. //
  21. // Any mode.
  22. //
  23. // Revision History:
  24. //
  25. // Thomas Van Baak (tvb) 21-May-1992
  26. //
  27. // Adapted for Alpha AXP.
  28. //
  29. //--
  30. #include "ksalpha.h"
  31. SBTTL("Enter Critical Section")
  32. //++
  33. //
  34. // NTSTATUS
  35. // RtlEnterCriticalSection (
  36. // IN PRTL_CRITICAL_SECTION CriticalSection
  37. // )
  38. //
  39. // Routine Description:
  40. //
  41. // This function enters a critical section.
  42. //
  43. // N.B. This function is duplicated in the runtime library.
  44. //
  45. // Arguments:
  46. //
  47. // CriticalSection (a0) - Supplies a pointer to a critical section.
  48. //
  49. // Return Value:
  50. //
  51. // STATUS_SUCCESS is returned as the function value.
  52. //
  53. //--
  54. .struct 0
  55. EcRa: .space 8 // saved return address
  56. EcA0: .space 8 // saved critical section address
  57. EcA1: .space 8 // saved unique thread id
  58. .space 1 * 8 // required for 16-byte stack alignment
  59. EcFrameLength: // length of stack frame
  60. NESTED_ENTRY(RtlEnterCriticalSection, EcFrameLength, zero)
  61. lda sp, -EcFrameLength(sp) // allocate stack frame
  62. stq ra, EcRa(sp) // save return address
  63. PROLOGUE_END
  64. GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) get TEB address in v0
  65. ldl t3, CsSpinCount(a0) // check if spin count is zero
  66. LDP a1, TeClientId + CidUniqueThread(v0) // get current thread id
  67. bne t3, 50f // if ne, spin count specified
  68. //
  69. // Attempt to enter the critical section.
  70. //
  71. 10: ldl_l t0, CsLockCount(a0) // get addend value - locked
  72. addl t0, 1, t0 // increment addend value
  73. mov t0, t1 //
  74. stl_c t1, CsLockCount(a0) // store conditionally
  75. beq t1, 40f // if eq, conditional store failed
  76. mb // synchronize memory access
  77. //
  78. // If the critical section is not already owned, then initialize the owner
  79. // thread id, initialize the recursion count, and return a success status.
  80. // The updated lock value is now in t0.
  81. //
  82. bne t0, 20f // if ne, lock already owned
  83. STP a1, CsOwningThread(a0) // set critical section owner
  84. ldil v0, STATUS_SUCCESS // set return status
  85. lda sp, EcFrameLength(sp) // deallocate stack frame
  86. ret zero, (ra) // return
  87. //
  88. // The critical section is owned. If the current thread is the owner, then
  89. // increment the recursion count, and return a success status. Otherwise,
  90. // wait for critical section ownership.
  91. // The current thread unique id is in a1.
  92. //
  93. 20: LDP t0, CsOwningThread(a0) // get unique id of owner thread
  94. cmpeq a1, t0, t1 // check if current thread is owner
  95. beq t1, 30f // if eq, current thread not owner
  96. ldl t0, CsRecursionCount(a0) // increment the recursion count
  97. addl t0, 1, t2 //
  98. stl t2, CsRecursionCount(a0) //
  99. ldil v0, STATUS_SUCCESS // set return status
  100. lda sp, EcFrameLength(sp) // deallocate stack frame
  101. ret zero, (ra) // return
  102. //
  103. // The critical section is owned by a thread other than the current thread.
  104. // Wait for ownership of the critical section.
  105. // N.B. a1 is just a temp register below, not an argument to the function.
  106. //
  107. 30: stq a0, EcA0(sp) // save address of critical section
  108. stq a1, EcA1(sp) // save unique thread id
  109. bsr ra, RtlpWaitForCriticalSection // wait for critical section
  110. ldq a0, EcA0(sp) // restore address of critical section
  111. ldq a1, EcA1(sp) // restore unique thread id
  112. STP a1, CsOwningThread(a0) // set critical section owner
  113. ldil v0, STATUS_SUCCESS // set return status
  114. ldq ra, EcRa(sp) // restore return address
  115. lda sp, EcFrameLength(sp) // deallocate stack frame
  116. ret zero, (ra) // return
  117. //
  118. // We expect the store conditional will usually succeed the first time so it
  119. // is faster to branch forward (predicted not taken) to here and then branch
  120. // backward (predicted taken) to where we wanted to go.
  121. //
  122. 40: br zero, 10b // go try lock again
  123. //
  124. // A nonzero spin count is specified
  125. //
  126. 50: LDP t4, CsOwningThread(a0) // get owner thread id
  127. cmpeq t4, a1, t5 // check if current thread is owner
  128. beq t5, 60f // if eq, current thread not owner
  129. //
  130. // The critical section is owned by the current thread. Increment the lock
  131. // count and the recursion count.
  132. //
  133. 55: ldl_l t0, CsLockCount(a0) // get addend value - locked
  134. addl t0, 1, t1 // increment addend value
  135. stl_c t1, CsLockCount(a0) // store conditionally
  136. beq t1, 59f // if lock-flag eq zero, store failed
  137. mb // synchronize memory access
  138. ldl t3, CsRecursionCount(a0) // increment recursion count
  139. addl t3, 1, t4 //
  140. stl t4, CsRecursionCount(a0) //
  141. ldil v0, STATUS_SUCCESS // set return status
  142. lda sp, EcFrameLength(sp) // deallocate stack frame
  143. ret zero, (ra) // return
  144. //
  145. // Store conditional attempt failed.
  146. //
  147. 59: br zero, 55b // go try lock again
  148. //
  149. // A nonzero spin count is specified and the current thread is not the owner.
  150. //
  151. 60: ldl_l t0, CsLockCount(a0) // get addend value - locked
  152. addl t0, 1, t1 // increment addend value
  153. bne t1, 70f // if ne, critical section is owned
  154. stl_c t1, CsLockCount(a0) // set new lock count
  155. beq t1, 69f // if eq, conditional store failed
  156. mb // synchronize memory access
  157. //
  158. // The critical section has been acquired. Set the owning thread and the initial
  159. // recursion count.
  160. //
  161. STP a1, CsOwningThread(a0) // set critical section owner
  162. ldil v0, STATUS_SUCCESS // set return status
  163. lda sp, EcFrameLength(sp) // deallocate stack frame
  164. ret zero, (ra) // return
  165. //
  166. // Store conditional attempt failed.
  167. //
  168. 69: br zero, 60b //
  169. //
  170. // The critical section is currently owned. Spin until it is either unowned
  171. // or the spin count has reached zero. Spin count is in t3
  172. //
  173. // If waiters are present, don't spin on the lock since we will never see it
  174. // go free.
  175. //
  176. 70: ldl t0, CsLockCount(a0) // check if lock is owned
  177. addl t0, 1, t1 //
  178. bgt t0, 10b // if >=, then do not spin
  179. beq t1, 60b // if eq, lock is not owned
  180. subl t3, 1, t3 // decrement spin count
  181. bne t3, 70b // if nz, continue spinning
  182. br 10b // spin expired, go wait for lock
  183. .end RtlEnterCriticalSection
  184. SBTTL("Leave Critical Section")
  185. //++
  186. //
  187. // NTSTATUS
  188. // RtlLeaveCriticalSection (
  189. // IN PRTL_CRITICAL_SECTION CriticalSection
  190. // )
  191. //
  192. // Routine Description:
  193. //
  194. // This function leaves a critical section.
  195. //
  196. // N.B. This function is duplicated in the runtime library.
  197. //
  198. // Arguments:
  199. //
  200. // CriticalSection (a0) - Supplies a pointer to a critical section.
  201. //
  202. // Return Value:
  203. //
  204. // STATUS_SUCCESS is returned as the function value.
  205. //
  206. //--
  207. .struct 0
  208. LcRa: .space 8 // saved return address
  209. .space 1 * 8 // required for 16-byte stack alignment
  210. LcFrameLength: // length of stack frame
  211. NESTED_ENTRY(RtlLeaveCriticalSection, LcFrameLength, zero)
  212. lda sp, -LcFrameLength(sp) // allocate stack frame
  213. stq ra, LcRa(sp) // save return address
  214. PROLOGUE_END
  215. //
  216. // If the current thread is not the owner of the critical section, then
  217. // raise an exception.
  218. //
  219. #if DBG
  220. GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) get TEB address in v0
  221. LDP a1, TeClientId + CidUniqueThread(v0) // get current thread id
  222. LDP t0, CsOwningThread(a0) // get owner thread id
  223. cmpeq a1, t0, t1 // check if current thread owner
  224. bne t1, 10f // if ne, current thread is owner
  225. bsr ra, RtlpNotOwnerCriticalSection // raise exception
  226. ldil v0, STATUS_INVALID_OWNER // set completion status
  227. ldq ra, LcRa(sp) // restore return address
  228. lda sp, LcFrameLength(sp) // deallocate stack frame
  229. ret zero, (ra) // return
  230. #endif
  231. //
  232. // Decrement the recursion count. If the result is zero, then the lock
  233. // is no longer owned.
  234. //
  235. 10: ldl t0, CsRecursionCount(a0) // decrement recursion count
  236. subl t0, 1, t0 //
  237. bge t0, 30f // if ge, lock still owned
  238. STP zero, CsOwningThread(a0) // clear owner thread id
  239. //
  240. // Decrement the lock count and check if a waiter should be continued.
  241. //
  242. //
  243. 20: mb // synchronize memory access
  244. ldl_l t0, CsLockCount(a0) // get addend value - locked
  245. subl t0, 1, t0 // decrement addend value
  246. mov t0, t1 // copy updated value to t1 for store
  247. stl_c t1, CsLockCount(a0) // store conditionally
  248. beq t1, 60f // if eq, conditional store failed
  249. blt t0, 50f // if lt, no waiter present
  250. bsr ra, RtlpUnWaitCriticalSection // unwait thread
  251. ldil v0, STATUS_SUCCESS // set completion status
  252. ldq ra, LcRa(sp) // restore return address
  253. lda sp, LcFrameLength(sp) // deallocate stack frame
  254. ret zero, (ra) // return
  255. //
  256. // Decrement the lock count and return a success status since the lock
  257. // is still owned.
  258. //
  259. 30: stl t0, CsRecursionCount(a0) // store updated recursion count
  260. 40: ldl_l t0, CsLockCount(a0) // get addend value - locked
  261. subl t0, 1, t0 // decrement addend value
  262. stl_c t0, CsLockCount(a0) // store conditionally
  263. beq t0, 70f // if lock-flag eq zero, store failed
  264. 50: ldil v0, STATUS_SUCCESS // set completion status
  265. lda sp, LcFrameLength(sp) // deallocate stack frame
  266. ret zero, (ra) // return
  267. //
  268. // We expect the store conditional will usually succeed the first time so it
  269. // is faster to branch forward (predicted not taken) to here and then branch
  270. // backward (predicted taken) to where we wanted to go.
  271. //
  272. 60: br zero, 20b // go try lock again
  273. 70: br zero, 40b // go try lock again
  274. .end RtlLeaveCriticalSection
  275. SBTTL("Try to Enter Critical Section")
  276. //++
  277. //
  278. // BOOLEAN
  279. // RtlTryEnterCriticalSection(
  280. // IN PRTL_CRITICAL_SECTION CriticalSection
  281. // )
  282. //
  283. // Routine Description:
  284. //
  285. // This function attempts to enter a critical section without blocking.
  286. //
  287. // Arguments:
  288. //
  289. // CriticalSection (a0) - Supplies a pointer to a critical section.
  290. //
  291. // Return Value:
  292. //
  293. // If the critical section was successfully entered, then a value of TRUE
  294. // is returned as the function value. Otherwise, a value of FALSE is returned.
  295. //
  296. //--
  297. .struct 0
  298. EcRa: .space 8 // saved return address
  299. EcA0: .space 8 // saved critical section address
  300. EcA1: .space 8 // saved unique thread id
  301. .space 1 * 8 // required for 16-byte stack alignment
  302. EcFrameLength: // length of stack frame
  303. LEAF_ENTRY(RtlTryEnterCriticalSection)
  304. GET_THREAD_ENVIRONMENT_BLOCK // (PALcode) get TEB address in v0
  305. LDP a1, TeClientId + CidUniqueThread(v0) // get current thread unique id
  306. //
  307. // Attempt to enter the critical section.
  308. //
  309. 10: ldl_l t0, CsLockCount(a0) // get addend value - locked
  310. addl t0, 1, t1 // increment addend value
  311. bne t1, 20f // critical section owned
  312. stl_c t1, CsLockCount(a0) // store conditionally
  313. beq t1, 40f // if eq, conditional store failed
  314. mb // synchronize memory access
  315. //
  316. // The critical section is now owned by this thread. Initialize the owner
  317. // thread id and return a successful status.
  318. //
  319. STP a1, CsOwningThread(a0) // set critical section owner
  320. ldil v0, TRUE // set success status
  321. ret zero, (ra)
  322. //
  323. // The critical section is already owned. If it is owned by another thread,
  324. // return FALSE immediately. If it is owned by this thread, we must increment
  325. // the lock count here.
  326. //
  327. 20: LDP t2, CsOwningThread(a0) // get current owner
  328. cmpeq t2, a1, t3 // check if current thread owner
  329. bne t3, 30f // if ne, current thread owner
  330. bis zero,zero,v0 // set failure status
  331. ret zero, (ra) // return
  332. //
  333. // This thread is already the owner of the critical section. Perform an atomic
  334. // increment of the LockCount and a normal increment of the RecursionCount and
  335. // return success.
  336. //
  337. 30: ldl_l t0, CsLockCount(a0) // get addend value - locked
  338. addl t0, 1, t1 // increment addend value
  339. stl_c t1, CsLockCount(a0) // store conditionally
  340. beq t1, 50f // if eq, conditional store failed
  341. ldl t0, CsRecursionCount(a0) // increment recursion count
  342. addl t0, 1, t1 //
  343. stl t1, CsRecursionCount(a0) //
  344. ldil v0, TRUE // set success status
  345. ret zero, (ra) // return
  346. //
  347. // We expect the store conditional will usually succeed the first time so it
  348. // is faster to branch forward (predicted not taken) to here and then branch
  349. // backward (predicted taken) to where we wanted to go.
  350. //
  351. 40: br zero, 10b // go try lock again
  352. 50: br zero, 30b // retry lock
  353. .end RtlTryEnterCriticalSection