1
0

rwlock.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. /*++
  2. Copyright (c) 2016 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. rwlock.c
  5. Abstract:
  6. This module implements support for read/write locks.
  7. Author:
  8. Evan Green 28-Apr-2015
  9. Environment:
  10. User Mode
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "osbasep.h"
  16. //
  17. // ---------------------------------------------------------------- Definitions
  18. //
  19. #define OS_RWLOCK_UNLOCKED 0
  20. #define OS_RWLOCK_WRITE_LOCKED ((ULONG)-1)
  21. //
  22. // ------------------------------------------------------ Data Type Definitions
  23. //
  24. //
  25. // ----------------------------------------------- Internal Function Prototypes
  26. //
  27. KSTATUS
  28. OspAcquireReadWriteLockForRead (
  29. POS_RWLOCK Lock,
  30. ULONG TimeoutInMilliseconds
  31. );
  32. KSTATUS
  33. OspAcquireReadWriteLockForWrite (
  34. POS_RWLOCK Lock,
  35. ULONG TimeoutInMilliseconds
  36. );
  37. //
  38. // -------------------------------------------------------------------- Globals
  39. //
  40. //
  41. // ------------------------------------------------------------------ Functions
  42. //
  43. OS_API
  44. VOID
  45. OsRwLockInitialize (
  46. POS_RWLOCK Lock,
  47. ULONG Flags
  48. )
  49. /*++
  50. Routine Description:
  51. This routine initializes a read/write lock.
  52. Arguments:
  53. Lock - Supplies a pointer to the read/write lock.
  54. Flags - Supplies a bitfield of flags governing the lock behavior.
  55. Return Value:
  56. None.
  57. --*/
  58. {
  59. RtlZeroMemory(Lock, sizeof(OS_RWLOCK));
  60. Lock->Attributes = Flags;
  61. return;
  62. }
  63. OS_API
  64. KSTATUS
  65. OsRwLockRead (
  66. POS_RWLOCK Lock
  67. )
  68. /*++
  69. Routine Description:
  70. This routine acquires the read/write lock for read access. Multiple readers
  71. can acquire the lock simultaneously, but any writes that try to acquire
  72. the lock while it's held for read will block. Readers that try to
  73. acquire the lock while it's held for write will also block.
  74. Arguments:
  75. Lock - Supplies a pointer to the read/write lock.
  76. Return Value:
  77. Status code.
  78. --*/
  79. {
  80. return OspAcquireReadWriteLockForRead(Lock, SYS_WAIT_TIME_INDEFINITE);
  81. }
  82. OS_API
  83. KSTATUS
  84. OsRwLockReadTimed (
  85. POS_RWLOCK Lock,
  86. ULONG TimeoutInMilliseconds
  87. )
  88. /*++
  89. Routine Description:
  90. This routine acquires the read/write lock for read access just like the
  91. read lock function, except that this function will return after the
  92. specified deadline if the lock could not be acquired.
  93. Arguments:
  94. Lock - Supplies a pointer to the read/write lock.
  95. TimeoutInMilliseconds - Supplies the duration to wait in milliseconds.
  96. Return Value:
  97. Status code.
  98. --*/
  99. {
  100. return OspAcquireReadWriteLockForRead(Lock, TimeoutInMilliseconds);
  101. }
  102. OS_API
  103. KSTATUS
  104. OsRwLockTryRead (
  105. POS_RWLOCK Lock
  106. )
  107. /*++
  108. Routine Description:
  109. This routine performs a single attempt at acquiring the lock for read
  110. access.
  111. Arguments:
  112. Lock - Supplies a pointer to the read/write lock.
  113. Return Value:
  114. STATUS_SUCCESS on success.
  115. STATUS_RESOURCE_IN_USE if the lock is already held.
  116. --*/
  117. {
  118. ULONG NewState;
  119. ULONG OldState;
  120. OldState = Lock->State;
  121. if (OldState != OS_RWLOCK_WRITE_LOCKED) {
  122. NewState = RtlAtomicCompareExchange32(&(Lock->State),
  123. OldState + 1,
  124. OldState);
  125. if (NewState == OldState) {
  126. return STATUS_SUCCESS;
  127. }
  128. }
  129. return STATUS_RESOURCE_IN_USE;
  130. }
  131. OS_API
  132. KSTATUS
  133. OsRwLockWrite (
  134. POS_RWLOCK Lock
  135. )
  136. /*++
  137. Routine Description:
  138. This routine acquires the read/write lock for write access. The lock can
  139. only be acquired for write access if there are no readers and no other
  140. writers.
  141. Arguments:
  142. Lock - Supplies a pointer to the read/write lock.
  143. Return Value:
  144. Status code.
  145. --*/
  146. {
  147. return OspAcquireReadWriteLockForWrite(Lock, SYS_WAIT_TIME_INDEFINITE);
  148. }
  149. OS_API
  150. KSTATUS
  151. OsRwLockWriteTimed (
  152. POS_RWLOCK Lock,
  153. ULONG TimeoutInMilliseconds
  154. )
  155. /*++
  156. Routine Description:
  157. This routine acquires the read/write lock for write access just like the
  158. write lock function, except that this function will return after the
  159. specified deadline if the lock could not be acquired.
  160. Arguments:
  161. Lock - Supplies a pointer to the read/write lock.
  162. TimeoutInMilliseconds - Supplies the timeout to wait in milliseconds.
  163. Return Value:
  164. Status code.
  165. --*/
  166. {
  167. return OspAcquireReadWriteLockForWrite(Lock, TimeoutInMilliseconds);
  168. }
  169. OS_API
  170. KSTATUS
  171. OsRwLockTryWrite (
  172. POS_RWLOCK Lock
  173. )
  174. /*++
  175. Routine Description:
  176. This routine performs a single attempt at acquiring the lock for write
  177. access.
  178. Arguments:
  179. Lock - Supplies a pointer to the read/write lock.
  180. Return Value:
  181. STATUS_SUCCESS on success.
  182. STATUS_RESOURCE_IN_USE if the lock is already held.
  183. --*/
  184. {
  185. ULONG OldState;
  186. OldState = Lock->State;
  187. if (OldState == OS_RWLOCK_UNLOCKED) {
  188. OldState = RtlAtomicCompareExchange32(&(Lock->State),
  189. OS_RWLOCK_WRITE_LOCKED,
  190. OldState);
  191. if (OldState == OS_RWLOCK_UNLOCKED) {
  192. Lock->WriterThreadId = OsGetThreadId();
  193. return 0;
  194. }
  195. }
  196. return STATUS_RESOURCE_IN_USE;
  197. }
  198. OS_API
  199. KSTATUS
  200. OsRwLockUnlock (
  201. POS_RWLOCK Lock
  202. )
  203. /*++
  204. Routine Description:
  205. This routine unlocks a read/write lock that's been acquired by this thread
  206. for either read or write.
  207. Arguments:
  208. Lock - Supplies a pointer to the read/write lock.
  209. Return Value:
  210. STATUS_SUCCESS on success.
  211. STATUS_PERMISSION_DENIED if the lock is not held or was not held by this
  212. thread.
  213. --*/
  214. {
  215. ULONG Count;
  216. POS_RWLOCK LockInternal;
  217. ULONG NewState;
  218. ULONG OldState;
  219. ULONG Operation;
  220. LockInternal = (POS_RWLOCK)Lock;
  221. OldState = LockInternal->State;
  222. if (OldState == OS_RWLOCK_UNLOCKED) {
  223. return STATUS_PERMISSION_DENIED;
  224. }
  225. //
  226. // If this lock is held by a writer, make sure that this thread is that
  227. // writer, then set it to unlocked.
  228. //
  229. if (OldState == OS_RWLOCK_WRITE_LOCKED) {
  230. if (LockInternal->WriterThreadId != OsGetThreadId()) {
  231. return STATUS_PERMISSION_DENIED;
  232. }
  233. LockInternal->WriterThreadId = 0;
  234. LockInternal->State = OS_RWLOCK_UNLOCKED;
  235. //
  236. // The lock is held by a reader.
  237. //
  238. } else {
  239. while (OldState > OS_RWLOCK_UNLOCKED) {
  240. NewState = RtlAtomicCompareExchange32(&(LockInternal->State),
  241. OldState - 1,
  242. OldState);
  243. if (NewState == OldState) {
  244. break;
  245. }
  246. OldState = NewState;
  247. }
  248. if (OldState == 0) {
  249. return STATUS_PERMISSION_DENIED;
  250. //
  251. // If there are still other readers, don't release the writers.
  252. //
  253. } else if (OldState > 1) {
  254. return STATUS_SUCCESS;
  255. }
  256. }
  257. //
  258. // Wake anyone blocking (chaos ensues).
  259. //
  260. if ((LockInternal->PendingReaders != 0) ||
  261. (LockInternal->PendingWriters != 0)) {
  262. Count = MAX_ULONG;
  263. Operation = UserLockWake;
  264. if ((LockInternal->Attributes & OS_RWLOCK_SHARED) == 0) {
  265. Operation |= USER_LOCK_PRIVATE;
  266. }
  267. OsUserLock(&(LockInternal->State), Operation, &Count, 0);
  268. }
  269. return STATUS_SUCCESS;
  270. }
  271. //
  272. // --------------------------------------------------------- Internal Functions
  273. //
  274. KSTATUS
  275. OspAcquireReadWriteLockForRead (
  276. POS_RWLOCK Lock,
  277. ULONG TimeoutInMilliseconds
  278. )
  279. /*++
  280. Routine Description:
  281. This routine acquires the given read/write lock for read access.
  282. Arguments:
  283. Lock - Supplies a pointer to the lock to acquire.
  284. TimeoutInMilliseconds - Supplies the timeout to wait in milliseconds.
  285. Return Value:
  286. Status code.
  287. --*/
  288. {
  289. KSTATUS KernelStatus;
  290. ULONG NewState;
  291. ULONG OldState;
  292. ULONG Operation;
  293. UINTN ThreadId;
  294. ThreadId = OsGetThreadId();
  295. if (ThreadId == Lock->WriterThreadId) {
  296. return STATUS_DEADLOCK;
  297. }
  298. while (TRUE) {
  299. OldState = Lock->State;
  300. if (OldState != OS_RWLOCK_WRITE_LOCKED) {
  301. NewState = RtlAtomicCompareExchange32(&(Lock->State),
  302. OldState + 1,
  303. OldState);
  304. //
  305. // If the old value wasn't write locked, then the reader was
  306. // successfully added.
  307. //
  308. if (NewState == OldState) {
  309. break;
  310. }
  311. //
  312. // The lock is already acquired for write access.
  313. //
  314. } else {
  315. Operation = UserLockWait;
  316. if ((Lock->Attributes & OS_RWLOCK_SHARED) == 0) {
  317. Operation |= USER_LOCK_PRIVATE;
  318. }
  319. RtlAtomicAdd32(&(Lock->PendingReaders), 1);
  320. KernelStatus = OsUserLock(&(Lock->State),
  321. Operation,
  322. &OldState,
  323. TimeoutInMilliseconds);
  324. RtlAtomicAdd32(&(Lock->PendingReaders), -1);
  325. if (KernelStatus == STATUS_TIMEOUT) {
  326. return KernelStatus;
  327. }
  328. }
  329. }
  330. return STATUS_SUCCESS;
  331. }
  332. KSTATUS
  333. OspAcquireReadWriteLockForWrite (
  334. POS_RWLOCK Lock,
  335. ULONG TimeoutInMilliseconds
  336. )
  337. /*++
  338. Routine Description:
  339. This routine acquires the given read/write lock for write access.
  340. Arguments:
  341. Lock - Supplies a pointer to the lock to acquire.
  342. TimeoutInMilliseconds - Supplies the timeout to wait in milliseconds.
  343. Return Value:
  344. 0 on success.
  345. Returns an error number on failure.
  346. --*/
  347. {
  348. KSTATUS KernelStatus;
  349. ULONG OldState;
  350. ULONG Operation;
  351. UINTN ThreadId;
  352. ThreadId = OsGetThreadId();
  353. if (ThreadId == Lock->WriterThreadId) {
  354. return STATUS_DEADLOCK;
  355. }
  356. while (TRUE) {
  357. OldState = Lock->State;
  358. if (OldState == OS_RWLOCK_UNLOCKED) {
  359. OldState = RtlAtomicCompareExchange32(&(Lock->State),
  360. OS_RWLOCK_WRITE_LOCKED,
  361. OldState);
  362. //
  363. // If the old value was unlocked, then this thread successfully
  364. // got the write lock.
  365. //
  366. if (OldState == OS_RWLOCK_UNLOCKED) {
  367. Lock->WriterThreadId = ThreadId;
  368. break;
  369. }
  370. //
  371. // The lock is already acquired for read or write access.
  372. //
  373. } else {
  374. Operation = UserLockWait;
  375. if ((Lock->Attributes & OS_RWLOCK_SHARED) == 0) {
  376. Operation |= USER_LOCK_PRIVATE;
  377. }
  378. RtlAtomicAdd32(&(Lock->PendingWriters), 1);
  379. KernelStatus = OsUserLock(&(Lock->State),
  380. Operation,
  381. &OldState,
  382. TimeoutInMilliseconds);
  383. RtlAtomicAdd32(&(Lock->PendingWriters), -1);
  384. if (KernelStatus == STATUS_TIMEOUT) {
  385. return KernelStatus;
  386. }
  387. }
  388. }
  389. return STATUS_SUCCESS;
  390. }