/****************************************************************
 *								*
 *	Copyright 2001, 2012 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.	*
 *								*
 ****************************************************************/

/* 	insert_region.c
 *	requirement
 *		reg		initialized (gvcst_init)
 *	parameters
 *		reg		the region to be inserted
 *		reg_list	pointer to the pointer to the list head
 *		reg_free_list	pointer to the pointer to the free list
 *		size		size of the structure of each item in the list
 *	return
 *		pointer to the item in the list that is corresponding to the region.
 *		*reg_list and *reg_free_list are also updated if needed.
 *	fid_index field in csa and tp_reg_list is maintained by gvcst_init. Maintaining tp_reg_list is
 *	important, since the regions might be re-sorted in between insert_region() calls (i.e. new
 *	regions opening). All callers of insert_region except for dse_all() either use tp_reg_list or do not
 *	have the regions open.  dse_all() opens the regions before it calls insert_region(), so maintaining
 *	fid_index in tp_reg_list is sufficient.
 */

#include "mdef.h"

#ifdef	VMS
#include <rms>
#endif

#ifdef UNIX
#include "gtm_ipc.h"		/* needed for FTOK */
#endif

#include "gtm_string.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "gdscc.h"
#include "gdskill.h"
#include "filestruct.h"
#include "jnl.h"
#include "buddy_list.h"		/* needed for tp.h */
#include "hashtab_int4.h"	/* needed for tp.h */
#include "tp.h"
#include "iosp.h"
#include "dbfilop.h"
#include "gtmmsg.h"
#include "is_file_identical.h"
#include "tp_grab_crit.h"
#include "t_retry.h"
#include "wcs_mm_recover.h"
#include "gtmimagename.h"

GBLREF	gd_region	*gv_cur_region;
GBLREF	uint4		dollar_tlevel;
GBLREF	unsigned int	t_tries;

