/****************************************************************
 *								*
 *	Copyright 2001, 2011 Fidelity Information Services, Inc	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

/* note -- this may still need cleaning up for multiprocessor implementations, may need a get_latch */

#include "lockconst.h"
#include "compswap.h"
#include "aswp.h"
#include "memcoherency.h"

/* Define memory barrier macro to use in LOCK_BUFF_FOR_READ/RELEASE_BUFF_READ_LOCK macros below. On AIX
 * and Tru64, the locking macros have no memory barrier connotation because they use load/store_locked instructions.
 * The macros defined below give us that connotation on the platforms that need it and avoid adding additional
 * memory barriers on platforms that already have them.
 */
#if defined(_AIX) || defined(__osf__)
#  define READ_LOCK_READ_MEMBARRIER SHM_READ_MEMORY_BARRIER
#  define READ_LOCK_WRITE_MEMBARRIER SHM_WRITE_MEMORY_BARRIER
#else
#  define READ_LOCK_READ_MEMBARRIER
#  define READ_LOCK_WRITE_MEMBARRIER
#endif

/* LATCH_{CLEAR,SET,CONFLICT} need to be in ascending order. wcs_verify relies on this ordering for detecting out-of-range values */
#define LATCH_CLEAR	0
#define LATCH_SET	1
#define LATCH_CONFLICT	2
#define	WRITE_LATCH_VAL(cr)		(cr)->interlock.latch	/* this can take either of the above 3 LATCH_* values */

#define INTERLOCK_INIT(X)											\
{														\
	(X)->read_in_progress = -1;										\
	SHM_WRITE_MEMORY_BARRIER;										\
	SET_LATCH((sm_int_ptr_t)&((X)->interlock.latch), LATCH_CLEAR);						\
	SET_LATCH_GLOBAL(&((X)->rip_latch), LOCK_AVAILABLE);							\
}
/* On HPPA, SET_LATCH_GLOBAL forces its available value, for others it is the same as SET_LATCH */

#define INTERLOCK_INIT_MM(X)		(SET_LATCH((sm_int_ptr_t)&((X)->interlock.latch), LATCH_CLEAR))
					  /* similar to INTERLOCK_INIT except this is for a mmblk_rec */

/* New buffer doesn't need interlocked operation.  */
#define LOCK_NEW_BUFF_FOR_UPDATE(X)	(SET_LATCH((sm_int_ptr_t)&((X)->interlock.latch), LATCH_SET))

#define LOCK_BUFF_FOR_UPDATE(X,Y,Z)     {Y = ASWP((sm_int_ptr_t)&(X)->interlock.latch, LATCH_SET, Z);		\
						 LOCK_HIST("OBTN", &(X)->interlock.latch, process_id, -1);}
#define RELEASE_BUFF_UPDATE_LOCK(X,Y,Z) {LOCK_HIST("RLSE", &(X)->interlock.latch, process_id, -1); 		\
					         Y = ASWP((sm_int_ptr_t)&(X)->interlock.latch, LATCH_CLEAR, Z);}
#define CLEAR_BUFF_UPDATE_LOCK(X,Z)     (ASWP((sm_int_ptr_t)&(X)->interlock.latch, LATCH_CLEAR, Z))

/* Only the writer uses the LATCH_CONFLICT value.  */
#define LOCK_BUFF_FOR_WRITE(X,Y,Z)      (Y = ASWP((sm_int_ptr_t)&(X)->interlock.latch, LATCH_CONFLICT, Z))

/* The macros below currently use an interlocked increment/decrement type of locking unlike the above macros which
 * use an atomic swap method.
 */
#define LOCK_BUFF_FOR_READ(X,Y)											\
{														\
	Y = INCR_CNT((sm_int_ptr_t)&(X)->read_in_progress, (sm_global_latch_ptr_t)&(X)->rip_latch);		\
	READ_LOCK_READ_MEMBARRIER;										\
}

#define RELEASE_BUFF_READ_LOCK(X)										\
{														\
	READ_LOCK_WRITE_MEMBARRIER;										\
	DECR_CNT((sm_int_ptr_t)&(X)->read_in_progress, (sm_global_latch_ptr_t)&(X)->rip_latch);			\
}

/* Used after an aswp; if the return value (second argument to one of these macros) was
 * LATCH_CONFLICT, then the writer owned the buffer when this process performed the aswp.
 * WRITER_BLOCKED_BY_PROC is used by a non-writer process.
 * WRITER_OWNS_BUFF is used by the writer.
 */
#define WRITER_BLOCKED_BY_PROC(X)	((X) == LATCH_CONFLICT)
#define WRITER_OWNS_BUFF(X)		((X) == LATCH_CONFLICT)

#define WRITER_STILL_OWNS_BUFF(X,Y)	((X)->interlock.latch != LATCH_CLEAR)

/* Used after an aswp; if the return value (second argument to one of these macros)
 * was LATCH_CLEAR, then nobody had it when this process performed the aswp so this
 * process owns the resource.
 */
#define OWN_BUFF(X)			((X) == LATCH_CLEAR)

#define ADD_ENT_TO_ACTIVE_QUE_CNT(X,Y)		(INCR_CNT((sm_int_ptr_t)(X), (sm_global_latch_ptr_t)(Y)))
#define SUB_ENT_FROM_ACTIVE_QUE_CNT(X,Y)	(DECR_CNT((sm_int_ptr_t)(X), (sm_global_latch_ptr_t)(Y)))

#define INCR_CNT(X,Y)			INTERLOCK_ADD(X,Y,1)
#define DECR_CNT(X,Y)			INTERLOCK_ADD(X,Y,-1)

#ifndef __ia64
#define GET_SWAPLOCK(X)			(COMPSWAP_LOCK((X), LOCK_AVAILABLE, 0, process_id, 0))
#else
/* Doing the simple test before COMPSWAP_LOCK can help performance when a lock is highly contended
 */
#define GET_SWAPLOCK(X)		(((X)->u.parts.latch_pid == LOCK_AVAILABLE) && COMPSWAP_LOCK((X), LOCK_AVAILABLE, 0, process_id, 0))
#endif /* __ia64 */
/* Use COMPSWAP_UNLOCK to release the lock because of the memory barrier and other-processor notification it implies. Also
 * the usage of COMPSWAP_UNLOCK allows us to check (with low cost) that we have/had the lock we are trying to release.
 * If we don't have the lock and are trying to release it, a GTMASSERT seems the logical choice as the logic is very broken
 * at that point. If this macro is used in part of an expression, the GTMASSERT path must also return a value (to keep
 * the compiler happy) thus the construct (GTMASSERT, 0) which returns a zero (see usage with assert() on UNIX).
 */
#define RELEASE_SWAPLOCK(X)		(COMPSWAP_UNLOCK((X), process_id, 0, LOCK_AVAILABLE, 0) ? 1 : (GTMASSERT, 0))

