rwlock.c 11 KB

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