/****************************************************************
 *								*
 * Copyright (c) 2001-2019 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include "gdsroot.h"
#include "gdsblk.h"
#include "gdskill.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdscc.h"
#include "buddy_list.h"		/* needed for tp.h */
#include "cdb_sc.h"
#include "jnl.h"
#include "tp.h"
#include "tp_restart.h"
#include "op.h"

GBLREF	unsigned char	t_fail_hist[CDB_MAX_TRIES];
GBLREF	unsigned int	t_tries;
GBLREF	trans_num	tstart_local_tn;	/* copy of global variable "local_tn" at op_tstart time */
GBLREF	uint4		dollar_tlevel;
error_def(ERR_TPRETRY);
error_def(ERR_TRESTMAX);

/* Sets t_fail_hist[t_tries] to cdb_sc_optrestart to indicate an explicit TP restart was requested by the user. Before doing
 * so, it checks if we are in the final retry and issues another error if the explicit restart is requested more than once.
 */
void	op_trestart_set_cdb_code(void)
{
	static	trans_num	trestart_final_retry_local_tn;
	static	uint4		trestart_final_retry_cnt;

	/* Since this function can be called from the TRESTART and ZMESSAGE (with msgid=ERR_TPRETRY) commands, it is not
	 * necessary we are in TP at this point. If so, tp_restart will signal the appropriate error so skip the final
	 * retry TP check if we are not in TP. This is necessary because the use of tstart_local_tn assumes we are in TP.
	 */
	if (dollar_tlevel && (CDB_STAGNATE == t_tries))
	{	/* If we are in the final retry and holding crit (if this TP transaction has opened at least one database
		 * at this point), we want to limit the # of times the user can restart this transaction as a TP restart
		 * entails wasted work all while holding crit on the db and preventing others from accessing the same.
		 */
		if (trestart_final_retry_local_tn != tstart_local_tn)
		{
			trestart_final_retry_cnt = 0;	/* Restart counting */
			trestart_final_retry_local_tn = tstart_local_tn;
		}
		trestart_final_retry_cnt++;
		assert(MAX_TP_FINAL_RETRY_TRESTART_CNT >= trestart_final_retry_cnt);
		if (MAX_TP_FINAL_RETRY_TRESTART_CNT <= trestart_final_retry_cnt)
		{	/* Currently TRESTMAX message text is framed based on the assumption that we do
			 * not allow more than one TRESTART in the final retry. If this ever changes (by
			 * changing the macro MAX_TP_FINAL_RETRY_TRESTART_CNT), we need to change the
			 * message text. The assert below is a reminder for this case.
			 */
			assert(1 == (MAX_TP_FINAL_RETRY_TRESTART_CNT - 1));
			rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_TRESTMAX);
		}
	}
	t_fail_hist[t_tries] = cdb_sc_optrestart;
}

void	op_trestart(int newlevel)
{
	op_trestart_set_cdb_code();
	assert(1 == newlevel);	/* newlevel probably needs to become GBLREF assigned here and reset to 1 in tp_restart */
	INVOKE_RESTART;
}
