/** {file:
	  {name: LmiAtomicIntegerInline.h}
	  {description: Inline functions for LmiAtomicInteger.}
	  {copyright:
	  	(c) 2006-2015 Vidyo, Inc.,
	  	433 Hackensack Avenue,
	  	Hackensack, NJ  07601.
	  
	  	All rights reserved.
	  
	  	The information contained herein is proprietary to Vidyo, Inc.
	  	and shall not be reproduced, copied (in whole or in part), adapted,
	  	modified, disseminated, transmitted, transcribed, stored in a retrieval
	  	system, or translated into any language in any form by any means
	  	without the express written consent of Vidyo, Inc.
	  	                  ***** CONFIDENTIAL *****
	  }
	}
*/

#if LMI_ATOMIC_INTEGER_USE_STDATOMIC_

/** {implementation: {name: LmiAtomicIntegerConstruct}} */
LMI_INLINE LmiAtomicInteger* LmiAtomicIntegerConstruct(LmiAtomicInteger* a)
{
	atomic_init(a, 0);
	return a;
}

/** {implementation: {name: LmiAtomicIntegerConstructWithValue}} */
LMI_INLINE LmiAtomicInteger*
LmiAtomicIntegerConstructWithValue(LmiAtomicInteger* a, LmiInt val)
{
	atomic_init(a, val);
	return a;
}

/** {implementation: {name: LmiAtomicIntegerGetValue}} */
LMI_INLINE LmiInt LmiAtomicIntegerGetValue(const LmiAtomicInteger* a)
{
	return atomic_load_explicit(a, memory_order_relaxed);
}

/** {implementation: {name: LmiAtomicIntegerDestruct}} */
LMI_INLINE void LmiAtomicIntegerDestruct(LmiAtomicInteger* a)
{
	/* Nothing needs to be done */
}

/** {implementation: {name: LmiAtomicIntegerCompareAndExchange}} */
LMI_INLINE LmiBool LmiAtomicIntegerCompareAndExchange(LmiAtomicInteger* a,
	LmiInt oldVal, LmiInt newVal)
{
	/* Is memory_order_relaxed good enough? According to the Apple deprecation notice, the functions on macOS/iOS that
	 motivated the addition of <stdatomic.h> only supported the equivalent of memory_order_relaxed.  However, now
	 <stdatomic.h> will be detected and preferred on all platforms where previously, for example, GCC Atomic Builtins
	 were preferred.  GCC Atomic Builtins seem to provide more strict memory order (memory_order_seq_cst???).
	 The question is what memory order assumptions does Vidyo SDK code make regarding LmiAtomicInteger? Changing the
	 memory order to memory_order_seq_cst would eliminate this concern, but may negatively impact performance.
	 */
	return atomic_compare_exchange_strong_explicit(a, &oldVal, newVal, memory_order_relaxed, memory_order_relaxed);
}

/** {implementation: {name: LmiAtomicIntegerIncrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerIncrement(LmiAtomicInteger* a)
{
	/* Is memory_order_relaxed good enough? According to the Apple deprecation notice, the functions on macOS/iOS that
	 motivated the addition of <stdatomic.h> only supported the equivalent of memory_order_relaxed.  However, now
	 <stdatomic.h> will be detected and preferred on all platforms where previously, for example, GCC Atomic Builtins
	 were preferred.  GCC Atomic Builtins seem to provide more strict memory order (memory_order_seq_cst???).
	 The question is what memory order assumptions does Vidyo SDK code make regarding LmiAtomicInteger? Changing the
	 memory order to memory_order_seq_cst would eliminate this concern, but may negatively impact performance.
	 */
	return atomic_fetch_add_explicit(a, 1, memory_order_relaxed) + 1;
}

/** {implementation: {name: LmiAtomicIntegerDecrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerDecrement(LmiAtomicInteger* a)
{
	/* Is memory_order_relaxed good enough? According to the Apple deprecation notice, the functions on macOS/iOS that
	 motivated the addition of <stdatomic.h> only supported the equivalent of memory_order_relaxed.  However, now
	 <stdatomic.h> will be detected and preferred on all platforms where previously, for example, GCC Atomic Builtins
	 were preferred.  GCC Atomic Builtins seem to provide more strict memory order (memory_order_seq_cst???).
	 The question is what memory order assumptions does Vidyo SDK code make regarding LmiAtomicInteger? Changing the
	 memory order to memory_order_seq_cst would eliminate this concern, but may negatively impact performance.
	 */
	return atomic_fetch_sub_explicit(a, 1, memory_order_relaxed) - 1;
}

