📄 prrwlock.c
字号:
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape Portable Runtime (NSPR). * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998-2000 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */#include "primpl.h"#include <string.h>#if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)#include <pthread.h>#define HAVE_UNIX98_RWLOCK#define RWLOCK_T pthread_rwlock_t#define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL)#define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock)#define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock)#define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock)#define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock)#elif defined(SOLARIS) && (defined(_PR_PTHREADS) \ || defined(_PR_GLOBAL_THREADS_ONLY))#include <synch.h>#define HAVE_UI_RWLOCK#define RWLOCK_T rwlock_t#define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL)#define RWLOCK_DESTROY(lock) rwlock_destroy(lock)#define RWLOCK_RDLOCK(lock) rw_rdlock(lock)#define RWLOCK_WRLOCK(lock) rw_wrlock(lock)#define RWLOCK_UNLOCK(lock) rw_unlock(lock)#endif/* * Reader-writer lock */struct PRRWLock { char *rw_name; /* lock name */ PRUint32 rw_rank; /* rank of the lock */#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) RWLOCK_T rw_lock;#else PRLock *rw_lock; PRInt32 rw_lock_cnt; /* == 0, if unlocked */ /* == -1, if write-locked */ /* > 0 , # of read locks */ PRUint32 rw_reader_cnt; /* number of waiting readers */ PRUint32 rw_writer_cnt; /* number of waiting writers */ PRCondVar *rw_reader_waitq; /* cvar for readers */ PRCondVar *rw_writer_waitq; /* cvar for writers */#ifdef DEBUG PRThread *rw_owner; /* lock owner for write-lock */#endif#endif};#ifdef DEBUG#define _PR_RWLOCK_RANK_ORDER_DEBUG /* enable deadlock detection using rank-order for locks */#endif#ifdef _PR_RWLOCK_RANK_ORDER_DEBUGstatic PRUintn pr_thread_rwlock_key; /* TPD key for lock stack */static PRUintn pr_thread_rwlock_alloc_failed;#define _PR_RWLOCK_RANK_ORDER_LIMIT 10typedef struct thread_rwlock_stack { PRInt32 trs_index; /* top of stack */ PRRWLock *trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT]; /* stack of lock pointers */} thread_rwlock_stack;static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void);static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);static void _PR_RELEASE_LOCK_STACK(void *lock_stack);#endif/* * Reader/Writer Locks *//* * PR_NewRWLock * Create a reader-writer lock, with the given lock rank and lock name * */PR_IMPLEMENT(PRRWLock *)PR_NewRWLock(PRUint32 lock_rank, const char *lock_name){ PRRWLock *rwlock;#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) int err;#endif if (!_pr_initialized) _PR_ImplicitInitialization(); rwlock = PR_NEWZAP(PRRWLock); if (rwlock == NULL) return NULL; rwlock->rw_rank = lock_rank; if (lock_name != NULL) { rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1); if (rwlock->rw_name == NULL) { PR_DELETE(rwlock); return(NULL); } strcpy(rwlock->rw_name, lock_name); } else { rwlock->rw_name = NULL; } #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) err = RWLOCK_INIT(&rwlock->rw_lock); if (err != 0) { PR_SetError(PR_UNKNOWN_ERROR, err); PR_Free(rwlock->rw_name); PR_DELETE(rwlock); return NULL; } return rwlock;#else rwlock->rw_lock = PR_NewLock(); if (rwlock->rw_lock == NULL) { goto failed; } rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock); if (rwlock->rw_reader_waitq == NULL) { goto failed; } rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock); if (rwlock->rw_writer_waitq == NULL) { goto failed; } rwlock->rw_reader_cnt = 0; rwlock->rw_writer_cnt = 0; rwlock->rw_lock_cnt = 0; return rwlock;failed: if (rwlock->rw_reader_waitq != NULL) { PR_DestroyCondVar(rwlock->rw_reader_waitq); } if (rwlock->rw_lock != NULL) { PR_DestroyLock(rwlock->rw_lock); } PR_Free(rwlock->rw_name); PR_DELETE(rwlock); return NULL;#endif}/*** Destroy the given RWLock "lock".*/PR_IMPLEMENT(void)PR_DestroyRWLock(PRRWLock *rwlock){#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) int err; err = RWLOCK_DESTROY(&rwlock->rw_lock); PR_ASSERT(err == 0);#else PR_ASSERT(rwlock->rw_reader_cnt == 0); PR_DestroyCondVar(rwlock->rw_reader_waitq); PR_DestroyCondVar(rwlock->rw_writer_waitq); PR_DestroyLock(rwlock->rw_lock);#endif if (rwlock->rw_name != NULL) PR_Free(rwlock->rw_name); PR_DELETE(rwlock);}/*** Read-lock the RWLock.*/PR_IMPLEMENT(void)PR_RWLock_Rlock(PRRWLock *rwlock){#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)int err;#endif#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG /* * assert that rank ordering is not violated; the rank of 'rwlock' should * be equal to or greater than the highest rank of all the locks held by * the thread. */ PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));#endif#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) err = RWLOCK_RDLOCK(&rwlock->rw_lock); PR_ASSERT(err == 0);#else PR_Lock(rwlock->rw_lock); /* * wait if write-locked or if a writer is waiting; preference for writers */ while ((rwlock->rw_lock_cnt < 0) || (rwlock->rw_writer_cnt > 0)) { rwlock->rw_reader_cnt++; PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT); rwlock->rw_reader_cnt--; } /* * Increment read-lock count */ rwlock->rw_lock_cnt++; PR_Unlock(rwlock->rw_lock);#endif#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG /* * update thread's lock rank */ _PR_SET_THREAD_RWLOCK_RANK(rwlock);#endif}/*** Write-lock the RWLock.*/PR_IMPLEMENT(void)PR_RWLock_Wlock(PRRWLock *rwlock){#if defined(DEBUG)PRThread *me = PR_GetCurrentThread();#endif#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)int err;#endif#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG /* * assert that rank ordering is not violated; the rank of 'rwlock' should * be equal to or greater than the highest rank of all the locks held by * the thread. */ PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));#endif#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) err = RWLOCK_WRLOCK(&rwlock->rw_lock); PR_ASSERT(err == 0);#else PR_Lock(rwlock->rw_lock); /* * wait if read locked */ while (rwlock->rw_lock_cnt != 0) { rwlock->rw_writer_cnt++; PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT); rwlock->rw_writer_cnt--; } /* * apply write lock */ rwlock->rw_lock_cnt--; PR_ASSERT(rwlock->rw_lock_cnt == -1);#ifdef DEBUG PR_ASSERT(me != NULL); rwlock->rw_owner = me;#endif PR_Unlock(rwlock->rw_lock);#endif#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG /* * update thread's lock rank */ _PR_SET_THREAD_RWLOCK_RANK(rwlock);#endif}/*** Unlock the RW lock.*/PR_IMPLEMENT(void)PR_RWLock_Unlock(PRRWLock *rwlock){#if defined(DEBUG)PRThread *me = PR_GetCurrentThread();#endif#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)int err;#endif#if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK) err = RWLOCK_UNLOCK(&rwlock->rw_lock); PR_ASSERT(err == 0);#else PR_Lock(rwlock->rw_lock); /* * lock must be read or write-locked */ PR_ASSERT(rwlock->rw_lock_cnt != 0); if (rwlock->rw_lock_cnt > 0) { /* * decrement read-lock count */ rwlock->rw_lock_cnt--; if (rwlock->rw_lock_cnt == 0) { /* * lock is not read-locked anymore; wakeup a waiting writer */ if (rwlock->rw_writer_cnt > 0) PR_NotifyCondVar(rwlock->rw_writer_waitq); } } else { PR_ASSERT(rwlock->rw_lock_cnt == -1); rwlock->rw_lock_cnt = 0;#ifdef DEBUG PR_ASSERT(rwlock->rw_owner == me); rwlock->rw_owner = NULL;#endif /* * wakeup a writer, if present; preference for writers */ if (rwlock->rw_writer_cnt > 0) PR_NotifyCondVar(rwlock->rw_writer_waitq); /* * else, wakeup all readers, if any */ else if (rwlock->rw_reader_cnt > 0) PR_NotifyAllCondVar(rwlock->rw_reader_waitq); } PR_Unlock(rwlock->rw_lock);#endif#ifdef _PR_RWLOCK_RANK_ORDER_DEBUG /* * update thread's lock rank */ _PR_UNSET_THREAD_RWLOCK_RANK(rwlock);#endif return;}#ifndef _PR_RWLOCK_RANK_ORDER_DEBUGvoid _PR_InitRWLocks(void) { }#elsevoid _PR_InitRWLocks(void){ /* * allocated thread-private-data index for rwlock list */ if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key, _PR_RELEASE_LOCK_STACK) == PR_FAILURE) { pr_thread_rwlock_alloc_failed = 1; return; }}/* * _PR_SET_THREAD_RWLOCK_RANK * Set a thread's lock rank, which is the highest of the ranks of all * the locks held by the thread. Pointers to the locks are added to a * per-thread list, which is anchored off a thread-private data key. */static void_PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock){thread_rwlock_stack *lock_stack;PRStatus rv; /* * allocate a lock stack */ if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) { lock_stack = (thread_rwlock_stack *) PR_CALLOC(1 * sizeof(thread_rwlock_stack)); if (lock_stack) { rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack); if (rv == PR_FAILURE) { PR_DELETE(lock_stack); pr_thread_rwlock_alloc_failed = 1; return; } } else { pr_thread_rwlock_alloc_failed = 1; return; } } /* * add rwlock to lock stack, if limit is not exceeded */ if (lock_stack) { if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT) lock_stack->trs_stack[lock_stack->trs_index++] = rwlock; }}static void_PR_RELEASE_LOCK_STACK(void *lock_stack){ PR_ASSERT(lock_stack); PR_DELETE(lock_stack);}/* * _PR_GET_THREAD_RWLOCK_RANK * * return thread's lock rank. If thread-private-data for the lock * stack is not allocated, return PR_RWLOCK_RANK_NONE. */ static PRUint32_PR_GET_THREAD_RWLOCK_RANK(void){ thread_rwlock_stack *lock_stack; if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) return (PR_RWLOCK_RANK_NONE); else return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);}/* * _PR_UNSET_THREAD_RWLOCK_RANK * * remove the rwlock from the lock stack. Since locks may not be * unlocked in a FIFO order, the entire lock stack is searched. */ static void_PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock){ thread_rwlock_stack *lock_stack; int new_index = 0, index, done = 0; lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key); PR_ASSERT(lock_stack != NULL); index = lock_stack->trs_index - 1; while (index-- >= 0) { if ((lock_stack->trs_stack[index] == rwlock) && !done) { /* * reset the slot for rwlock */ lock_stack->trs_stack[index] = NULL; done = 1; } /* * search for the lowest-numbered empty slot, above which there are * no non-empty slots */ if ((lock_stack->trs_stack[index] != NULL) && !new_index) new_index = index + 1; if (done && new_index) break; } /* * set top of stack to highest numbered empty slot */ lock_stack->trs_index = new_index;}#endif /* _PR_RWLOCK_RANK_ORDER_DEBUG */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -