/**********************************************************************
 * Copyright (c) 2005, 2007 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: lock3.c,v 1.2 2007/04/06 00:06:45 jkubasta Exp $
 * 
 * Contributors: 
 * IBM - Initial API and implementation
 **********************************************************************/

#include <stddef.h>
#include "lock3.h"

BOOL ra_initializeLock(Lock_t *lock) {
	BOOL fOk;
#ifdef _WIN32

	// Initialize all data members to NULL so that we can
	// accurately check whether an error has occurred.
	lock->hEventNoReaders = NULL;

	// This critical section guards access to the other objects
	// managed by this data structure and also indicates
	// whether there are any writer threads writing.
	InitializeCriticalSection(&lock->cs);
	
	// Create the manual-reset event that is signalled when
	// no reader threads are reading.
	// Initially no reader threads are reading.
	lock->hEventNoReaders = CreateEvent(NULL,
										TRUE,
										TRUE,
										NULL);


	// Initialize the variable that indicates the number of
	// reader threads that are reading.
	lock->numReaders=0;

	if ((NULL == lock->hEventNoReaders)) {
		// If a synchronization object could not be created,
		// destroy any created objects and return failure.
		ra_deleteLock(lock);
		fOk = FALSE;
	} else {
		fOk = TRUE;
	}

	// Return TRUE upon success, FALSE upon failure.
	return(fOk);

#else

#if defined __linux__ || defined _HPUX
 pthread_mutex_init(&(lock->mutex), /* Create mutex */
					0);				/* Default behaviour (unlocked) */

 pthread_cond_init(&(lock->rcond),	/* Create read condition variable */
				   0);				/* Default behaviour */

 pthread_cond_init(&(lock->wcond),	/* Create write condition variable */
				   0);				/* Default behaviour */

 lock->waiting_writers=0;
 lock->readCount = 0;
 return TRUE;
#else
	pthread_rwlock_init(lock, NULL);
	return TRUE;
#endif

#endif
}



void ra_deleteLock(Lock_t *lock) {
#ifdef _WIN32
	if (NULL != lock->hEventNoReaders)
		CloseHandle(lock->hEventNoReaders);
#elif defined __linux__ || defined _HPUX
  /* do nothing */
#else
	pthread_rwlock_destroy(lock);
#endif
}



int ra_getWriteLock(Lock_t *lock) {
#ifdef _WIN32
	WaitForSingleObject(lock->hEventNoReaders, INFINITE);
	EnterCriticalSection(&lock->cs);
	return 0;
#elif defined __linux__ || defined _HPUX
	/*pthread_cleanup_push(rwlock_WaitingWriterCleanup, lock); */
	pthread_mutex_lock(&(lock->mutex));
	lock->waiting_writers++;
	while(lock->readCount || lock->active_writers ) {
		pthread_cond_wait(&(lock->wcond), &(lock->mutex));
	}
	lock->active_writers++;
	lock->waiting_writers--;
	/* pthread_cleanup_pop(1); */
	pthread_mutex_unlock(&(lock->mutex));
#else
	pthread_rwlock_wrlock(lock);
	return 0;
#endif
}



void ra_releaseWriteLock(Lock_t* lock) {
#ifdef _WIN32
	LeaveCriticalSection(&lock->cs);
#elif defined __linux__ || defined _HPUX
	pthread_mutex_lock(&(lock->mutex));
	if(!--lock->active_writers) {
		pthread_cond_broadcast(&lock->rcond);
	}
	else {
		pthread_cond_signal(&lock->wcond);
	}
	pthread_mutex_unlock(&(lock->mutex));
#else
	pthread_rwlock_unlock(lock);
#endif
}



int ra_getReadLock(Lock_t *lock) {
#ifdef _WIN32
	EnterCriticalSection(&lock->cs);
	if (lock->numReaders++==1) {
		// If this is the first reader thread,
		// set our event to reflect this.
		ResetEvent(lock->hEventNoReaders);
	}
	LeaveCriticalSection(&lock->cs);
	return 0;
#elif defined __linux__ || defined _HPUX
	/*pthread_cleanup_push(rwlock_WaitingReaderCleanup, lock); */
	pthread_mutex_lock(&(lock->mutex));
	if(lock->waiting_writers || lock->active_writers) {
		pthread_cond_wait(&(lock->rcond), &(lock->mutex));
	}
	lock->readCount++;
	/* pthread_cleanup_pop(1); */
	pthread_mutex_unlock(&(lock->mutex));
	return 0;
#else
	pthread_rwlock_rdlock(lock);
	return 0;
#endif
}




void ra_releaseReadLock (Lock_t *lock) {
#ifdef _WIN32

	EnterCriticalSection(&lock->cs);
	if (!--lock->numReaders) {
		SetEvent(lock->hEventNoReaders);
	}
	LeaveCriticalSection(&lock->cs);
#elif defined __linux__ || defined _HPUX
	pthread_mutex_lock(&(lock->mutex));
	if (!--lock->readCount) {
		pthread_cond_signal(&(lock->wcond));
	}
	pthread_mutex_unlock(&(lock->mutex));
#else
	pthread_rwlock_unlock(lock);
#endif
}


#if defined __linux__ || defined _HPUX

void rwlock_WaitingReaderCleanup(void *arg)
{
    Lock_t *rwl;
    rwl = (Lock_t*)arg;
/*	printf("Cleaning up reader\n"); */
    pthread_mutex_unlock(&(rwl->mutex));
/*	printf("Reader cleaned\n"); */
}

void rwlock_WaitingWriterCleanup(void *arg)
{
    Lock_t *rwl;
    rwl = (Lock_t*)arg;
/*	printf("Cleaning up writer\n"); */
    if( (rwl->readCount>= 0 && !rwl->waiting_writers) )
		pthread_cond_broadcast(&(rwl->wcond)); /*  This only happens if we have been cancelled */
/*	printf("Writer Cleaned\n"); */
    pthread_mutex_unlock(&(rwl->mutex));
}
#endif