#elif LMI_ATOMIC_INTEGER_USE_GCC_ATOMIC_BUILTINS_

/** {implementation: {name: LmiAtomicIntegerConstruct}} */
LMI_INLINE LmiAtomicInteger* LmiAtomicIntegerConstruct(LmiAtomicInteger* a)
{
	*a = 0;
	return a;
}

/** {implementation: {name: LmiAtomicIntegerConstructWithValue}} */
LMI_INLINE LmiAtomicInteger*
LmiAtomicIntegerConstructWithValue(LmiAtomicInteger* a, LmiInt val)
{
	*a = val;
	return a;
}

/** {implementation: {name: LmiAtomicIntegerGetValue}} */
LMI_INLINE LmiInt LmiAtomicIntegerGetValue(const LmiAtomicInteger* a)
{
	return *a;
}

/** {implementation: {name: LmiAtomicIntegerDestruct}} */
LMI_INLINE void LmiAtomicIntegerDestruct(LmiAtomicInteger* a)
{
	/* Nothing needs to be done */
}

/** {implementation: {name: LmiAtomicIntegerCompareAndExchange}} */
LMI_INLINE LmiBool LmiAtomicIntegerCompareAndExchange(LmiAtomicInteger* a,
	LmiInt oldVal, LmiInt newVal)
{
	return __sync_bool_compare_and_swap(a, oldVal, newVal);
}

/** {implementation: {name: LmiAtomicIntegerIncrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerIncrement(LmiAtomicInteger* a)
{
	return __sync_add_and_fetch(a, 1);
}

/** {implementation: {name: LmiAtomicIntegerDecrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerDecrement(LmiAtomicInteger* a)
{
	return __sync_sub_and_fetch(a, 1);
}

#elif LMI_ATOMIC_INTEGER_USE_GCC_X86_ASM_

/** {implementation: {name: LmiAtomicIntegerConstruct}} */
LMI_INLINE LmiAtomicInteger* LmiAtomicIntegerConstruct(LmiAtomicInteger* a)
{
	*a = 0;
	return a;
}

/** {implementation: {name: LmiAtomicIntegerConstructWithValue}} */
LMI_INLINE LmiAtomicInteger*
LmiAtomicIntegerConstructWithValue(LmiAtomicInteger* a, LmiInt val)
{
	*a = (LmiInt32)val;
	return a;
}

/** {implementation: {name: LmiAtomicIntegerGetValue}} */
LMI_INLINE LmiInt LmiAtomicIntegerGetValue(const LmiAtomicInteger* a)
{
	return (LmiInt)*a;
}

/** {implementation: {name: LmiAtomicIntegerDestruct}} */
LMI_INLINE void LmiAtomicIntegerDestruct(LmiAtomicInteger* a)
{
	/* Nothing needs to be done */
}

/** {implementation: {name: LmiAtomicIntegerCompareAndExchange}} */
LMI_INLINE LmiBool LmiAtomicIntegerCompareAndExchange(LmiAtomicInteger* a,
	LmiInt oldVal, LmiInt newVal)
{
	LmiUint8 ret;
    __asm__ __volatile__(
		"lock; cmpxchgl %1, %3; sete %0"
		:"=r" (ret)
		:"r" (newVal), "a" (oldVal), "m" (*a)
		:"memory");
	return (LmiBool)(ret);
}

/** {implementation: {name: LmiAtomicIntegerIncrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerIncrement(LmiAtomicInteger* a)
{
	LmiInt32 tmp = 1;
	LmiInt32 result;
    __asm__ __volatile__(
		"lock; xaddl %0, %2"
		:"=r" (result)
		:"0" (tmp), "m" (*a)
		:"memory");
	return (LmiInt)(result+1);
}

/** {implementation: {name: LmiAtomicIntegerDecrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerDecrement(LmiAtomicInteger* a)
{
	LmiInt32 tmp = -1;
	LmiInt32 result;
    __asm__ __volatile__(
		"lock; xaddl %0, %2"
		:"=r" (result)
		:"0" (tmp), "m" (*a)
		:"memory");
	return (LmiInt)(result-1);
}

#elif LMI_ATOMIC_INTEGER_USE_WIN32_

/** {implementation: {name: LmiAtomicIntegerConstruct}} */
LMI_INLINE LmiAtomicInteger* LmiAtomicIntegerConstruct(LmiAtomicInteger* a)
{
	*a = 0;
	return a;
}

