nsprpub/pr/src/threads/prrwlock.c

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "primpl.h"
     8 #include <string.h>
    10 #if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
    12 #include <pthread.h>
    13 #define HAVE_UNIX98_RWLOCK
    14 #define RWLOCK_T pthread_rwlock_t
    15 #define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL)
    16 #define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock)
    17 #define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock)
    18 #define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock)
    19 #define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock)
    21 #elif defined(SOLARIS) && (defined(_PR_PTHREADS) \
    22         || defined(_PR_GLOBAL_THREADS_ONLY))
    24 #include <synch.h>
    25 #define HAVE_UI_RWLOCK
    26 #define RWLOCK_T rwlock_t
    27 #define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL)
    28 #define RWLOCK_DESTROY(lock) rwlock_destroy(lock)
    29 #define RWLOCK_RDLOCK(lock) rw_rdlock(lock)
    30 #define RWLOCK_WRLOCK(lock) rw_wrlock(lock)
    31 #define RWLOCK_UNLOCK(lock) rw_unlock(lock)
    33 #endif
    35 /*
    36  * Reader-writer lock
    37  */
    38 struct PRRWLock {
    39 	char			*rw_name;			/* lock name					*/
    40 	PRUint32		rw_rank;			/* rank of the lock				*/
    42 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
    43 	RWLOCK_T		rw_lock;
    44 #else
    45     PRLock			*rw_lock;
    46 	PRInt32			rw_lock_cnt;		/* ==  0, if unlocked			*/
    47 										/* == -1, if write-locked		*/
    48 										/* > 0	, # of read locks		*/
    49 	PRUint32		rw_reader_cnt;		/* number of waiting readers	*/
    50 	PRUint32		rw_writer_cnt;		/* number of waiting writers	*/
    51 	PRCondVar   	*rw_reader_waitq;	/* cvar for readers 			*/
    52 	PRCondVar   	*rw_writer_waitq;	/* cvar for writers				*/
    53 #ifdef DEBUG
    54     PRThread 		*rw_owner;			/* lock owner for write-lock	*/
    55 #endif
    56 #endif
    57 };
    59 #ifdef DEBUG
    60 #define _PR_RWLOCK_RANK_ORDER_DEBUG	/* enable deadlock detection using
    61 									   rank-order for locks
    62 									*/
    63 #endif
    65 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
    67 static PRUintn	pr_thread_rwlock_key;			/* TPD key for lock stack */
    68 static PRUintn	pr_thread_rwlock_alloc_failed;
    70 #define	_PR_RWLOCK_RANK_ORDER_LIMIT	10
    72 typedef struct thread_rwlock_stack {
    73 	PRInt32		trs_index;									/* top of stack */
    74 	PRRWLock	*trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT];	/* stack of lock
    75 														 	   pointers */
    77 } thread_rwlock_stack;
    79 static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);
    80 static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void);
    81 static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);
    82 static void _PR_RELEASE_LOCK_STACK(void *lock_stack);
    84 #endif
    86 /*
    87  * Reader/Writer Locks
    88  */
    90 /*
    91  * PR_NewRWLock
    92  *		Create a reader-writer lock, with the given lock rank and lock name
    93  *	
    94  */
    96 PR_IMPLEMENT(PRRWLock *)
    97 PR_NewRWLock(PRUint32 lock_rank, const char *lock_name)
    98 {
    99     PRRWLock *rwlock;
   100 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   101 	int err;
   102 #endif
   104     if (!_pr_initialized) _PR_ImplicitInitialization();
   106     rwlock = PR_NEWZAP(PRRWLock);
   107     if (rwlock == NULL)
   108 		return NULL;
   110 	rwlock->rw_rank = lock_rank;
   111 	if (lock_name != NULL) {
   112 		rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
   113     	if (rwlock->rw_name == NULL) {
   114 			PR_DELETE(rwlock);
   115 			return(NULL);
   116 		}
   117 		strcpy(rwlock->rw_name, lock_name);
   118 	} else {
   119 		rwlock->rw_name = NULL;
   120 	}
   122 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   123 	err = RWLOCK_INIT(&rwlock->rw_lock);
   124 	if (err != 0) {
   125 		PR_SetError(PR_UNKNOWN_ERROR, err);
   126 		PR_Free(rwlock->rw_name);
   127 		PR_DELETE(rwlock);
   128 		return NULL;
   129 	}
   130 	return rwlock;
   131 #else
   132 	rwlock->rw_lock = PR_NewLock();
   133     if (rwlock->rw_lock == NULL) {
   134 		goto failed;
   135 	}
   136 	rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
   137     if (rwlock->rw_reader_waitq == NULL) {
   138 		goto failed;
   139 	}
   140 	rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
   141     if (rwlock->rw_writer_waitq == NULL) {
   142 		goto failed;
   143 	}
   144 	rwlock->rw_reader_cnt = 0;
   145 	rwlock->rw_writer_cnt = 0;
   146 	rwlock->rw_lock_cnt = 0;
   147 	return rwlock;
   149 failed:
   150 	if (rwlock->rw_reader_waitq != NULL) {
   151 		PR_DestroyCondVar(rwlock->rw_reader_waitq);	
   152 	}
   153 	if (rwlock->rw_lock != NULL) {
   154 		PR_DestroyLock(rwlock->rw_lock);
   155 	}
   156 	PR_Free(rwlock->rw_name);
   157 	PR_DELETE(rwlock);
   158 	return NULL;
   159 #endif
   160 }
   162 /*
   163 ** Destroy the given RWLock "lock".
   164 */
   165 PR_IMPLEMENT(void)
   166 PR_DestroyRWLock(PRRWLock *rwlock)
   167 {
   168 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   169 	int err;
   170 	err = RWLOCK_DESTROY(&rwlock->rw_lock);
   171 	PR_ASSERT(err == 0);
   172 #else
   173 	PR_ASSERT(rwlock->rw_reader_cnt == 0);
   174 	PR_DestroyCondVar(rwlock->rw_reader_waitq);	
   175 	PR_DestroyCondVar(rwlock->rw_writer_waitq);	
   176 	PR_DestroyLock(rwlock->rw_lock);
   177 #endif
   178 	if (rwlock->rw_name != NULL)
   179 		PR_Free(rwlock->rw_name);
   180     PR_DELETE(rwlock);
   181 }
   183 /*
   184 ** Read-lock the RWLock.
   185 */
   186 PR_IMPLEMENT(void)
   187 PR_RWLock_Rlock(PRRWLock *rwlock)
   188 {
   189 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   190 int err;
   191 #endif
   193 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
   194 	/*
   195 	 * assert that rank ordering is not violated; the rank of 'rwlock' should
   196 	 * be equal to or greater than the highest rank of all the locks held by
   197 	 * the thread.
   198 	 */
   199 	PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || 
   200 					(rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
   201 #endif
   203 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   204 	err = RWLOCK_RDLOCK(&rwlock->rw_lock);
   205 	PR_ASSERT(err == 0);
   206 #else
   207 	PR_Lock(rwlock->rw_lock);
   208 	/*
   209 	 * wait if write-locked or if a writer is waiting; preference for writers
   210 	 */
   211 	while ((rwlock->rw_lock_cnt < 0) ||
   212 			(rwlock->rw_writer_cnt > 0)) {
   213 		rwlock->rw_reader_cnt++;
   214 		PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
   215 		rwlock->rw_reader_cnt--;
   216 	}
   217 	/*
   218 	 * Increment read-lock count
   219 	 */
   220 	rwlock->rw_lock_cnt++;
   222 	PR_Unlock(rwlock->rw_lock);
   223 #endif
   225 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
   226 	/*
   227 	 * update thread's lock rank
   228 	 */
   229 	if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE)
   230 		_PR_SET_THREAD_RWLOCK_RANK(rwlock);
   231 #endif
   232 }
   234 /*
   235 ** Write-lock the RWLock.
   236 */
   237 PR_IMPLEMENT(void)
   238 PR_RWLock_Wlock(PRRWLock *rwlock)
   239 {
   240 #if defined(DEBUG)
   241 PRThread *me = PR_GetCurrentThread();
   242 #endif
   243 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   244 int err;
   245 #endif
   247 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
   248 	/*
   249 	 * assert that rank ordering is not violated; the rank of 'rwlock' should
   250 	 * be equal to or greater than the highest rank of all the locks held by
   251 	 * the thread.
   252 	 */
   253 	PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || 
   254 					(rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
   255 #endif
   257 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   258 	err = RWLOCK_WRLOCK(&rwlock->rw_lock);
   259 	PR_ASSERT(err == 0);
   260 #else
   261 	PR_Lock(rwlock->rw_lock);
   262 	/*
   263 	 * wait if read locked
   264 	 */
   265 	while (rwlock->rw_lock_cnt != 0) {
   266 		rwlock->rw_writer_cnt++;
   267 		PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
   268 		rwlock->rw_writer_cnt--;
   269 	}
   270 	/*
   271 	 * apply write lock
   272 	 */
   273 	rwlock->rw_lock_cnt--;
   274 	PR_ASSERT(rwlock->rw_lock_cnt == -1);
   275 #ifdef DEBUG
   276 	PR_ASSERT(me != NULL);
   277 	rwlock->rw_owner = me;
   278 #endif
   279 	PR_Unlock(rwlock->rw_lock);
   280 #endif
   282 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
   283 	/*
   284 	 * update thread's lock rank
   285 	 */
   286 	if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE)
   287 		_PR_SET_THREAD_RWLOCK_RANK(rwlock);
   288 #endif
   289 }
   291 /*
   292 ** Unlock the RW lock.
   293 */
   294 PR_IMPLEMENT(void)
   295 PR_RWLock_Unlock(PRRWLock *rwlock)
   296 {
   297 #if defined(DEBUG)
   298 PRThread *me = PR_GetCurrentThread();
   299 #endif
   300 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   301 int err;
   302 #endif
   304 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
   305 	err = RWLOCK_UNLOCK(&rwlock->rw_lock);
   306 	PR_ASSERT(err == 0);
   307 #else
   308 	PR_Lock(rwlock->rw_lock);
   309 	/*
   310 	 * lock must be read or write-locked
   311 	 */
   312 	PR_ASSERT(rwlock->rw_lock_cnt != 0);
   313 	if (rwlock->rw_lock_cnt > 0) {
   315 		/*
   316 		 * decrement read-lock count
   317 		 */
   318 		rwlock->rw_lock_cnt--;
   319 		if (rwlock->rw_lock_cnt == 0) {
   320 			/*
   321 			 * lock is not read-locked anymore; wakeup a waiting writer
   322 			 */
   323 			if (rwlock->rw_writer_cnt > 0)
   324 				PR_NotifyCondVar(rwlock->rw_writer_waitq);
   325 		}
   326 	} else {
   327 		PR_ASSERT(rwlock->rw_lock_cnt == -1);
   329 		rwlock->rw_lock_cnt = 0;
   330 #ifdef DEBUG
   331     	PR_ASSERT(rwlock->rw_owner == me);
   332     	rwlock->rw_owner = NULL;
   333 #endif
   334 		/*
   335 		 * wakeup a writer, if present; preference for writers
   336 		 */
   337 		if (rwlock->rw_writer_cnt > 0)
   338 			PR_NotifyCondVar(rwlock->rw_writer_waitq);
   339 		/*
   340 		 * else, wakeup all readers, if any
   341 		 */
   342 		else if (rwlock->rw_reader_cnt > 0)
   343 			PR_NotifyAllCondVar(rwlock->rw_reader_waitq);
   344 	}
   345 	PR_Unlock(rwlock->rw_lock);
   346 #endif
   348 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
   349 	/*
   350 	 * update thread's lock rank
   351 	 */
   352 	if (rwlock->rw_rank != PR_RWLOCK_RANK_NONE)
   353 		_PR_UNSET_THREAD_RWLOCK_RANK(rwlock);
   354 #endif
   355 	return;
   356 }
   358 #ifndef _PR_RWLOCK_RANK_ORDER_DEBUG
   360 void _PR_InitRWLocks(void) { }
   362 #else
   364 void _PR_InitRWLocks(void)
   365 {
   366 	/*
   367 	 * allocated thread-private-data index for rwlock list
   368 	 */
   369 	if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key,
   370 			_PR_RELEASE_LOCK_STACK) == PR_FAILURE) {
   371 		pr_thread_rwlock_alloc_failed = 1;
   372 		return;
   373 	}
   374 }
   376 /*
   377  * _PR_SET_THREAD_RWLOCK_RANK
   378  *		Set a thread's lock rank, which is the highest of the ranks of all
   379  *		the locks held by the thread. Pointers to the locks are added to a
   380  *		per-thread list, which is anchored off a thread-private data key.
   381  */
   383 static void
   384 _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
   385 {
   386 thread_rwlock_stack *lock_stack;
   387 PRStatus rv;
   389 	/*
   390 	 * allocate a lock stack
   391 	 */
   392 	if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) {
   393 		lock_stack = (thread_rwlock_stack *)
   394 						PR_CALLOC(1 * sizeof(thread_rwlock_stack));
   395 		if (lock_stack) {
   396 			rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack);
   397 			if (rv == PR_FAILURE) {
   398 				PR_DELETE(lock_stack);
   399 				pr_thread_rwlock_alloc_failed = 1;
   400 				return;
   401 			}
   402 		} else {
   403 			pr_thread_rwlock_alloc_failed = 1;
   404 			return;
   405 		}
   406 	}
   407 	/*
   408 	 * add rwlock to lock stack, if limit is not exceeded
   409 	 */
   410 	if (lock_stack) {
   411 		if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT)
   412 			lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;	
   413 	}
   414 }
   416 static void
   417 _PR_RELEASE_LOCK_STACK(void *lock_stack)
   418 {
   419 	PR_ASSERT(lock_stack);
   420 	PR_DELETE(lock_stack);
   421 }
   423 /*
   424  * _PR_GET_THREAD_RWLOCK_RANK
   425  *
   426  *		return thread's lock rank. If thread-private-data for the lock
   427  *		stack is not allocated, return PR_RWLOCK_RANK_NONE.
   428  */
   430 static PRUint32
   431 _PR_GET_THREAD_RWLOCK_RANK(void)
   432 {
   433 	thread_rwlock_stack *lock_stack;
   435 	lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
   436 	if (lock_stack == NULL || lock_stack->trs_index == 0)
   437 		return (PR_RWLOCK_RANK_NONE);
   438 	else
   439 		return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
   440 }
   442 /*
   443  * _PR_UNSET_THREAD_RWLOCK_RANK
   444  *
   445  *		remove the rwlock from the lock stack. Since locks may not be
   446  *		unlocked in a FIFO order, the entire lock stack is searched.
   447  */
   449 static void
   450 _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
   451 {
   452 	thread_rwlock_stack *lock_stack;
   453 	int new_index = 0, index, done = 0;
   455 	lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
   457 	PR_ASSERT(lock_stack != NULL);
   459 	for (index = lock_stack->trs_index - 1; index >= 0; index--) {
   460 		if (!done && (lock_stack->trs_stack[index] == rwlock))  {
   461 			/*
   462 			 * reset the slot for rwlock
   463 			 */
   464 			lock_stack->trs_stack[index] = NULL;
   465 			done = 1;
   466 		}
   467 		/*
   468 		 * search for the lowest-numbered empty slot, above which there are
   469 		 * no non-empty slots
   470 		 */
   471 		if (!new_index && (lock_stack->trs_stack[index] != NULL))
   472 			new_index = index + 1;
   473 		if (done && new_index)
   474 			break;
   475 	}
   476 	/*
   477 	 * set top of stack to highest numbered empty slot
   478 	 */
   479 	lock_stack->trs_index = new_index;
   481 }
   483 #endif 	/* _PR_RWLOCK_RANK_ORDER_DEBUG */

mercurial