Logo Search packages:      
Sourcecode: virtualbox-ose version File versions

prrwlock.c

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * 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 the Initial Developer are Copyright (C) 1998-2000
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#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_DEBUG

static PRUintn    pr_thread_rwlock_key;               /* TPD key for lock stack */
static PRUintn    pr_thread_rwlock_alloc_failed;

#define     _PR_RWLOCK_RANK_ORDER_LIMIT   10

typedef 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_DEBUG

void _PR_InitRWLocks(void) { }

#else

void _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 */

Generated by  Doxygen 1.6.0   Back to index