nssrwlk.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #include "nssrwlk.h"
  5. #include "nspr.h"
  6. PR_BEGIN_EXTERN_C
  7. /*
  8. * Reader-writer lock
  9. */
  10. struct nssRWLockStr {
  11. PZLock *rw_lock;
  12. char *rw_name; /* lock name */
  13. PRUint32 rw_rank; /* rank of the lock */
  14. PRInt32 rw_writer_locks; /* == 0, if unlocked */
  15. PRInt32 rw_reader_locks; /* == 0, if unlocked */
  16. /* > 0 , # of read locks */
  17. PRUint32 rw_waiting_readers; /* number of waiting readers */
  18. PRUint32 rw_waiting_writers; /* number of waiting writers */
  19. PZCondVar *rw_reader_waitq; /* cvar for readers */
  20. PZCondVar *rw_writer_waitq; /* cvar for writers */
  21. PRThread *rw_owner; /* lock owner for write-lock */
  22. /* Non-null if write lock held. */
  23. };
  24. PR_END_EXTERN_C
  25. #include <string.h>
  26. #ifdef DEBUG_RANK_ORDER
  27. #define NSS_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using \
  28. rank-order for locks \
  29. */
  30. #endif
  31. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  32. static PRUintn nss_thread_rwlock_initialized;
  33. static PRUintn nss_thread_rwlock; /* TPD key for lock stack */
  34. static PRUintn nss_thread_rwlock_alloc_failed;
  35. #define _NSS_RWLOCK_RANK_ORDER_LIMIT 10
  36. typedef struct thread_rwlock_stack {
  37. PRInt32 trs_index; /* top of stack */
  38. NSSRWLock *trs_stack[_NSS_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock
  39. pointers */
  40. } thread_rwlock_stack;
  41. /* forward static declarations. */
  42. static PRUint32 nssRWLock_GetThreadRank(PRThread *me);
  43. static void nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock);
  44. static void nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock);
  45. static void nssRWLock_ReleaseLockStack(void *lock_stack);
  46. #endif
  47. #define UNTIL(x) while (!(x))
  48. /*
  49. * Reader/Writer Locks
  50. */
  51. /*
  52. * NSSRWLock_New
  53. * Create a reader-writer lock, with the given lock rank and lock name
  54. *
  55. */
  56. NSSRWLock *
  57. NSSRWLock_New(PRUint32 lock_rank, const char *lock_name)
  58. {
  59. NSSRWLock *rwlock;
  60. rwlock = PR_NEWZAP(NSSRWLock);
  61. if (rwlock == NULL)
  62. return NULL;
  63. rwlock->rw_lock = PZ_NewLock(nssILockRWLock);
  64. if (rwlock->rw_lock == NULL) {
  65. goto loser;
  66. }
  67. rwlock->rw_reader_waitq = PZ_NewCondVar(rwlock->rw_lock);
  68. if (rwlock->rw_reader_waitq == NULL) {
  69. goto loser;
  70. }
  71. rwlock->rw_writer_waitq = PZ_NewCondVar(rwlock->rw_lock);
  72. if (rwlock->rw_writer_waitq == NULL) {
  73. goto loser;
  74. }
  75. if (lock_name != NULL) {
  76. rwlock->rw_name = (char *)PR_Malloc((PRUint32)strlen(lock_name) + 1);
  77. if (rwlock->rw_name == NULL) {
  78. goto loser;
  79. }
  80. strcpy(rwlock->rw_name, lock_name);
  81. } else {
  82. rwlock->rw_name = NULL;
  83. }
  84. rwlock->rw_rank = lock_rank;
  85. rwlock->rw_waiting_readers = 0;
  86. rwlock->rw_waiting_writers = 0;
  87. rwlock->rw_reader_locks = 0;
  88. rwlock->rw_writer_locks = 0;
  89. return rwlock;
  90. loser:
  91. NSSRWLock_Destroy(rwlock);
  92. return (NULL);
  93. }
  94. /*
  95. ** Destroy the given RWLock "lock".
  96. */
  97. void
  98. NSSRWLock_Destroy(NSSRWLock *rwlock)
  99. {
  100. PR_ASSERT(rwlock != NULL);
  101. PR_ASSERT(rwlock->rw_waiting_readers == 0);
  102. PR_ASSERT(rwlock->rw_writer_locks == 0);
  103. PR_ASSERT(rwlock->rw_reader_locks == 0);
  104. /* XXX Shouldn't we lock the PZLock before destroying this?? */
  105. if (rwlock->rw_name)
  106. PR_Free(rwlock->rw_name);
  107. if (rwlock->rw_reader_waitq)
  108. PZ_DestroyCondVar(rwlock->rw_reader_waitq);
  109. if (rwlock->rw_writer_waitq)
  110. PZ_DestroyCondVar(rwlock->rw_writer_waitq);
  111. if (rwlock->rw_lock)
  112. PZ_DestroyLock(rwlock->rw_lock);
  113. PR_DELETE(rwlock);
  114. }
  115. /*
  116. ** Read-lock the RWLock.
  117. */
  118. void
  119. NSSRWLock_LockRead(NSSRWLock *rwlock)
  120. {
  121. PRThread *me = PR_GetCurrentThread();
  122. PZ_Lock(rwlock->rw_lock);
  123. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  124. /*
  125. * assert that rank ordering is not violated; the rank of 'rwlock' should
  126. * be equal to or greater than the highest rank of all the locks held by
  127. * the thread.
  128. */
  129. PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
  130. (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
  131. #endif
  132. /*
  133. * wait if write-locked or if a writer is waiting; preference for writers
  134. */
  135. UNTIL((rwlock->rw_owner == me) || /* I own it, or */
  136. ((rwlock->rw_owner == NULL) && /* no-one owns it, and */
  137. (rwlock->rw_waiting_writers == 0)))
  138. { /* no-one is waiting to own */
  139. rwlock->rw_waiting_readers++;
  140. PZ_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
  141. rwlock->rw_waiting_readers--;
  142. }
  143. rwlock->rw_reader_locks++; /* Increment read-lock count */
  144. PZ_Unlock(rwlock->rw_lock);
  145. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  146. nssRWLock_SetThreadRank(me, rwlock); /* update thread's lock rank */
  147. #endif
  148. }
  149. /* Unlock a Read lock held on this RW lock.
  150. */
  151. void
  152. NSSRWLock_UnlockRead(NSSRWLock *rwlock)
  153. {
  154. PZ_Lock(rwlock->rw_lock);
  155. PR_ASSERT(rwlock->rw_reader_locks > 0); /* lock must be read locked */
  156. if ((rwlock->rw_reader_locks > 0) && /* caller isn't screwey */
  157. (--rwlock->rw_reader_locks == 0) && /* not read locked any more */
  158. (rwlock->rw_owner == NULL) && /* not write locked */
  159. (rwlock->rw_waiting_writers > 0)) { /* someone's waiting. */
  160. PZ_NotifyCondVar(rwlock->rw_writer_waitq); /* wake him up. */
  161. }
  162. PZ_Unlock(rwlock->rw_lock);
  163. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  164. /*
  165. * update thread's lock rank
  166. */
  167. nssRWLock_UnsetThreadRank(me, rwlock);
  168. #endif
  169. return;
  170. }
  171. /*
  172. ** Write-lock the RWLock.
  173. */
  174. void
  175. NSSRWLock_LockWrite(NSSRWLock *rwlock)
  176. {
  177. PRThread *me = PR_GetCurrentThread();
  178. PZ_Lock(rwlock->rw_lock);
  179. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  180. /*
  181. * assert that rank ordering is not violated; the rank of 'rwlock' should
  182. * be equal to or greater than the highest rank of all the locks held by
  183. * the thread.
  184. */
  185. PR_ASSERT((rwlock->rw_rank == NSS_RWLOCK_RANK_NONE) ||
  186. (rwlock->rw_rank >= nssRWLock_GetThreadRank(me)));
  187. #endif
  188. /*
  189. * wait if read locked or write locked.
  190. */
  191. PR_ASSERT(rwlock->rw_reader_locks >= 0);
  192. PR_ASSERT(me != NULL);
  193. UNTIL((rwlock->rw_owner == me) || /* I own write lock, or */
  194. ((rwlock->rw_owner == NULL) && /* no writer and */
  195. (rwlock->rw_reader_locks == 0)))
  196. { /* no readers, either. */
  197. rwlock->rw_waiting_writers++;
  198. PZ_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
  199. rwlock->rw_waiting_writers--;
  200. PR_ASSERT(rwlock->rw_reader_locks >= 0);
  201. }
  202. PR_ASSERT(rwlock->rw_reader_locks == 0);
  203. /*
  204. * apply write lock
  205. */
  206. rwlock->rw_owner = me;
  207. rwlock->rw_writer_locks++; /* Increment write-lock count */
  208. PZ_Unlock(rwlock->rw_lock);
  209. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  210. /*
  211. * update thread's lock rank
  212. */
  213. nssRWLock_SetThreadRank(me, rwlock);
  214. #endif
  215. }
  216. /* Unlock a Read lock held on this RW lock.
  217. */
  218. void
  219. NSSRWLock_UnlockWrite(NSSRWLock *rwlock)
  220. {
  221. PRThread *me = PR_GetCurrentThread();
  222. PZ_Lock(rwlock->rw_lock);
  223. PR_ASSERT(rwlock->rw_owner == me); /* lock must be write-locked by me. */
  224. PR_ASSERT(rwlock->rw_writer_locks > 0); /* lock must be write locked */
  225. if (rwlock->rw_owner == me && /* I own it, and */
  226. rwlock->rw_writer_locks > 0 && /* I own it, and */
  227. --rwlock->rw_writer_locks == 0) { /* I'm all done with it */
  228. rwlock->rw_owner = NULL; /* I don't own it any more. */
  229. /* Give preference to waiting writers. */
  230. if (rwlock->rw_waiting_writers > 0) {
  231. if (rwlock->rw_reader_locks == 0)
  232. PZ_NotifyCondVar(rwlock->rw_writer_waitq);
  233. } else if (rwlock->rw_waiting_readers > 0) {
  234. PZ_NotifyAllCondVar(rwlock->rw_reader_waitq);
  235. }
  236. }
  237. PZ_Unlock(rwlock->rw_lock);
  238. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  239. /*
  240. * update thread's lock rank
  241. */
  242. nssRWLock_UnsetThreadRank(me, rwlock);
  243. #endif
  244. return;
  245. }
  246. /* This is primarily for debugging, i.e. for inclusion in ASSERT calls. */
  247. PRBool
  248. NSSRWLock_HaveWriteLock(NSSRWLock *rwlock)
  249. {
  250. PRBool ownWriteLock;
  251. PRThread *me = PR_GetCurrentThread();
  252. /* This lock call isn't really necessary.
  253. ** If this thread is the owner, that fact cannot change during this call,
  254. ** because this thread is in this call.
  255. ** If this thread is NOT the owner, the owner could change, but it
  256. ** could not become this thread.
  257. */
  258. #if UNNECESSARY
  259. PZ_Lock(rwlock->rw_lock);
  260. #endif
  261. ownWriteLock = (PRBool)(me == rwlock->rw_owner);
  262. #if UNNECESSARY
  263. PZ_Unlock(rwlock->rw_lock);
  264. #endif
  265. return ownWriteLock;
  266. }
  267. #ifdef NSS_RWLOCK_RANK_ORDER_DEBUG
  268. /*
  269. * nssRWLock_SetThreadRank
  270. * Set a thread's lock rank, which is the highest of the ranks of all
  271. * the locks held by the thread. Pointers to the locks are added to a
  272. * per-thread list, which is anchored off a thread-private data key.
  273. */
  274. static void
  275. nssRWLock_SetThreadRank(PRThread *me, NSSRWLock *rwlock)
  276. {
  277. thread_rwlock_stack *lock_stack;
  278. PRStatus rv;
  279. /*
  280. * allocated thread-private-data for rwlock list, if not already allocated
  281. */
  282. if (!nss_thread_rwlock_initialized) {
  283. /*
  284. * allocate tpd, only if not failed already
  285. */
  286. if (!nss_thread_rwlock_alloc_failed) {
  287. if (PR_NewThreadPrivateIndex(&nss_thread_rwlock,
  288. nssRWLock_ReleaseLockStack) == PR_FAILURE) {
  289. nss_thread_rwlock_alloc_failed = 1;
  290. return;
  291. }
  292. } else
  293. return;
  294. }
  295. /*
  296. * allocate a lock stack
  297. */
  298. if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL) {
  299. lock_stack = (thread_rwlock_stack *)
  300. PR_CALLOC(1 * sizeof(thread_rwlock_stack));
  301. if (lock_stack) {
  302. rv = PR_SetThreadPrivate(nss_thread_rwlock, lock_stack);
  303. if (rv == PR_FAILURE) {
  304. PR_DELETE(lock_stack);
  305. nss_thread_rwlock_alloc_failed = 1;
  306. return;
  307. }
  308. } else {
  309. nss_thread_rwlock_alloc_failed = 1;
  310. return;
  311. }
  312. }
  313. /*
  314. * add rwlock to lock stack, if limit is not exceeded
  315. */
  316. if (lock_stack) {
  317. if (lock_stack->trs_index < _NSS_RWLOCK_RANK_ORDER_LIMIT)
  318. lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;
  319. }
  320. nss_thread_rwlock_initialized = 1;
  321. }
  322. static void
  323. nssRWLock_ReleaseLockStack(void *lock_stack)
  324. {
  325. PR_ASSERT(lock_stack);
  326. PR_DELETE(lock_stack);
  327. }
  328. /*
  329. * nssRWLock_GetThreadRank
  330. *
  331. * return thread's lock rank. If thread-private-data for the lock
  332. * stack is not allocated, return NSS_RWLOCK_RANK_NONE.
  333. */
  334. static PRUint32
  335. nssRWLock_GetThreadRank(PRThread *me)
  336. {
  337. thread_rwlock_stack *lock_stack;
  338. if (nss_thread_rwlock_initialized) {
  339. if ((lock_stack = PR_GetThreadPrivate(nss_thread_rwlock)) == NULL)
  340. return (NSS_RWLOCK_RANK_NONE);
  341. else
  342. return (lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
  343. } else
  344. return (NSS_RWLOCK_RANK_NONE);
  345. }
  346. /*
  347. * nssRWLock_UnsetThreadRank
  348. *
  349. * remove the rwlock from the lock stack. Since locks may not be
  350. * unlocked in a FIFO order, the entire lock stack is searched.
  351. */
  352. static void
  353. nssRWLock_UnsetThreadRank(PRThread *me, NSSRWLock *rwlock)
  354. {
  355. thread_rwlock_stack *lock_stack;
  356. int new_index = 0, index, done = 0;
  357. if (!nss_thread_rwlock_initialized)
  358. return;
  359. lock_stack = PR_GetThreadPrivate(nss_thread_rwlock);
  360. PR_ASSERT(lock_stack != NULL);
  361. index = lock_stack->trs_index - 1;
  362. while (index-- >= 0) {
  363. if ((lock_stack->trs_stack[index] == rwlock) && !done) {
  364. /*
  365. * reset the slot for rwlock
  366. */
  367. lock_stack->trs_stack[index] = NULL;
  368. done = 1;
  369. }
  370. /*
  371. * search for the lowest-numbered empty slot, above which there are
  372. * no non-empty slots
  373. */
  374. if ((lock_stack->trs_stack[index] != NULL) && !new_index)
  375. new_index = index + 1;
  376. if (done && new_index)
  377. break;
  378. }
  379. /*
  380. * set top of stack to highest numbered empty slot
  381. */
  382. lock_stack->trs_index = new_index;
  383. }
  384. #endif /* NSS_RWLOCK_RANK_ORDER_DEBUG */