/******************************************************
The interface to the operating system
synchronization primitives.

(c) 1995 Innobase Oy

Created 9/6/1995 Heikki Tuuri
*******************************************************/

#include "os0sync.h"
#ifdef UNIV_NONINL
#include "os0sync.ic"
#endif

#ifdef __WIN__
#include <windows.h>
#endif

#include "ut0mem.h"

/* Type definition for an operating system mutex struct */
struct os_mutex_struct{ 
	void*		handle;	/* OS handle to mutex */
	ulint		count;	/* we use this counter to check
				that the same thread does not
				recursively lock the mutex: we
				do not assume that the OS mutex
				supports recursive locking, though
				NT seems to do that */				
};

/*************************************************************
Creates an event semaphore, i.e., a semaphore which may
just have two states: signaled and nonsignaled.
The created event is manual reset: it must be reset
explicitly by calling sync_os_reset_event. */

os_event_t
os_event_create(
/*============*/
			/* out: the event handle */
	char*	name)	/* in: the name of the event, if NULL
			the event is created without a name */
{
#ifdef __WIN__
	HANDLE	event;
	
	event = CreateEvent(NULL,	/* No security attributes */
			TRUE,		/* Manual reset */
			FALSE,		/* Initial state nonsignaled */
			name);
	if (!event) {
	        fprintf(stderr,
"InnoDB: Could not create a Windows event semaphore; Windows error %lu\n",
		  (ulint)GetLastError());
	}

	ut_a(event);

	return(event);
#else
	os_event_t	event;

	UT_NOT_USED(name);

	event = ut_malloc(sizeof(struct os_event_struct));

	os_fast_mutex_init(&(event->os_mutex));

#if defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10)
	pthread_cond_init(&(event->cond_var), pthread_condattr_default);
#else
	pthread_cond_init(&(event->cond_var), NULL);
#endif
	event->is_set = FALSE;

	return(event);
#endif
}

/*************************************************************
Creates an auto-reset event semaphore, i.e., an event
which is automatically reset when a single thread is
released. */

os_event_t
os_event_create_auto(
/*=================*/
			/* out: the event handle */
	char*	name)	/* in: the name of the event, if NULL
			the event is created without a name */
{
#ifdef __WIN__
	HANDLE	event;

	event = CreateEvent(NULL,	/* No security attributes */
			FALSE,		/* Auto-reset */
			FALSE,		/* Initial state nonsignaled */
			name);
	ut_a(event);

	return(event);
#else
	/* Does nothing in Posix because we do not need this with MySQL  */

	UT_NOT_USED(name);

	return(NULL);
#endif
}

/**************************************************************
Sets an event semaphore to the signaled state: lets waiting threads
proceed. */

void
os_event_set(
/*=========*/
	os_event_t	event)	/* in: event to set */
{
#ifdef __WIN__	
	ut_a(event);
	ut_a(SetEvent(event));
#else
	ut_a(event);

	os_fast_mutex_lock(&(event->os_mutex));

	if (event->is_set) {
		/* Do nothing */
	} else {
		event->is_set = TRUE;
		pthread_cond_broadcast(&(event->cond_var));
	}

	os_fast_mutex_unlock(&(event->os_mutex));
#endif		
}

/**************************************************************
Resets an event semaphore to the nonsignaled state. Waiting threads will
stop to wait for the event. */

void
os_event_reset(
/*===========*/
	os_event_t	event)	/* in: event to reset */
{
#ifdef __WIN__
	ut_a(event);

	ut_a(ResetEvent(event));
#else
	ut_a(event);

	os_fast_mutex_lock(&(event->os_mutex));

	if (!event->is_set) {
		/* Do nothing */
	} else {
		event->is_set = FALSE;
	}

	os_fast_mutex_unlock(&(event->os_mutex));
#endif
}

/**************************************************************
Frees an event object. */

void
os_event_free(
/*==========*/
	os_event_t	event)	/* in: event to free */
	
