/*****************************************************************************

Copyright (c) 1995, 2011, Oracle and/or its affiliates. All Rights Reserved.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA

*****************************************************************************/

/**************************************************//**
@file include/os0sync.ic
The interface to the operating system synchronization primitives.

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

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

/**********************************************************//**
Acquires ownership of a fast mutex.
@return	0 if success, != 0 if was reserved by another thread */
UNIV_INLINE
ulint
os_fast_mutex_trylock(
/*==================*/
	os_fast_mutex_t*	fast_mutex)	/*!< in: mutex to acquire */
{
	fast_mutex_t*	mutex = &fast_mutex->mutex;

#ifdef __WIN__
	return(!TryEnterCriticalSection(mutex));
#else
	/* NOTE that the MySQL my_pthread.h redefines pthread_mutex_trylock
	so that it returns 0 on success. In the operating system
	libraries, HP-UX-10.20 follows the old Posix 1003.4a Draft 4 and
	returns 1 on success (but MySQL remaps that to 0), while Linux,
	FreeBSD, Solaris, AIX, Tru64 Unix, HP-UX-11.0 return 0 on success. */

	return((ulint) pthread_mutex_trylock(mutex));
#endif
}

#ifdef UNIV_PFS_MUTEX
/*********************************************************//**
NOTE! Please use the corresponding macro os_fast_mutex_init(), not directly
this function!
A wrapper function for os_fast_mutex_init_func(). Initializes an operating
system fast mutex semaphore. */
UNIV_INLINE
void
pfs_os_fast_mutex_init(
/*===================*/
	PSI_mutex_key		key,		/*!< in: Performance Schema
						key */
	os_fast_mutex_t*	fast_mutex)	/*!< out: fast mutex */
{
#ifdef HAVE_PSI_MUTEX_INTERFACE
	fast_mutex->pfs_psi = PSI_MUTEX_CALL(init_mutex)(key, &fast_mutex->mutex);
#else
	fast_mutex->pfs_psi = NULL;
#endif

	os_fast_mutex_init_func(&fast_mutex->mutex);
}
/******************************************************************//**
NOTE! Please use the corresponding macro os_fast_mutex_free(), not directly
this function!
Wrapper function for pfs_os_fast_mutex_free(). Also destroys the performance
schema probes when freeing the mutex */
UNIV_INLINE
void
pfs_os_fast_mutex_free(
/*===================*/
	os_fast_mutex_t*	fast_mutex)  /*!< in/out: mutex */
{
#ifdef HAVE_PSI_MUTEX_INTERFACE
	if (fast_mutex->pfs_psi != NULL)
		PSI_MUTEX_CALL(destroy_mutex)(fast_mutex->pfs_psi);
#endif
	fast_mutex->pfs_psi = NULL;

	os_fast_mutex_free_func(&fast_mutex->mutex);
}
/**********************************************************//**
NOTE! Please use the corresponding macro os_fast_mutex_lock, not directly
this function!
Wrapper function of os_fast_mutex_lock_func. Acquires ownership of a fast
mutex. */
UNIV_INLINE
void
pfs_os_fast_mutex_lock(
/*===================*/
	os_fast_mutex_t*	fast_mutex,	/*!< in/out: mutex to acquire */
	const char*		file_name,	/*!< in: file name where
						 locked */
	ulint			line)		/*!< in: line where locked */
{
#ifdef HAVE_PSI_MUTEX_INTERFACE
	if (fast_mutex->pfs_psi != NULL)
	{
		PSI_mutex_locker* 	locker;
		PSI_mutex_locker_state	state;

		locker = PSI_MUTEX_CALL(start_mutex_wait)(
			&state, fast_mutex->pfs_psi,
			PSI_MUTEX_LOCK, file_name,
			static_cast<uint>(line));

		os_fast_mutex_lock_func(&fast_mutex->mutex);

		if (locker != NULL)
			PSI_MUTEX_CALL(end_mutex_wait)(locker, 0);
	}
	else
#endif
	{
		os_fast_mutex_lock_func(&fast_mutex->mutex);
	}

	return;
}
/**********************************************************//**
NOTE! Please use the corresponding macro os_fast_mutex_unlock, not directly
this function!
Wrapper function of os_fast_mutex_unlock_func. Releases ownership of a
fast mutex. */
UNIV_INLINE
void
pfs_os_fast_mutex_unlock(
/*=====================*/
	os_fast_mutex_t*	fast_mutex)	/*!< in/out: mutex to release */
{
#ifdef HAVE_PSI_MUTEX_INTERFACE
	if (fast_mutex->pfs_psi != NULL)
		PSI_MUTEX_CALL(unlock_mutex)(fast_mutex->pfs_psi);
#endif

	os_fast_mutex_unlock_func(&fast_mutex->mutex);
}
#endif /* UNIV_PFS_MUTEX */

#ifdef HAVE_WINDOWS_ATOMICS

/* Use inline functions to make 64 and 32 bit versions of windows atomic
functions so that typecasts are evaluated at compile time. Take advantage
that lint is either __int64 or long int and windows atomic functions work
on __int64 and LONG */

/**********************************************************//**
Atomic compare and exchange of unsigned integers.
@return value found before the exchange.
If it is not equal to old_value the exchange did not happen. */
UNIV_INLINE
lint
win_cmp_and_xchg_lint(
/*==================*/
	volatile lint*	ptr,		/*!< in/out: source/destination */
	lint		new_val,	/*!< in: exchange value */
	lint		old_val)	/*!< in: value to compare to */
{
# ifdef _WIN64
	return(InterlockedCompareExchange64(ptr, new_val, old_val));
# else
	return(InterlockedCompareExchange(ptr, new_val, old_val));
# endif
}

/**********************************************************//**
Atomic addition of signed integers.
@return Initial value of the variable pointed to by ptr */
UNIV_INLINE
lint
win_xchg_and_add(
/*=============*/
	volatile lint*	ptr,	/*!< in/out: address of destination */
	lint		val)	/*!< in: number to be added */
{
#ifdef _WIN64
	return(InterlockedExchangeAdd64(ptr, val));
#else
	return(InterlockedExchangeAdd(ptr, val));
#endif
}

/**********************************************************//**
Atomic compare and exchange of unsigned integers.
@return value found before the exchange.
If it is not equal to old_value the exchange did not happen. */
UNIV_INLINE
ulint
win_cmp_and_xchg_ulint(
/*===================*/
	volatile ulint*	ptr,		/*!< in/out: source/destination */
	ulint		new_val,	/*!< in: exchange value */
	ulint		old_val)	/*!< in: value to compare to */
{
	return((ulint) win_cmp_and_xchg_lint(
		(volatile lint*) ptr,
		(lint) new_val,
		(lint) old_val));
}

/**********************************************************//**
Atomic compare and exchange of 32-bit unsigned integers.
@return value found before the exchange.
If it is not equal to old_value the exchange did not happen. */
UNIV_INLINE
DWORD
win_cmp_and_xchg_dword(
/*===================*/
	volatile DWORD*	ptr,		/*!< in/out: source/destination */
	DWORD		new_val,	/*!< in: exchange value */
	DWORD		old_val)	/*!< in: value to compare to */
{
	ut_ad(sizeof(DWORD) == sizeof(LONG));	/* We assume this. */
	return(InterlockedCompareExchange(
		(volatile LONG*) ptr,
		(LONG) new_val,
		(LONG) old_val));
}

#endif /* HAVE_WINDOWS_ATOMICS */