tp_region	*insert_region(	gd_region	*reg,
		   		tp_region	**reg_list,
		   		tp_region	**reg_free_list,
		   		int4		size)
{
	tp_region	*tr, *tr_last, *tr_new;
	unique_file_id	local_id;
#	ifdef 	VMS
	char		*local_id_fiptr;
	file_control	*fc;
	uint4		status;
	gd_region	*temp_reg;
#	endif
	int4		local_fid_index;
	sgmnt_addrs	*csa;
	int4		match;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	assert(SIZEOF(tp_region) <= size);
	assert(!IS_GTM_IMAGE || dollar_tlevel);
	if (reg->open)
		csa = (sgmnt_addrs *)&FILE_INFO(reg)->s_addrs;
#	if defined(VMS)
	if (!reg->open)
	{
		temp_reg = gv_cur_region;
		gv_cur_region = reg;
		local_id_fiptr = local_id.file_id;
		if (!mupfndfil(reg, NULL))
		{
			gv_cur_region = temp_reg;
			return NULL;
		}
		FILE_CNTL_INIT_IF_NULL(reg->dyn.addr);
		fc = reg->dyn.addr->file_cntl;
		fc->file_type = reg->dyn.addr->acc_meth;
		fc->op = FC_OPEN;
		status = dbfilop(fc);
		if (status & 1)
		{
			local_id_fiptr = &(FILE_INFO(reg)->file_id);
			sys$dassgn(FILE_INFO(reg)->fab->fab$l_stv);
		} else
		{
			gtm_putmsg(VARLSTCNT(1) status);
			gv_cur_region = temp_reg;
			return NULL;
		}
		gv_cur_region = temp_reg;
	} else
		local_fid_index = csa->fid_index;
#	elif defined(UNIX)
	if (!reg->open)
	{
		if (!mupfndfil(reg, NULL))
			return NULL;
		if (!filename_to_id(&local_id.uid, (char *)reg->dyn.addr->fname))
			return NULL;
	} else
		local_fid_index = csa->fid_index;
#	endif
	/* See if the region is already on the list or if we have to add it */
	for (tr = *reg_list, tr_last = NULL; NULL != tr; tr = tr->fPtr)
	{
		if (reg->open)
		{	/* gvcst_init must have sorted them and filled in the fid_index field of node_local */
			assert(tr->reg->open);
			/* note that it is possible that "reg" and "tr->reg" are different although their "fid_index" is the same.
			 * this is possible if both regions point to the same physical file.
			 * in this case we return the existing "tr" instead of creating a new one.
			 */
			if (local_fid_index == tr->file.fid_index)	/* Region is found */
			{	/* assert we are not in final retry or we are in TP and have crit on the region already */
				assert((CDB_STAGNATE > t_tries)
					|| (dollar_tlevel && reg->open && csa->now_crit));
				return tr;
			}
			if ((tr->file.fid_index > local_fid_index))
				break;				/* .. we have found our insertion point */
		} else
		{
			if (reg == tr->reg)	/* Region is found */
			{	/* assert we are not in final retry or we are in TP and have crit on the region already */
				assert((CDB_STAGNATE > t_tries)
					|| (dollar_tlevel && reg->open && csa->now_crit));
				return tr;
			}
			/* let's sort here */
			if (!tr->reg->open)
			{	/* all regions closed */
				VMS_ONLY(match = memcmp(&(tr->file.file_id), local_id_fiptr, SIZEOF(gd_id)));
				UNIX_ONLY(match = gdid_cmp(&(tr->file.file_id), &(local_id.uid)));
			} else
			{	/* the other regions are open, i.e. file is pointing to fid_index, use file_id
				 * from node_local */
				VMS_ONLY(match = memcmp(
					&(((sgmnt_addrs *)&FILE_INFO(tr->reg)->s_addrs)->nl->unique_id.file_id),
					local_id_fiptr, SIZEOF(gd_id)));
				UNIX_ONLY(match = gdid_cmp(
					&(((sgmnt_addrs *)&FILE_INFO(tr->reg)->s_addrs)->nl->unique_id.uid), &(local_id.uid)));
			}
			if (0 == match)
				return tr;
			if (0 < match)
				break;				/* .. we have found our insertion point */
		}
		tr_last = tr;
	}
	if ((NULL != reg_free_list) && (NULL != *reg_free_list))	/* Get a used block off our unused queue */
	{
		tr_new = *reg_free_list;		/* Get element */
		*reg_free_list = tr_new->fPtr;		/* Remove from queue */
	} else						/* get a new one */
	{
		tr_new = (tp_region *)malloc(size);
		if (size > SIZEOF(tp_region))
			memset(tr_new, 0, size);
	}
	tr_new->reg = reg;				/* Add this region to end of list */
	if (!reg->open)
	{
		VMS_ONLY(memcpy(&(tr_new->file.file_id), local_id_fiptr, SIZEOF(gd_id)));
		UNIX_ONLY(tr_new->file.file_id = local_id.uid;)
	} else
		tr_new->file.fid_index = local_fid_index;
	if (NULL == tr_last)
	{	/* First element on the list */
		tr_new->fPtr = *reg_list;
		*reg_list = tr_new;
	} else
	{	/* Insert into list */
		tr_new->fPtr = tr_last->fPtr;
		tr_last->fPtr = tr_new;
	}
	if ((CDB_STAGNATE <= t_tries) && dollar_tlevel && reg->open && !csa->now_crit)
	{	/* Final retry in TP and this region not locked down. Get crit on it if it is open.
		 * reg->open needs to be checked above to take care of the case where we do an insert_region() from gvcst_init()
		 * 	in the 3rd retry in TP when we have not yet opened the region. In case region is not open,
		 * 	tp_restart() (invoked through t_retry from gvcst_init) will open "reg" as well as get crit on it for us.
		 */
		DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = TRUE;)
		if (FALSE == tp_grab_crit(reg))		/* Attempt lockdown now */
		{
			DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = FALSE;)
			t_retry(cdb_sc_needcrit);	/* avoid deadlock -- restart transaction */
			assert(FALSE);			/* should not come here as t_retry() does not return */
		}
		DEBUG_ONLY(TREF(ok_to_call_wcs_recover) = FALSE;)
		assert(csa->now_crit);	/* ensure we have crit now */
		CHECK_MM_DBFILEXT_REMAP_IF_NEEDED(csa, reg);
#		ifdef UNIX
		if (MISMATCH_ROOT_CYCLES(csa, csa->nl))
		{	/* Going into this retry, we have already checked in tp_restart for moved root blocks in tp_reg_list.
			 * Since we haven't yet checked this region, we check it here and reset clues for an globals in the
			 * newly inserted region. We don't want to reset ALL gvt clues because the current retry may have made
			 * use (and valid use at that) of clues for globals in other regions.
			 */
			RESET_ALL_GVT_CLUES_REG(csa);
			csa->root_search_cycle = csa->nl->root_search_cycle;
		}
#		endif
	}
	DBG_CHECK_TP_REG_LIST_SORTING(*reg_list);
	return tr_new;
}