{
#ifdef __WIN__
	ut_a(event);

	ut_a(CloseHandle(event));
#else
	ut_a(event);

	os_fast_mutex_free(&(event->os_mutex));
	pthread_cond_destroy(&(event->cond_var));

	ut_free(event);
#endif
}

/**************************************************************
Waits for an event object until it is in the signaled state. */

void
os_event_wait(
/*==========*/
	os_event_t	event)	/* in: event to wait */
{
#ifdef __WIN__
	DWORD	err;

	ut_a(event);

	/* Specify an infinite time limit for waiting */
	err = WaitForSingleObject(event, INFINITE);

	ut_a(err == WAIT_OBJECT_0);
#else
	os_fast_mutex_lock(&(event->os_mutex));
loop:
	if (event->is_set == TRUE) {
		os_fast_mutex_unlock(&(event->os_mutex));

		/* Ok, we may return */

		return;
	}

	pthread_cond_wait(&(event->cond_var), &(event->os_mutex));

	/* Solaris manual said that spurious wakeups may occur: we have
	to check the 'is_set' variable again */

	goto loop;
#endif
}

/**************************************************************
Waits for an event object until it is in the signaled state or
a timeout is exceeded. */

ulint
os_event_wait_time(
/*===============*/
				/* out: 0 if success, OS_SYNC_TIME_EXCEEDED if
				timeout was exceeded */
	os_event_t	event,	/* in: event to wait */
	ulint		time)	/* in: timeout in microseconds, or
				OS_SYNC_INFINITE_TIME */
{
#ifdef __WIN__
	DWORD	err;

	ut_a(event);

	if (time != OS_SYNC_INFINITE_TIME) {
		err = WaitForSingleObject(event, time / 1000);
	} else {
		err = WaitForSingleObject(event, INFINITE);
	}
	
	if (err == WAIT_OBJECT_0) {

		return(0);
	} else if (err == WAIT_TIMEOUT) {

		return(OS_SYNC_TIME_EXCEEDED);
	} else {
		ut_error;
		return(1000000); /* dummy value to eliminate compiler warn. */
	}
#else
	UT_NOT_USED(time);
	
	/* In Posix this is just an ordinary, infinite wait */

	os_event_wait(event);

	return(0);
#endif
}

/**************************************************************
Waits for any event in an event array. Returns if even a single
one is signaled or becomes signaled. */

ulint
os_event_wait_multiple(
/*===================*/
					/* out: index of the event
					which was signaled */
	ulint		n,		/* in: number of events in the
					array */
	os_event_t*	event_array)	/* in: pointer to an array of event
					handles */
{
#ifdef __WIN__
	DWORD	index;

	ut_a(event_array);
	ut_a(n > 0);

	index = WaitForMultipleObjects(n,
					event_array,
					FALSE,	   /* Wait for any 1 event */
					INFINITE); /* Infinite wait time
						   limit */
	ut_a(index >= WAIT_OBJECT_0);
	ut_a(index < WAIT_OBJECT_0 + n);

	return(index - WAIT_OBJECT_0);
#else
	ut_a(n == 0);
	
	/* In Posix we can only wait for a single event */

	os_event_wait(*event_array);

	return(0);
#endif
}

/*************************************************************
Creates an operating system mutex semaphore.
Because these are slow, the mutex semaphore of the database
itself (sync_mutex_t) should be used where possible. */

