/*
 * nasd_cheops_locks.c
 *
 * lock module: implements multi-reader single-writer locks
 *
 * Author: Khalil Amiri, CMU SCS/ECE, July 20 1997
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */

#include <nasd/nasd_options.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <nasd/nasd_types.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_threadstuff.h>
#include <sys/errno.h>
#include <nasd/nasd_cheops_locks.h>

int 
_nasd_cheops_lockInit( _nasd_cheops_rwlock_t *l )
{
  int rc;

  rc = nasd_mutex_init( &(l->m) );
  if (rc) {
    return(ENOMEM);
  }
  rc = nasd_cond_init ( &(l->canRead) );
  if (rc) {
    return(ENOMEM);
  }

  rc = nasd_cond_init ( &(l->canWrite) );
  if (rc) {
    return(ENOMEM);
  }
	
  l->nActive = 0;     /* number of active readers, -1 if active writer*/
  l->nPendingReaders = 0;
  l->nPendingWriters = 0;

  return 0;
}

int 
_nasd_cheops_lockShared( _nasd_cheops_rwlock_t *l )
{

  NASD_LOCK_MUTEX( l->m );

  l->nPendingReaders++;
  if (l->nPendingWriters >0)
    NASD_WAIT_COND( l->canRead, l->m );
  while ( l->nActive < 0)
    NASD_WAIT_COND ( l->canRead, l->m);
  l->nActive++;
  l->nPendingReaders--;
	
  NASD_UNLOCK_MUTEX ( l->m );
  return 0;
}

int 
_nasd_cheops_unlockShared( _nasd_cheops_rwlock_t *l)
{
  NASD_LOCK_MUTEX( l->m );

  l->nActive--;
  if ( l->nActive == 0) {			/* last reader to leave */
    NASD_UNLOCK_MUTEX( l->m );
    NASD_SIGNAL_COND ( l->canWrite);
  } else
    NASD_UNLOCK_MUTEX( l->m );

  return 0;
}

int 
_nasd_cheops_lockExclusive( _nasd_cheops_rwlock_t *l )
{
  NASD_LOCK_MUTEX( l->m );
  l->nPendingWriters++;
  while ( l->nActive )
    NASD_WAIT_COND(l->canWrite, l->m);
  l->nPendingWriters--;
  l->nActive = -1;
  NASD_UNLOCK_MUTEX( l->m );	

  return 0;
}

int 
_nasd_cheops_unlockExclusive( _nasd_cheops_rwlock_t *l )
{
  nasd_boolean_t wakeReaders;

  NASD_LOCK_MUTEX( l->m );
  l->nActive = 0;
  wakeReaders = (l->nPendingReaders != 0 );
  NASD_UNLOCK_MUTEX( l->m);
  if (wakeReaders) {
    NASD_BROADCAST_COND(l->canRead);
  }
  else {
    NASD_SIGNAL_COND(l->canWrite);
  }
  return 0;
}

int 
_nasd_cheops_downgrade( _nasd_cheops_rwlock_t *l )
{
  nasd_boolean_t wakeReaders;

  NASD_LOCK_MUTEX ( l->m );
  NASD_ASSERT(l->nActive == -1); /* to downgrade, need to be writer */
  l->nActive = 1;
  wakeReaders = (l->nPendingReaders != 0);
  NASD_UNLOCK_MUTEX( l->m);
  if ( wakeReaders)
    NASD_BROADCAST_COND(l->canRead );
  return 0;
}

int 
_nasd_cheops_upgrade( _nasd_cheops_rwlock_t *l )
{
  NASD_LOCK_MUTEX( l->m );
  if ( l->nActive == 1) {
    l->nActive == -1;
  } else {              /* there are other active readers */
    l->nPendingWriters++;
    l->nActive--;
    while (l->nActive)
      NASD_WAIT_COND(l->canWrite, l->m);
    l->nPendingWriters--;
    l->nActive= -1;
  }
  NASD_UNLOCK_MUTEX ( l->m );
  return 0;
	
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
