/*******************************************************************************
 * Copyright (c) 2005, 2009 IBM, Intel Corporation.
 * 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
 *
 * Contributors:
 *    Karla Callaghan, Intel - Initial version.
 * $Id: TPTPSemaphore.c,v 1.7 2009/09/24 21:02:03 jwest Exp $
 *******************************************************************************/ 


#include <stddef.h>
#include <limits.h>
#include "tptp/TPTPUtils.h"
#include "tptp/TPTPCommon.h"

#ifndef _WIN32
#include <errno.h>
#endif

#ifdef MVS
	#include <sys/sem.h>
	#include <sys/ipc.h>
	#include <sys/stat.h>
	#include <sys/types.h>
	#include <unistd.h>
#endif

/*
#ifndef _WIN32 
	#include <semaphore.h>
	#include <time.h> 
#endif
*/
/* Semaphore utilities */

#ifdef MVS

// List of semaphores to delete on process shutdown
typedef struct {
	Semaphore_t * semaphore;
	void *  /* Semaphore_List_t */  next;
} Semaphore_List_t;

// Mutex on semaphore list
static Semaphore_t semaphoreLock;

// Instance of semaphore list
static Semaphore_List_t * semaphoreList = NULL;

// Adds an undeleted semaphore to the semaphore list
void tptp_zosRegisterUndeletedSemaphore(Semaphore_t *pSem) {
	Semaphore_List_t * curr;
	Semaphore_List_t *newList;
	
	// semaphoreLock Mutex begins
	tptp_waitSemaphore(&semaphoreLock);
	
	if(semaphoreList == NULL) {
		// Initialize the list if it doesn't exist
		
		newList = (Semaphore_List_t *) tptp_malloc(sizeof(Semaphore_List_t));
		newList->semaphore = pSem;
		newList->next = NULL;
		
		semaphoreList = newList;
		
	} else {
		// The new list does exist, so add to it
		
		curr = semaphoreList;
		
		// Set curr to the final element of the list
		while(curr->next != NULL) {
			curr = (Semaphore_List_t *) curr->next;
		}

		// Create a new element
		newList = (Semaphore_List_t *) tptp_malloc(sizeof(Semaphore_List_t));
		newList->semaphore = pSem;
		newList->next = NULL;
		
		// Append it
		curr->next = newList;
		
	}

	// semaphoreLock Mutex ends
	tptp_postSemaphore(&semaphoreLock);
	
}

// Deletes all undeleted semaphores (this is called on process termination)
void tptp_zosDeleteUndeletedSemaphores() {
	Semaphore_List_t * curr = semaphoreList;
	
	while(curr != NULL) {
		tptp_deleteSemaphore( curr->semaphore );
		curr = (Semaphore_List_t *) curr->next;
	}
	
}

// Before the undeleted semaphores list/functions are used, this must be called to initialize the mutex it uses
void tptp_zosInitializeSemaphoreLock() {
	tptp_initializeSemaphore(&semaphoreLock);
	tptp_postSemaphore(&semaphoreLock);
	tptp_zosRegisterUndeletedSemaphore(&semaphoreLock);
}


// end MVS section
#endif

/* 
 * Create a semaphore with initial count of 0 (unsignaled).
 */
int tptp_initializeSemaphore(Semaphore_t *pSem )
{

#ifdef _WIN32
	/* Create un-named semaphore, with unlimited max count,
	 * that is unsignaled (intial count = 0).
	 */
	pSem->hSem = CreateSemaphore(NULL, 0, INT_MAX, NULL);
	if(pSem->hSem == NULL)
		return -1;
#else
	/* Create semaphore with initial value 0 */
	#ifdef MVS
		pSem->sem = semget(IPC_PRIVATE, 1, IPC_CREAT | 0660 );
		if (pSem->sem == -1)
			return -1;
	#else
		int rc;
		rc = sem_init(&(pSem->sem), 0, 0);
		if (rc != 0)
			return -1;
	#endif
#endif

	return 0;
}

/*
 * Destroys resources associated with the given semaphore.
 */
int tptp_deleteSemaphore(Semaphore_t *pSem)
{

#ifdef _WIN32
	if (pSem->hSem)
		CloseHandle(pSem->hSem);
#else
	int rc;
	#ifdef MVS
		rc = semctl(pSem->sem, 0, IPC_RMID, 0);
		if (rc == -1)
			return -1;
	#else
		rc = sem_destroy(&(pSem->sem));
	/* Can fail if sem is being waited on. */
	if (rc != 0)
		return -1;
	#endif
#endif

	return 0;
}

/*
 * Increments the count of the given semaphore by 1, causing
 * any thread that is waiting on the semaphore to be released.
 */
int tptp_postSemaphore(Semaphore_t *pSem)
{

#ifdef _WIN32
	if (pSem->hSem == NULL)
		return -1;
	ReleaseSemaphore(pSem->hSem, 1, NULL);
#else
	int rc=0;
	#ifdef MVS
		struct sembuf semdata;
		semdata.sem_num=0;
		semdata.sem_op=1;
		semdata.sem_flg=0;
		rc = semop(pSem->sem, &semdata, 1);
	#else
		rc = sem_post(&(pSem->sem));
	#endif
	if (rc != 0)
		return -1;
#endif

	return 0;
}

/*
 * Releases thread when count of semaphore is >0 and
 * immediately decrements that count by 1.
 */
int tptp_waitSemaphore(Semaphore_t *pSem)
{

#ifdef _WIN32
	unsigned long rc;
	if (pSem->hSem == NULL)
		return -1;

	rc = WaitForSingleObject(pSem->hSem, INFINITE);
	if (rc != WAIT_OBJECT_0)
		return -1;

#else
	while (1) {
		int rc;
		#ifdef MVS
			struct sembuf semdata;
                	semdata.sem_num=0;
                	semdata.sem_op=-1;
                	semdata.sem_flg=0;
			rc = semop(pSem->sem, &semdata, 1);
		#else
			rc = sem_wait(&(pSem->sem));
		#endif
		if (rc == 0) break;
		
		if (errno != EINTR) return -1;
	}
#endif

	return 0;
}
/*
int tptp_waitSemaphoreTimeout(Semaphore_t *pSem, int timeoutInSecs) {
	// Note this code is a placeholder that hasn't been tested. 
	// Keep this in mind when using it. - jwest
#ifdef _WIN32
	unsigned long rc;
	if (pSem->hSem == NULL)
		return -1;

	rc = WaitForSingleObject(pSem->hSem, timeoutInSecs*1000);
	if(rc == WAIT_TIMEOUT) {
		return -2;
	} else
	if (rc != WAIT_OBJECT_0)
		return -1;

#else
	struct timespec waitTime;
	if (clock_gettime(CLOCK_REALTIME, &waitTime) == -1) {
		return -1;
	}
	waitTime.tv_sec += timeoutInSecs;
	
	while (1) {
		int rc = sem_timedwait(&(pSem->sem), &waitTime);
		if (rc == 0) break;

		if(errno == ETIMEDOUT) {
			return -2;
		}
		if (errno != EINTR) return -1;
	}
#endif

	return 0;	
}
*/