os_mutex_t
os_mutex_create(
/*============*/
			/* out: the mutex handle */
	char*	name)	/* in: the name of the mutex, if NULL
			the mutex is created without a name */
{
#ifdef __WIN__
	HANDLE		mutex;
	os_mutex_t	mutex_str;

	mutex = CreateMutex(NULL,	/* No security attributes */
			FALSE,		/* Initial state: no owner */
			name);
	ut_a(mutex);

	mutex_str = ut_malloc(sizeof(os_mutex_str_t));

	mutex_str->handle = mutex;
	mutex_str->count = 0;

	return(mutex_str);
#else
	os_fast_mutex_t*	os_mutex;
	os_mutex_t		mutex_str;

	UT_NOT_USED(name);
	
	os_mutex = ut_malloc(sizeof(os_fast_mutex_t));

	os_fast_mutex_init(os_mutex);

	mutex_str = ut_malloc(sizeof(os_mutex_str_t));

	mutex_str->handle = os_mutex;
	mutex_str->count = 0;

	return(mutex_str);
#endif	
}

/**************************************************************
Acquires ownership of a mutex semaphore. */

void
os_mutex_enter(
/*===========*/
	os_mutex_t	mutex)	/* in: mutex to acquire */
{
#ifdef __WIN__
	DWORD	err;

	ut_a(mutex);

	/* Specify infinite time limit for waiting */
	err = WaitForSingleObject(mutex->handle, INFINITE);

	ut_a(err == WAIT_OBJECT_0);

	(mutex->count)++;
	ut_a(mutex->count == 1);
#else
	os_fast_mutex_lock(mutex->handle);

	(mutex->count)++;

	ut_a(mutex->count == 1);
#endif
}

/**************************************************************
Releases ownership of a mutex. */

void
os_mutex_exit(
/*==========*/
	os_mutex_t	mutex)	/* in: mutex to release */
{
#ifdef __WIN__
	ut_a(mutex);

	ut_a(mutex->count == 1);

	(mutex->count)--;

	ut_a(ReleaseMutex(mutex->handle));
#else
	ut_a(mutex);

	ut_a(mutex->count == 1);

	(mutex->count)--;

	os_fast_mutex_unlock(mutex->handle);
#endif	
}

/**************************************************************
Frees a mutex object. */

void
os_mutex_free(
/*==========*/
	os_mutex_t	mutex)	/* in: mutex to free */
{
#ifdef __WIN__
	ut_a(mutex);

	ut_a(CloseHandle(mutex->handle));
	ut_free(mutex);
#else
	os_fast_mutex_free(mutex->handle);
	ut_free(mutex->handle);
	ut_free(mutex);
#endif
}

/*************************************************************
Initializes an operating system fast mutex semaphore. */

void
os_fast_mutex_init(
/*===============*/
	os_fast_mutex_t*	fast_mutex)	/* in: fast mutex */
{
#ifdef __WIN__
	ut_a(fast_mutex);
	
	InitializeCriticalSection((LPCRITICAL_SECTION) fast_mutex);
#else
#if defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10)
	pthread_mutex_init(fast_mutex, pthread_mutexattr_default);
#else
	pthread_mutex_init(fast_mutex, MY_MUTEX_INIT_FAST);
#endif
#endif
}

/**************************************************************
Acquires ownership of a fast mutex. */

void
os_fast_mutex_lock(
/*===============*/
	os_fast_mutex_t*	fast_mutex)	/* in: mutex to acquire */
{
#ifdef __WIN__
	EnterCriticalSection((LPCRITICAL_SECTION) fast_mutex);
#else
	pthread_mutex_lock(fast_mutex);
#endif
}

/**************************************************************
Releases ownership of a fast mutex. */

void
os_fast_mutex_unlock(
/*=================*/
	os_fast_mutex_t*	fast_mutex)	/* in: mutex to release */
{
#ifdef __WIN__
	LeaveCriticalSection(fast_mutex);
#else
	pthread_mutex_unlock(fast_mutex);
#endif
}

/**************************************************************
Frees a mutex object. */

void
os_fast_mutex_free(
/*===============*/
	os_fast_mutex_t*	fast_mutex)	/* in: mutex to free */
{
#ifdef __WIN__
	ut_a(fast_mutex);

	DeleteCriticalSection((LPCRITICAL_SECTION) fast_mutex);
#else
	UT_NOT_USED(fast_mutex);

#endif
}
