Back to index

lightning-sunbird  0.9+nobinonly
prrwlock.c
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Netscape Portable Runtime (NSPR).
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-2000
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "primpl.h"
00039 
00040 #include <string.h>
00041 
00042 #if defined(HPUX) && defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
00043 
00044 #include <pthread.h>
00045 #define HAVE_UNIX98_RWLOCK
00046 #define RWLOCK_T pthread_rwlock_t
00047 #define RWLOCK_INIT(lock) pthread_rwlock_init(lock, NULL)
00048 #define RWLOCK_DESTROY(lock) pthread_rwlock_destroy(lock)
00049 #define RWLOCK_RDLOCK(lock) pthread_rwlock_rdlock(lock)
00050 #define RWLOCK_WRLOCK(lock) pthread_rwlock_wrlock(lock)
00051 #define RWLOCK_UNLOCK(lock) pthread_rwlock_unlock(lock)
00052 
00053 #elif defined(SOLARIS) && (defined(_PR_PTHREADS) \
00054         || defined(_PR_GLOBAL_THREADS_ONLY))
00055 
00056 #include <synch.h>
00057 #define HAVE_UI_RWLOCK
00058 #define RWLOCK_T rwlock_t
00059 #define RWLOCK_INIT(lock) rwlock_init(lock, USYNC_THREAD, NULL)
00060 #define RWLOCK_DESTROY(lock) rwlock_destroy(lock)
00061 #define RWLOCK_RDLOCK(lock) rw_rdlock(lock)
00062 #define RWLOCK_WRLOCK(lock) rw_wrlock(lock)
00063 #define RWLOCK_UNLOCK(lock) rw_unlock(lock)
00064 
00065 #endif
00066 
00067 /*
00068  * Reader-writer lock
00069  */
00070 struct PRRWLock {
00071        char                 *rw_name;                   /* lock name                              */
00072        PRUint32             rw_rank;                    /* rank of the lock                       */
00073 
00074 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00075        RWLOCK_T             rw_lock;
00076 #else
00077     PRLock                  *rw_lock;
00078        PRInt32                     rw_lock_cnt;         /* ==  0, if unlocked                     */
00079                                                                       /* == -1, if write-locked          */
00080                                                                       /* > 0 , # of read locks           */
00081        PRUint32             rw_reader_cnt;              /* number of waiting readers       */
00082        PRUint32             rw_writer_cnt;              /* number of waiting writers       */
00083        PRCondVar     *rw_reader_waitq;    /* cvar for readers                */
00084        PRCondVar     *rw_writer_waitq;    /* cvar for writers                       */
00085 #ifdef DEBUG
00086     PRThread         *rw_owner;                  /* lock owner for write-lock       */
00087 #endif
00088 #endif
00089 };
00090 
00091 #ifdef DEBUG
00092 #define _PR_RWLOCK_RANK_ORDER_DEBUG       /* enable deadlock detection using
00093                                                                   rank-order for locks
00094                                                                */
00095 #endif
00096 
00097 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
00098 
00099 static PRUintn       pr_thread_rwlock_key;                     /* TPD key for lock stack */
00100 static PRUintn       pr_thread_rwlock_alloc_failed;
00101 
00102 #define       _PR_RWLOCK_RANK_ORDER_LIMIT 10
00103 
00104 typedef struct thread_rwlock_stack {
00105        PRInt32              trs_index;                                                            /* top of stack */
00106        PRRWLock      *trs_stack[_PR_RWLOCK_RANK_ORDER_LIMIT];  /* stack of lock
00107                                                                                                             pointers */
00108 
00109 } thread_rwlock_stack;
00110 
00111 static void _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);
00112 static PRUint32 _PR_GET_THREAD_RWLOCK_RANK(void);
00113 static void _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock);
00114 static void _PR_RELEASE_LOCK_STACK(void *lock_stack);
00115 
00116 #endif
00117 
00118 /*
00119  * Reader/Writer Locks
00120  */
00121 
00122 /*
00123  * PR_NewRWLock
00124  *            Create a reader-writer lock, with the given lock rank and lock name
00125  *     
00126  */
00127 
00128 PR_IMPLEMENT(PRRWLock *)
00129 PR_NewRWLock(PRUint32 lock_rank, const char *lock_name)
00130 {
00131     PRRWLock *rwlock;
00132 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00133        int err;
00134 #endif
00135 
00136     if (!_pr_initialized) _PR_ImplicitInitialization();
00137 
00138     rwlock = PR_NEWZAP(PRRWLock);
00139     if (rwlock == NULL)
00140               return NULL;
00141 
00142        rwlock->rw_rank = lock_rank;
00143        if (lock_name != NULL) {
00144               rwlock->rw_name = (char*) PR_Malloc(strlen(lock_name) + 1);
00145        if (rwlock->rw_name == NULL) {
00146                      PR_DELETE(rwlock);
00147                      return(NULL);
00148               }
00149               strcpy(rwlock->rw_name, lock_name);
00150        } else {
00151               rwlock->rw_name = NULL;
00152        }
00153        
00154 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00155        err = RWLOCK_INIT(&rwlock->rw_lock);
00156        if (err != 0) {
00157               PR_SetError(PR_UNKNOWN_ERROR, err);
00158               PR_Free(rwlock->rw_name);
00159               PR_DELETE(rwlock);
00160               return NULL;
00161        }
00162        return rwlock;
00163 #else
00164        rwlock->rw_lock = PR_NewLock();
00165     if (rwlock->rw_lock == NULL) {
00166               goto failed;
00167        }
00168        rwlock->rw_reader_waitq = PR_NewCondVar(rwlock->rw_lock);
00169     if (rwlock->rw_reader_waitq == NULL) {
00170               goto failed;
00171        }
00172        rwlock->rw_writer_waitq = PR_NewCondVar(rwlock->rw_lock);
00173     if (rwlock->rw_writer_waitq == NULL) {
00174               goto failed;
00175        }
00176        rwlock->rw_reader_cnt = 0;
00177        rwlock->rw_writer_cnt = 0;
00178        rwlock->rw_lock_cnt = 0;
00179        return rwlock;
00180 
00181 failed:
00182        if (rwlock->rw_reader_waitq != NULL) {
00183               PR_DestroyCondVar(rwlock->rw_reader_waitq);      
00184        }
00185        if (rwlock->rw_lock != NULL) {
00186               PR_DestroyLock(rwlock->rw_lock);
00187        }
00188        PR_Free(rwlock->rw_name);
00189        PR_DELETE(rwlock);
00190        return NULL;
00191 #endif
00192 }
00193 
00194 /*
00195 ** Destroy the given RWLock "lock".
00196 */
00197 PR_IMPLEMENT(void)
00198 PR_DestroyRWLock(PRRWLock *rwlock)
00199 {
00200 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00201        int err;
00202        err = RWLOCK_DESTROY(&rwlock->rw_lock);
00203        PR_ASSERT(err == 0);
00204 #else
00205        PR_ASSERT(rwlock->rw_reader_cnt == 0);
00206        PR_DestroyCondVar(rwlock->rw_reader_waitq);      
00207        PR_DestroyCondVar(rwlock->rw_writer_waitq);      
00208        PR_DestroyLock(rwlock->rw_lock);
00209 #endif
00210        if (rwlock->rw_name != NULL)
00211               PR_Free(rwlock->rw_name);
00212     PR_DELETE(rwlock);
00213 }
00214 
00215 /*
00216 ** Read-lock the RWLock.
00217 */
00218 PR_IMPLEMENT(void)
00219 PR_RWLock_Rlock(PRRWLock *rwlock)
00220 {
00221 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00222 int err;
00223 #endif
00224 
00225 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
00226        /*
00227         * assert that rank ordering is not violated; the rank of 'rwlock' should
00228         * be equal to or greater than the highest rank of all the locks held by
00229         * the thread.
00230         */
00231        PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || 
00232                                    (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
00233 #endif
00234 
00235 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00236        err = RWLOCK_RDLOCK(&rwlock->rw_lock);
00237        PR_ASSERT(err == 0);
00238 #else
00239        PR_Lock(rwlock->rw_lock);
00240        /*
00241         * wait if write-locked or if a writer is waiting; preference for writers
00242         */
00243        while ((rwlock->rw_lock_cnt < 0) ||
00244                      (rwlock->rw_writer_cnt > 0)) {
00245               rwlock->rw_reader_cnt++;
00246               PR_WaitCondVar(rwlock->rw_reader_waitq, PR_INTERVAL_NO_TIMEOUT);
00247               rwlock->rw_reader_cnt--;
00248        }
00249        /*
00250         * Increment read-lock count
00251         */
00252        rwlock->rw_lock_cnt++;
00253 
00254        PR_Unlock(rwlock->rw_lock);
00255 #endif
00256 
00257 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
00258        /*
00259         * update thread's lock rank
00260         */
00261        _PR_SET_THREAD_RWLOCK_RANK(rwlock);
00262 #endif
00263 }
00264 
00265 /*
00266 ** Write-lock the RWLock.
00267 */
00268 PR_IMPLEMENT(void)
00269 PR_RWLock_Wlock(PRRWLock *rwlock)
00270 {
00271 #if defined(DEBUG)
00272 PRThread *me = PR_GetCurrentThread();
00273 #endif
00274 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00275 int err;
00276 #endif
00277 
00278 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
00279        /*
00280         * assert that rank ordering is not violated; the rank of 'rwlock' should
00281         * be equal to or greater than the highest rank of all the locks held by
00282         * the thread.
00283         */
00284        PR_ASSERT((rwlock->rw_rank == PR_RWLOCK_RANK_NONE) || 
00285                                    (rwlock->rw_rank >= _PR_GET_THREAD_RWLOCK_RANK()));
00286 #endif
00287 
00288 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00289        err = RWLOCK_WRLOCK(&rwlock->rw_lock);
00290        PR_ASSERT(err == 0);
00291 #else
00292        PR_Lock(rwlock->rw_lock);
00293        /*
00294         * wait if read locked
00295         */
00296        while (rwlock->rw_lock_cnt != 0) {
00297               rwlock->rw_writer_cnt++;
00298               PR_WaitCondVar(rwlock->rw_writer_waitq, PR_INTERVAL_NO_TIMEOUT);
00299               rwlock->rw_writer_cnt--;
00300        }
00301        /*
00302         * apply write lock
00303         */
00304        rwlock->rw_lock_cnt--;
00305        PR_ASSERT(rwlock->rw_lock_cnt == -1);
00306 #ifdef DEBUG
00307        PR_ASSERT(me != NULL);
00308        rwlock->rw_owner = me;
00309 #endif
00310        PR_Unlock(rwlock->rw_lock);
00311 #endif
00312 
00313 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
00314        /*
00315         * update thread's lock rank
00316         */
00317        _PR_SET_THREAD_RWLOCK_RANK(rwlock);
00318 #endif
00319 }
00320 
00321 /*
00322 ** Unlock the RW lock.
00323 */
00324 PR_IMPLEMENT(void)
00325 PR_RWLock_Unlock(PRRWLock *rwlock)
00326 {
00327 #if defined(DEBUG)
00328 PRThread *me = PR_GetCurrentThread();
00329 #endif
00330 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00331 int err;
00332 #endif
00333 
00334 #if defined(HAVE_UNIX98_RWLOCK) || defined(HAVE_UI_RWLOCK)
00335        err = RWLOCK_UNLOCK(&rwlock->rw_lock);
00336        PR_ASSERT(err == 0);
00337 #else
00338        PR_Lock(rwlock->rw_lock);
00339        /*
00340         * lock must be read or write-locked
00341         */
00342        PR_ASSERT(rwlock->rw_lock_cnt != 0);
00343        if (rwlock->rw_lock_cnt > 0) {
00344 
00345               /*
00346                * decrement read-lock count
00347                */
00348               rwlock->rw_lock_cnt--;
00349               if (rwlock->rw_lock_cnt == 0) {
00350                      /*
00351                       * lock is not read-locked anymore; wakeup a waiting writer
00352                       */
00353                      if (rwlock->rw_writer_cnt > 0)
00354                             PR_NotifyCondVar(rwlock->rw_writer_waitq);
00355               }
00356        } else {
00357               PR_ASSERT(rwlock->rw_lock_cnt == -1);
00358 
00359               rwlock->rw_lock_cnt = 0;
00360 #ifdef DEBUG
00361        PR_ASSERT(rwlock->rw_owner == me);
00362        rwlock->rw_owner = NULL;
00363 #endif
00364               /*
00365                * wakeup a writer, if present; preference for writers
00366                */
00367               if (rwlock->rw_writer_cnt > 0)
00368                      PR_NotifyCondVar(rwlock->rw_writer_waitq);
00369               /*
00370                * else, wakeup all readers, if any
00371                */
00372               else if (rwlock->rw_reader_cnt > 0)
00373                      PR_NotifyAllCondVar(rwlock->rw_reader_waitq);
00374        }
00375        PR_Unlock(rwlock->rw_lock);
00376 #endif
00377 
00378 #ifdef _PR_RWLOCK_RANK_ORDER_DEBUG
00379        /*
00380         * update thread's lock rank
00381         */
00382        _PR_UNSET_THREAD_RWLOCK_RANK(rwlock);
00383 #endif
00384        return;
00385 }
00386 
00387 #ifndef _PR_RWLOCK_RANK_ORDER_DEBUG
00388 
00389 void _PR_InitRWLocks(void) { }
00390 
00391 #else
00392 
00393 void _PR_InitRWLocks(void)
00394 {
00395        /*
00396         * allocated thread-private-data index for rwlock list
00397         */
00398        if (PR_NewThreadPrivateIndex(&pr_thread_rwlock_key,
00399                      _PR_RELEASE_LOCK_STACK) == PR_FAILURE) {
00400               pr_thread_rwlock_alloc_failed = 1;
00401               return;
00402        }
00403 }
00404 
00405 /*
00406  * _PR_SET_THREAD_RWLOCK_RANK
00407  *            Set a thread's lock rank, which is the highest of the ranks of all
00408  *            the locks held by the thread. Pointers to the locks are added to a
00409  *            per-thread list, which is anchored off a thread-private data key.
00410  */
00411 
00412 static void
00413 _PR_SET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
00414 {
00415 thread_rwlock_stack *lock_stack;
00416 PRStatus rv;
00417 
00418        /*
00419         * allocate a lock stack
00420         */
00421        if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL) {
00422               lock_stack = (thread_rwlock_stack *)
00423                                           PR_CALLOC(1 * sizeof(thread_rwlock_stack));
00424               if (lock_stack) {
00425                      rv = PR_SetThreadPrivate(pr_thread_rwlock_key, lock_stack);
00426                      if (rv == PR_FAILURE) {
00427                             PR_DELETE(lock_stack);
00428                             pr_thread_rwlock_alloc_failed = 1;
00429                             return;
00430                      }
00431               } else {
00432                      pr_thread_rwlock_alloc_failed = 1;
00433                      return;
00434               }
00435        }
00436        /*
00437         * add rwlock to lock stack, if limit is not exceeded
00438         */
00439        if (lock_stack) {
00440               if (lock_stack->trs_index < _PR_RWLOCK_RANK_ORDER_LIMIT)
00441                      lock_stack->trs_stack[lock_stack->trs_index++] = rwlock;       
00442        }
00443 }
00444 
00445 static void
00446 _PR_RELEASE_LOCK_STACK(void *lock_stack)
00447 {
00448        PR_ASSERT(lock_stack);
00449        PR_DELETE(lock_stack);
00450 }
00451 
00452 /*
00453  * _PR_GET_THREAD_RWLOCK_RANK
00454  *
00455  *            return thread's lock rank. If thread-private-data for the lock
00456  *            stack is not allocated, return PR_RWLOCK_RANK_NONE.
00457  */
00458        
00459 static PRUint32
00460 _PR_GET_THREAD_RWLOCK_RANK(void)
00461 {
00462        thread_rwlock_stack *lock_stack;
00463 
00464        if ((lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key)) == NULL)
00465               return (PR_RWLOCK_RANK_NONE);
00466        else
00467               return(lock_stack->trs_stack[lock_stack->trs_index - 1]->rw_rank);
00468 }
00469 
00470 /*
00471  * _PR_UNSET_THREAD_RWLOCK_RANK
00472  *
00473  *            remove the rwlock from the lock stack. Since locks may not be
00474  *            unlocked in a FIFO order, the entire lock stack is searched.
00475  */
00476        
00477 static void
00478 _PR_UNSET_THREAD_RWLOCK_RANK(PRRWLock *rwlock)
00479 {
00480        thread_rwlock_stack *lock_stack;
00481        int new_index = 0, index, done = 0;
00482 
00483        lock_stack = PR_GetThreadPrivate(pr_thread_rwlock_key);
00484 
00485        PR_ASSERT(lock_stack != NULL);
00486 
00487        index = lock_stack->trs_index - 1;
00488        while (index-- >= 0) {
00489               if ((lock_stack->trs_stack[index] == rwlock) && !done)  {
00490                      /*
00491                       * reset the slot for rwlock
00492                       */
00493                      lock_stack->trs_stack[index] = NULL;
00494                      done = 1;
00495               }
00496               /*
00497                * search for the lowest-numbered empty slot, above which there are
00498                * no non-empty slots
00499                */
00500               if ((lock_stack->trs_stack[index] != NULL) && !new_index)
00501                      new_index = index + 1;
00502               if (done && new_index)
00503                      break;
00504        }
00505        /*
00506         * set top of stack to highest numbered empty slot
00507         */
00508        lock_stack->trs_index = new_index;
00509 
00510 }
00511 
00512 #endif        /* _PR_RWLOCK_RANK_ORDER_DEBUG */