/** {implementation: {name: LmiAtomicIntegerConstructWithValue}} */
LMI_INLINE LmiAtomicInteger*
LmiAtomicIntegerConstructWithValue(LmiAtomicInteger* a, LmiInt val)
{
	*a = (LONG)val;
	return a;
}

/** {implementation: {name: LmiAtomicIntegerGetValue}} */
LMI_INLINE LmiInt LmiAtomicIntegerGetValue(const LmiAtomicInteger* a)
{
	return (LmiInt)*a;
}

/** {implementation: {name: LmiAtomicIntegerDestruct}} */
LMI_INLINE void LmiAtomicIntegerDestruct(LmiAtomicInteger* a)
{
	/* Nothing needs to be done */
}

/** {implementation: {name: LmiAtomicIntegerCompareAndExchange}} */
LMI_INLINE LmiBool LmiAtomicIntegerCompareAndExchange(LmiAtomicInteger* a,
	LmiInt oldVal, LmiInt newVal)
{
	return InterlockedCompareExchange(a, (LONG)newVal, (LONG)oldVal) == oldVal;
}

/** {implementation: {name: LmiAtomicIntegerIncrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerIncrement(LmiAtomicInteger* a)
{
	return (LmiInt)InterlockedIncrement(a);
}

/** {implementation: {name: LmiAtomicIntegerDecrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerDecrement(LmiAtomicInteger* a)
{
	return (LmiInt)InterlockedDecrement(a);
}

#elif LMI_ATOMIC_INTEGER_USE_GENERIC_

/** {implementation: {name: LmiAtomicIntegerConstruct}} */
LMI_INLINE LmiAtomicInteger* LmiAtomicIntegerConstruct(LmiAtomicInteger* a)
{
	if (LmiMutexConstruct(&a->mutex) == NULL) {
		return NULL;
	}
	a->value = 0;
	return a;
}

/** {implementation: {name: LmiAtomicIntegerConstructWithValue}} */
LMI_INLINE LmiAtomicInteger*
LmiAtomicIntegerConstructWithValue(LmiAtomicInteger* a, LmiInt val)
{
	if (LmiMutexConstruct(&a->mutex) == NULL) {
		return NULL;
	}
	a->value = val;
	return a;
}

/** {implementation: {name: LmiAtomicIntegerDestruct}} */
LMI_INLINE void LmiAtomicIntegerDestruct(LmiAtomicInteger* a)
{
	LmiMutexDestruct(&a->mutex);
}

/** {implementation: {name: LmiAtomicIntegerGetValue}} */
LMI_INLINE LmiInt LmiAtomicIntegerGetValue(const LmiAtomicInteger* a)
{
	LmiInt ret;
	
	LmiMutexLock(&a->mutex);
	ret = a->value;
	LmiMutexUnlock(&a->mutex);

	return ret;
}

/** {implementation: {name: LmiAtomicIntegerCompareAndExchange}} */
LMI_INLINE LmiBool LmiAtomicIntegerCompareAndExchange(LmiAtomicInteger* a,
	LmiInt oldVal, LmiInt newVal)
{
	LmiInt ret;
	
	LmiMutexLock(&a->mutex);

	ret = (a->value == oldVal);

	if (ret) {
		a->value = newVal;
	}

	LmiMutexUnlock(&a->mutex);

	return ret;
}

/** {implementation: {name: LmiAtomicIntegerIncrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerIncrement(LmiAtomicInteger* a)
{
	LmiInt ret;
	
	LmiMutexLock(&a->mutex);
	a->value++;
	ret = a->value;
	LmiMutexUnlock(&a->mutex);

	return ret;
}

/** {implementation: {name: LmiAtomicIntegerDecrement}} */
LMI_INLINE LmiInt LmiAtomicIntegerDecrement(LmiAtomicInteger* a)
{
	LmiInt ret;
	
	LmiMutexLock(&a->mutex);
	a->value--;
	ret = a->value;
	LmiMutexUnlock(&a->mutex);

	return ret;
}

#endif /* LMI_ATOMIC_INTEGER_USE_GENERIC_ */

LMI_INLINE LmiInt LmiAtomicIntegerIncrementConditional(LmiAtomicInteger* a)
{
	LmiInt val, newVal;

	do {
		val = LmiAtomicIntegerGetValue(a);
		if (val <= 0) {
			return 0;
		}
		newVal = val + 1;
	} while (!LmiAtomicIntegerCompareAndExchange(a, val, newVal));

	return newVal;
}
