/****************************************************************
 *								*
 * Copyright (c) 2001-2020 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 "gtm_time.h"
#include "gtm_inet.h"

#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "jnl.h"
#include "repl_msg.h"
#include "gtmsource.h"
#include "op.h"
#include "jnl_write.h"
#include "wcs_timer_start.h"
#include "tp_change_reg.h"
#include "jnl_get_checksum.h"
#include "iosp.h"

GBLREF  jnl_fence_control       jnl_fence_ctl;
GBLREF  uint4			dollar_tlevel;
GBLREF	seq_num			seq_num_zero;
GBLREF 	jnl_gbls_t		jgbl;
GBLREF	gd_region		*gv_cur_region;
GBLREF	sgmnt_addrs		*cs_addrs;
GBLREF	jnlpool_addrs_ptr_t	jnlpool;

error_def(ERR_REPLOFFJNLON);
error_def(ERR_TRANSMINUS);
error_def(ERR_TRANSNOSTART);

void    op_ztcommit(int4 n)
{
	boolean_t			replication, yes_jnl_no_repl;
	uint4				jnl_status;
        sgmnt_addrs			*csa, *csa_next, *new_fence_list, *tcsa, **tcsa_insert;
	gd_region			*save_gv_cur_region;
	jnlpool_addrs_ptr_t		save_jnlpool;
	jnl_private_control		*jpc;
	sgmnt_data_ptr_t		csd;
	jnl_buffer_ptr_t		jbp;
	jnl_tm_t			save_gbl_jrec_time;
	int4				prev_index;
        static struct_jrec_ztcom	ztcom_record = {{JRT_ZTCOM, ZTCOM_RECLEN, 0, 0, 0, 0},
						0, 0, 0, 0, {ZTCOM_RECLEN, JNL_REC_SUFFIX_CODE}};

	assert(ZTCOM_RECLEN == ztcom_record.suffix.backptr);
        if (n < 0)
                rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_TRANSMINUS);
        if (jnl_fence_ctl.level == 0  ||  n > jnl_fence_ctl.level)
                rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_TRANSNOSTART);
        assert(jnl_fence_ctl.level > 0);
        assert(!dollar_tlevel);

        if (n == 0)
                jnl_fence_ctl.level = 0;
        else
                jnl_fence_ctl.level -= n;
        if (0 != jnl_fence_ctl.level)
		return;
	if (!jgbl.forw_phase_recovery)
	{
		SET_GBL_JREC_TIME;
		ztcom_record.participants = 0;
		for (csa = jnl_fence_ctl.fence_list; JNL_FENCE_LIST_END != csa; csa = csa->next_fenced)
			ztcom_record.participants++;
	} else
		ztcom_record.participants = jgbl.mur_jrec_participants;
	/* If GT.M, token was computed in the first call to jnl_write_ztp_logical after op_ztstart.
	 * If journal recovery, token was set in mur_output_record.
	 */
	QWASSIGN(ztcom_record.token, jnl_fence_ctl.token);
	replication = yes_jnl_no_repl = FALSE;
	new_fence_list = JNL_FENCE_LIST_END;
	/* Sort journaled regions based on ftok order and grab crit. Do this BEFORE grabbing jnlpool lock to avoid deadlock */
	for (csa = jnl_fence_ctl.fence_list; JNL_FENCE_LIST_END != csa; csa = csa_next)
	{
		if (REPL_ALLOWED(csa))
		{
			assert(JNL_ENABLED(csa) || REPL_WAS_ENABLED(csa));
			replication = TRUE;
		} else if (JNL_ENABLED(csa))
		{
			yes_jnl_no_repl = TRUE;
			save_gv_cur_region = csa->region;
		}
		csa_next = csa->next_fenced;
		tcsa_insert = &new_fence_list;
		for (tcsa = *tcsa_insert; JNL_FENCE_LIST_END != tcsa; tcsa_insert = &tcsa->next_fenced, tcsa = *tcsa_insert)
		{
			assert(csa->fid_index != tcsa->fid_index);
			if (csa->fid_index < tcsa->fid_index)
				break;
		}
		csa->next_fenced = tcsa;
		*tcsa_insert = csa;
	}
	save_gv_cur_region = gv_cur_region; /* we change gv_cur_region in the loop below, so save for later restore */
	save_jnlpool = jnlpool;
	DEBUG_ONLY(prev_index = 0;)
	jnl_fence_ctl.fence_list = new_fence_list;
	/* Note that only those regions that are actively journaling will appear in the following list: */
	for (csa = new_fence_list; JNL_FENCE_LIST_END != csa; csa = csa->next_fenced)
	{
		assert(prev_index < csa->fid_index);
		DEBUG_ONLY(prev_index = csa->fid_index;)
		jpc = csa->jnl;
		gv_cur_region = jpc->region;	/* needed for jnl_ensure_open */
		tp_change_reg();		/* needed for jnl_ensure_open */
		assert(csa == cs_addrs);
		csd = csa->hdr;
		if (!csa->hold_onto_crit)
			grab_crit(gv_cur_region, NOT_APPLICABLE);
		assert(csa->now_crit);
		if (JNL_ENABLED(csd))
		{
			jbp = jpc->jnl_buff;
			/* Before writing to jnlfile, adjust jgbl.gbl_jrec_time if needed to maintain time order of jnl
			 * records. This needs to be done BEFORE the jnl_ensure_open as that could write journal records
			 * (if it decides to switch to a new journal file)
			 */
			ADJUST_GBL_JREC_TIME(jgbl, jbp);
			jnl_status = jnl_ensure_open(gv_cur_region, csa);
			if (jnl_status)
			{
				if (SS_NORMAL != jpc->status)
					rts_error_csa(CSA_ARG(csa) VARLSTCNT(7) jnl_status, 4,
							JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region), jpc->status);
				else
					rts_error_csa(CSA_ARG(csa) VARLSTCNT(6) jnl_status, 4,
							JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region));
			}
		}
	}
	gv_cur_region = save_gv_cur_region; /* restore original */
	tp_change_reg(); /* bring cs_* in sync with gv_cur_region */
	jnlpool = save_jnlpool;
	DEBUG_ONLY(save_gbl_jrec_time = jgbl.gbl_jrec_time;)
	if (replication) /* instance is replicated */
	{
		if (yes_jnl_no_repl) /* journal is ON but replication is OFF for a region in the replicated instance */
			rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) ERR_REPLOFFJNLON, 2, DB_LEN_STR(save_gv_cur_region));
	}
	for (csa = new_fence_list; JNL_FENCE_LIST_END != csa; csa = csa->next_fenced)
	{
		jpc = csa->jnl;
		assert(csa->now_crit);
		if (0 == jpc->pini_addr)
			jnl_write_pini(csa);
		ztcom_record.prefix.pini_addr = jpc->pini_addr;
		ztcom_record.prefix.tn = csa->ti->curr_tn;
		ztcom_record.prefix.checksum = INIT_CHECKSUM_SEED;
		ztcom_record.prefix.time = jgbl.gbl_jrec_time;
		ztcom_record.prefix.checksum = compute_checksum(INIT_CHECKSUM_SEED,
									(unsigned char *)&ztcom_record, SIZEOF(struct_jrec_ztcom));
		JNL_WRITE_APPROPRIATE(csa, jpc, JRT_ZTCOM, (jnl_record *)&ztcom_record, NULL);
		if (!csa->hold_onto_crit)
			rel_crit(jpc->region);
	}
	/* Ensure jgbl.gbl_jrec_time did not get reset by any of the jnl writing functions. This is necessary to ensure that
	 * the same timestamp is written in the ZTCOM record for ALL regions (which is currently required by journal recovery).
	 */
	assert(save_gbl_jrec_time == jgbl.gbl_jrec_time);
	for (csa = new_fence_list; JNL_FENCE_LIST_END != csa; csa = csa_next)
	{       /* do the waits in a separate loop to prevent spreading out the transaction */
		if (!jgbl.forw_phase_recovery)
		{
			jpc = csa->jnl;
			wcs_timer_start(jpc->region, TRUE);
			jnl_wait(jpc->region);
		}
		csa_next = csa->next_fenced;
		csa->next_fenced = NULL;
	}
	jgbl.tp_ztp_jnl_upd_num = 0;
}
