/****************************************************************
 *								*
 *	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.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include <stddef.h>
#include <signal.h>		/* for VSIG_ATOMIC_T type */

#include "gtm_time.h"
#include "gtm_inet.h"

#ifdef VMS
#include <descrip.h> /* Required for gtmsource.h */
#endif

#include "cdb_sc.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "gdskill.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsblk.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "gdscc.h"
#include "gdsbml.h"
#include "ccp.h"
#include "error.h"
#include "iosp.h"
#include "interlock.h"
#include "jnl.h"
#include "buddy_list.h"		/* needed for tp.h */
#include "hashtab_int4.h"	/* needed for tp.h and cws_insert.h */
#include "tp.h"
#include "gdsbgtr.h"
#include "repl_msg.h"
#include "gtmsource.h"
#include "mupipbckup.h"
#include "cache.h"
#include "gt_timer.h"
#include "longset.h"		/* needed for cws_insert.h */
#include "cws_insert.h"
#include "min_max.h"
#include "gtmimagename.h"
#include "anticipatory_freeze.h"

#ifdef UNIX
#include "gtmrecv.h"
#include "deferred_signal_handler.h"
#include "repl_instance.h"
#endif

/* Include prototypes */
#include "t_qread.h"
#include "t_retry.h"
#include "t_commit_cleanup.h"
#include "send_msg.h"
#include "bm_getfree.h"
#include "rc_cpt_ops.h"
#include "rel_quant.h"
#include "wcs_flu.h"
#include "mm_update.h"
#include "bg_update.h"
#include "wcs_get_space.h"
#include "wcs_timer_start.h"
#include "process_deferred_stale.h"
#include "t_end.h"
#include "add_inter.h"
#include "jnl_write_pblk.h"
#include "jnl_write_aimg_rec.h"
#include "memcoherency.h"
#include "jnl_get_checksum.h"
#include "wbox_test_init.h"
#include "have_crit.h"
#ifdef GTM_SNAPSHOT
#include "db_snapshot.h"
#endif
#include "shmpool.h"
#include "bml_status_check.h"
#include "is_proc_alive.h"

GBLREF	bool			rc_locked;
GBLREF	unsigned char		t_fail_hist[CDB_MAX_TRIES];
GBLREF	cache_rec_ptr_t		cr_array[((MAX_BT_DEPTH * 2) - 1) * 2]; /* Maximum number of blocks that can be in transaction */
GBLREF	unsigned int		cr_array_index;
GBLREF	boolean_t		block_saved;
GBLREF	uint4			update_trans;
GBLREF	cw_set_element		cw_set[];		/* create write set. */
GBLREF	gd_region		*gv_cur_region;
GBLREF	gv_namehead		*gv_target, *gv_target_list;
GBLREF	sgmnt_addrs		*cs_addrs;
GBLREF	sgmnt_data_ptr_t	cs_data;
GBLREF	uint4			dollar_tlevel;
GBLREF	trans_num		start_tn;
GBLREF	unsigned int		t_tries;
GBLREF	uint4			t_err, process_id;
GBLREF	unsigned char		cw_set_depth, cw_map_depth;
GBLREF	unsigned char		rdfail_detail;
GBLREF	jnlpool_addrs		jnlpool;
GBLREF	jnlpool_ctl_ptr_t	jnlpool_ctl, temp_jnlpool_ctl;
GBLREF	boolean_t		is_updproc;
GBLREF	seq_num			seq_num_one;
GBLREF	boolean_t		mu_reorg_process;
GBLREF	boolean_t		unhandled_stale_timer_pop;
GBLREF	jnl_format_buffer	*non_tp_jfb_ptr;
GBLREF	sgmnt_addrs 		*kip_csa;
GBLREF	boolean_t		need_kip_incr;
GBLREF	boolean_t		write_after_image;
GBLREF	boolean_t		is_replicator;
GBLREF	seq_num			seq_num_zero;
GBLREF	jnl_gbls_t		jgbl;
GBLREF	jnl_fence_control	jnl_fence_ctl;
GBLREF	boolean_t		gvdupsetnoop; /* if TRUE, duplicate SETs update journal but not database (except for curr_tn++) */
GBLREF	boolean_t		is_dollar_incr;	/* valid only if gvcst_put is in the call-stack.
						 * is a copy of "in_gvcst_incr" just before it got reset to FALSE */
GBLREF	boolean_t		mu_reorg_upgrd_dwngrd_in_prog;	/* TRUE if MUPIP REORG UPGRADE/DOWNGRADE is in progress */
GBLREF	boolean_t		mu_reorg_nosafejnl;		/* TRUE if NOSAFEJNL explicitly specified */
GBLREF	trans_num		mu_reorg_upgrd_dwngrd_blktn;	/* tn in blkhdr of current block processed by REORG UP/DOWNGRADE */
GBLREF	inctn_opcode_t		inctn_opcode;
GBLREF	inctn_detail_t		inctn_detail;			/* holds detail to fill in to inctn jnl record */
GBLREF	boolean_t		block_is_free;
GBLREF	boolean_t		gv_play_duplicate_kills;
GBLREF	boolean_t		pool_init;
#ifdef GTM_TRIGGER
GBLREF	boolean_t		skip_dbtriggers;	/* see gbldefs.c for description of this global */
#endif
#ifdef UNIX
GBLREF	recvpool_addrs		recvpool;
GBLREF	int4			strm_index;
#endif
#ifdef DEBUG
GBLREF	boolean_t		mupip_jnl_recover;
#endif

error_def(ERR_GVKILLFAIL);
error_def(ERR_GVPUTFAIL);
error_def(ERR_JNLFILOPN);
error_def(ERR_JNLFLUSH);
error_def(ERR_JRTNULLFAIL);
error_def(ERR_NOTREPLICATED);
error_def(ERR_TEXT);

#define BLOCK_FLUSHING(x) (csa->hdr->clustered && x->flushing && !CCP_SEGMENT_STATE(cs_addrs->nl,CCST_MASK_HAVE_DIRTY_BUFFERS))

#define	RESTORE_CURRTN_IF_NEEDED(csa, write_inctn, decremented_currtn)					\
{													\
	if (write_inctn && decremented_currtn)								\
	{	/* decremented curr_tn above; need to restore to original state due to the restart */	\
		assert(csa->now_crit);									\
		if (csa->now_crit)									\
		{	/* need crit to update curr_tn and early_tn */					\
			csa->ti->curr_tn++;								\
			csa->ti->early_tn++;								\
		}											\
		decremented_currtn = FALSE;								\
	}												\
}

/* This macro isn't enclosed in parantheses to allow for optimizations */
#define VALIDATE_CYCLE(is_mm, history)					\
if (history)								\
{									\
	for (t1 = history->h;  t1->blk_num;  t1++)			\
	{								\
		if (!is_mm && (t1->cr->cycle != t1->cycle))		\
		{	/* cache slot has been stolen */		\
			assert(!csa->now_crit || csa->hold_onto_crit);	\
			status = cdb_sc_cyclefail;			\
			goto failed_skip_revert;			\
		}							\
		n_blks_validated++;					\
	}								\
}

trans_num t_end(srch_hist *hist1, srch_hist *hist2, trans_num ctn)
{
	srch_hist		*hist;
	bt_rec_ptr_t		bt;
	boolean_t		blk_used;
	cache_rec		cr_save;
	cache_rec_ptr_t		cr;
	cw_set_element		*cs, *cs_top, *cs1;
	enum cdb_sc		status;
	int			int_depth, tmpi;
	uint4			jnl_status;
	jnl_private_control	*jpc;
	jnl_buffer_ptr_t	jbp, jbbp; /* jbp is non-NULL if journaling, jbbp is non-NULL only if before-image journaling */
	sgmnt_addrs		*csa, *repl_csa;
	sgmnt_data_ptr_t	csd;
	node_local_ptr_t	cnl;
	sgm_info		*dummysi = NULL;	/* needed as a dummy parameter for {mm,bg}_update */
	srch_blk_status		*t1;
	trans_num		valid_thru, oldest_hist_tn, dbtn, blktn, temp_tn, epoch_tn, old_block_tn;
	unsigned char		cw_depth, cw_bmp_depth;
	jnldata_hdr_ptr_t	jnl_header;
	uint4			total_jnl_rec_size, tmp_cumul_jnl_rec_len, tmp_cw_set_depth, prev_cw_set_depth;
	DEBUG_ONLY(unsigned int	tot_jrec_size;)
	jnlpool_ctl_ptr_t	jpl, tjpl;
	boolean_t		replication = FALSE;
#	ifdef UNIX
	boolean_t		supplementary = FALSE;	/* this variable is initialized ONLY if "replication" is TRUE. */
	seq_num			strm_seqno, next_strm_seqno;
#	endif
	boolean_t		is_mm;
	boolean_t		read_before_image; /* TRUE if before-image journaling or online backup in progress
						    * This is used to read before-images of blocks whose cs->mode is gds_t_create */
	boolean_t		write_inctn = FALSE;	/* set to TRUE in case writing an inctn record is necessary */
	boolean_t		decremented_currtn, retvalue, busy2free_seen, recompute_cksum, cksum_needed;
	boolean_t		recycled2free_seen;
	boolean_t		in_mu_truncate = FALSE, jnlpool_crit_acquired = FALSE;
	boolean_t		was_crit;
	blk_hdr_ptr_t		old_block;
	unsigned int		bsiz, crindex;
	jnl_tm_t		save_gbl_jrec_time;
	enum gds_t_mode		mode;
	uint4			prev_cr_array_index;
	uint4			com_csum;
#	ifdef DEBUG
	boolean_t		ready2signal_gvundef_lcl;
	enum cdb_sc		prev_status;
	GTMCRYPT_ONLY(
		blk_hdr_ptr_t	save_old_block;
	)
#	endif
	int			n_blks_validated;
	boolean_t		before_image_needed, lcl_ss_in_prog = FALSE, reorg_ss_in_prog = FALSE;
	boolean_t		ss_need_to_restart, new_bkup_started;
	gv_namehead		*gvnh;
#	ifdef GTM_TRIGGER
	uint4			cycle;
#	endif
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	/* Currently, the only callers of t_end with NULL histories are the update process and journal recovery when they
	 * are about to process a JRT_NULL record. Assert that.
	 */
	assert((hist1 != hist2) || (ERR_JRTNULLFAIL == t_err) && (NULL == hist1)
					&& update_trans && (is_updproc || jgbl.forw_phase_recovery));
	DEBUG_ONLY(
		/* Store global variable ready2signal_gvundef in a local variable and reset the global right away to ensure that
		 * the global value does not incorrectly get carried over to the next call of "t_end".
		 */
		ready2signal_gvundef_lcl = TREF(ready2signal_gvundef);
		TREF(ready2signal_gvundef) = FALSE;
	)
	csa = cs_addrs;
	csd = csa->hdr;
	cnl = csa->nl;
	is_mm = (dba_mm == csd->acc_meth);
#	ifdef GTM_TRUNCATE
	DEBUG_ONLY(in_mu_truncate = (cnl != NULL && process_id == cnl->trunc_pid);)
#	endif
	TREF(rlbk_during_redo_root) = FALSE;
	status = cdb_sc_normal;
	/* The only cases where we set csa->hold_onto_crit to TRUE are the following :
	 * (a) jgbl.onlnrlbk
	 * (b) DSE CRIT -SEIZE (and any command that follows it), DSE CHANGE -BLOCK, DSE ALL -SEIZE (and any command that follows)
	 *	and DSE MAPS -RESTORE_ALL. Since we cannot distinguish between different DSE qualifiers, we use IS_DSE_IMAGE.
	 * (c) gvcst_redo_root_search in the final retry.
	 *
	 * Since we don't expect hold_onto_crit to be set by any other utility/function, the below assert is valid and is intended
	 * to catch cases where the field is inadvertently set to TRUE.
	 */
	assert(!csa->hold_onto_crit || IS_DSE_IMAGE UNIX_ONLY(|| jgbl.onlnrlbk || TREF(in_gvcst_redo_root_search)));
	assert(cs_data == csd);
	assert((t_tries < CDB_STAGNATE) || csa->now_crit);
	assert(!dollar_tlevel);
	/* whenever cw_set_depth is non-zero, ensure update_trans is also non-zero */
	assert(!cw_set_depth || (UPDTRNS_DB_UPDATED_MASK == update_trans));
	/* whenever cw_set_depth is zero, ensure that update_trans is FALSE except when it is a duplicate set or a duplicate kill
	 * or a NULL journal record.
	 */
	assert(cw_set_depth || !update_trans || ((ERR_GVPUTFAIL == t_err) && gvdupsetnoop)
		|| (ERR_JRTNULLFAIL == t_err) || ((ERR_GVKILLFAIL == t_err) && gv_play_duplicate_kills));
	assert(0 == cr_array_index);
	assert(!gv_cur_region->read_only || !update_trans);
	cr_array_index = 0;	/* be safe and reset it in PRO even if it is not zero */
	if (cnl->wc_blocked || (is_mm && (csa->total_blks != csa->ti->total_blks)))
	{ /* If blocked, or we have MM and file has been extended, force repair */
		status = cdb_sc_helpedout;	/* force retry with special status so philanthropy isn't punished */
		goto failed_skip_revert;
	} else
	{
		/* See if we can take a fast path for read transactions based on the following conditions :
		 * 1. If the transaction number hasn't changed since we read the blocks from the disk or cache
		 * 2. If NO concurrent online rollback is running. This is needed because we don't want read transactions
		 *    to succeed. The issue with this check is that for a rollback that was killed, the PID will be non-zero.
		 *    In that case, we might skip the fast path and go ahead and do the validation. The validation logic
		 *    gets crit anyways and so will salvage the lock and do the necessary recovery and issue DBFLCORRP if
		 *    it notices that csd->file_corrupt is TRUE.
		 */
		if (!update_trans && (start_tn == csa->ti->early_tn) UNIX_ONLY(&& (0 == csa->nl->onln_rlbk_pid)))
		{	/* read with no change to the transaction history */
			n_blks_validated = 0;
			VALIDATE_CYCLE(is_mm, hist1);	/* updates n_blks_validated */
			VALIDATE_CYCLE(is_mm, hist2);	/* updates n_blks_validated */
			assert(cdb_sc_normal == status);
#			ifdef UNIX
			if (MISMATCH_ROOT_CYCLES(csa, cnl))
			{	/* If a root block has moved, we might have started the read from the wrong root block, in which
				 * case we cannot trust the entire search. Need to redo root search.
				 */
				was_crit = csa->now_crit;
				if (!was_crit)
					grab_crit(gv_cur_region);
				status = cdb_sc_gvtrootmod2;
				if (MISMATCH_ONLN_RLBK_CYCLES(csa, cnl))
				{
					assert(!mupip_jnl_recover);
					status = ONLN_RLBK_STATUS(csa, cnl);
					SYNC_ONLN_RLBK_CYCLES;
					SYNC_ROOT_CYCLES(NULL);
				} else
					SYNC_ROOT_CYCLES(csa);
				if (!was_crit && !csa->hold_onto_crit)
					rel_crit(gv_cur_region);
				goto failed_skip_revert;
			}
#			endif
			/* Assert that if gtm_gvundef_fatal is non-zero, then we better not be about to signal a GVUNDEF */
			assert(!TREF(gtm_gvundef_fatal) || !ready2signal_gvundef_lcl);
			if (csa->now_crit && !csa->hold_onto_crit)
				rel_crit(gv_cur_region);
			if (unhandled_stale_timer_pop)
				process_deferred_stale();
			CWS_RESET;
			assert(!csa->now_crit || csa->hold_onto_crit); /* shouldn't hold crit unless asked to */
			t_tries = 0;	/* commit was successful so reset t_tries */
			INCR_GVSTATS_COUNTER(csa, cnl, n_nontp_readonly, 1);
			INCR_GVSTATS_COUNTER(csa, cnl, n_nontp_blkread, n_blks_validated);
			return csa->ti->curr_tn;
		}
	}
	busy2free_seen = FALSE;
	recycled2free_seen = FALSE;
	cw_depth = cw_set_depth;
	cw_bmp_depth = cw_depth;
	if (0 != cw_set_depth)
	{
		if (gds_t_writemap == cw_set[0].mode)
		{
			cw_depth = 0;
			cw_bmp_depth = 0;
		} else if ((gds_t_busy2free == cw_set[0].mode) && (gds_t_writemap == cw_set[1].mode))
		{
			cw_depth = 0;
			busy2free_seen = TRUE;
			cw_bmp_depth = 1;
		} else if ((gds_t_recycled2free == cw_set[0].mode) && (gds_t_writemap == cw_set[1].mode))
		{
			assert(process_id == cnl->trunc_pid && in_mu_truncate); /* In phase 1 of MUPIP REORG -TRUNCATE */
			cw_depth = 1;
			recycled2free_seen = TRUE;
			cw_bmp_depth = 1;
		}
	}
#	ifdef GTM_SNAPSHOT
	if (update_trans && SNAPSHOTS_IN_PROG(cnl))
	{
		/* If snapshot context is not already created, then create one now to be used by this transaction. If context
		 * creation failed (for instance, on snapshot file open fail), then SS_INIT_IF_NEEDED sets csa->snapshot_in_prog
		 * to FALSE.
		 */
		SS_INIT_IF_NEEDED(csa, cnl);
	} else
		CLEAR_SNAPSHOTS_IN_PROG(csa);
	lcl_ss_in_prog = SNAPSHOTS_IN_PROG(csa); /* store in local variable to avoid pointer access */
	reorg_ss_in_prog = (mu_reorg_process && lcl_ss_in_prog); /* store in local variable if both snapshots and MUPIP REORG
								  * are in progress */
#	endif
	if (0 != cw_depth)
	{	/* Caution : since csa->backup_in_prog and read_before_image are initialized below
	 	 * only if (cw_depth), these variables should be used below only within an if (cw_depth).
		 */
		assert(SIZEOF(bsiz) == SIZEOF(old_block->bsiz));
		assert(update_trans);
		csa->backup_in_prog = (BACKUP_NOT_IN_PROGRESS != cnl->nbb);
		jbbp = (JNL_ENABLED(csa) && csa->jnl_before_image) ? csa->jnl->jnl_buff : NULL;
		read_before_image = ((NULL != jbbp) || csa->backup_in_prog || lcl_ss_in_prog);
		for (cs = cw_set, cs_top = cs + cw_depth; cs < cs_top; cs++)
		{
			assert(0 == cs->jnl_freeaddr);	/* ensure haven't missed out resetting jnl_freeaddr for any cse in
							 * t_write/t_create/t_write_map/t_write_root/mu_write_map [D9B11-001991] */
			if (gds_t_create == cs->mode)
			{
				assert(0 == cs->blk_checksum);
				int_depth = (int)cw_set_depth;
				if (0 > (cs->blk = bm_getfree(cs->blk, &blk_used, cw_depth, cw_set, &int_depth)))
				{
					if (FILE_EXTENDED == cs->blk)
					{
						status = cdb_sc_helpedout;
						assert(is_mm);
					} else
					{
						GET_CDB_SC_CODE(cs->blk, status);	/* code is set in status */
					}
					goto failed_skip_revert;
				}
				blk_used ? SET_RECYCLED(cs) : SET_NRECYCLED(cs);
				BEFORE_IMAGE_NEEDED(read_before_image, cs, csa, csd, cs->blk, before_image_needed);
				if (!before_image_needed)
					cs->old_block = NULL;
				else
				{
					block_is_free = WAS_FREE(cs);
					cs->old_block = t_qread(cs->blk, (sm_int_ptr_t)&cs->cycle, &cs->cr);
					old_block = (blk_hdr_ptr_t)cs->old_block;
					if (NULL == old_block)
					{
						status = (enum cdb_sc)rdfail_detail;
						goto failed_skip_revert;
					}
					if (!WAS_FREE(cs) && (NULL != jbbp) && (old_block->tn < jbbp->epoch_tn))
					{	/* Compute CHECKSUM for writing PBLK record before getting crit.
						 * It is possible that we are reading a block that is actually marked free in
						 * the bitmap (due to concurrency issues at this point). Therefore we might be
						 * actually reading uninitialized block headers and in turn a bad value of
						 * "old_block->bsiz". Restart if we ever access a buffer whose size is greater
						 * than the db block size.
						 */
						bsiz = old_block->bsiz;
						if (bsiz > csd->blk_size)
						{
							assert(CDB_STAGNATE > t_tries);
							status = cdb_sc_lostbmlcr;
							goto failed_skip_revert;
						}
						JNL_GET_CHECKSUM_ACQUIRED_BLK(cs, csd, csa, old_block, bsiz);
					}
				}
				/* assert that the block that we got from bm_getfree is less than the total blocks.
				 * if we do not have crit in this region, then it is possible that bm_getfree can return
				 * a cs->blk that is >= csa->ti->total_blks (i.e. if the bitmap buffer gets recycled).
				 * adjust assert accordingly.
				 * note that checking for crit is equivalent to checking if we are in the final retry.
				 */
				assert((CDB_STAGNATE > t_tries) || (cs->blk < csa->ti->total_blks));
				cs->mode = gds_t_acquired;
				assert(GDSVCURR == cs->ondsk_blkver);
			} else if (reorg_ss_in_prog && WAS_FREE(cs))
			{
				assert((gds_t_acquired == cs->mode) && (NULL == cs->old_block));
				/* If snapshots are in progress, we might want to read the before images of the FREE blocks also.
				 * Since mu_swap_blk mimics a small part of t_end, it sets cse->mode to gds_t_acquired and hence
				 * will not read the before images of the FREE blocks in t_end. To workaround this, set
				 * cse->blk_prior_state's free status to TRUE so that in t_end, this condition can be used to read
				 * the before images of the FREE blocks if needed.
				 */
				BEFORE_IMAGE_NEEDED(read_before_image, cs, csa, csd, cs->blk, before_image_needed);
				if (before_image_needed)
				{
					block_is_free = TRUE; /* To tell t_qread that the block it's trying to read is
							       * actually a FREE block */
					cs->old_block = t_qread(cs->blk, (sm_int_ptr_t)&cs->cycle, &cs->cr);
					if (NULL == cs->old_block)
					{
						status = (enum cdb_sc)rdfail_detail;
						goto failed_skip_revert;
					}
				}
			}
		}
	}
	if (update_trans && JNL_ENABLED(csa))
	{	/* compute the total journal record size requirements before grab_crit.
		 * there is code later that will check for state changes from now to then and if so do a recomputation
		 */
		assert(!cw_map_depth || cw_set_depth < cw_map_depth);
		tmp_cw_set_depth = cw_map_depth ? cw_map_depth : cw_set_depth;
		TOTAL_NONTPJNL_REC_SIZE(total_jnl_rec_size, non_tp_jfb_ptr, csa, tmp_cw_set_depth);
		/* For a non-tp update maximum journal space we may need is total size of
		 * 	1) space for maximum CDB_CW_SET_SIZE PBLKs, that is, MAX_JNL_REC_SIZE * CDB_CW_SET_SIZE
		 *	2) space for a logical record itself, that is, MAX_LOGI_JNL_REC_SIZE and
		 * 	3) overhead records (MIN_TOTAL_NONTPJNL_REC_SIZE + JNL_FILE_TAIL_PRESERVE)
		 * This requirement is less than the minimum autoswitchlimit size (JNL_AUTOSWITCHLIMIT_MIN) as asserted below.
		 * Therefore we do not need any check to issue JNLTRANS2BIG error like is being done in tp_tend.c
		 */
#		ifdef UNIX
		assert((CDB_CW_SET_SIZE * MAX_PHY_JNL_REC_SIZE(csd) + MAX_LOGI_JNL_REC_SIZE +
			MIN_TOTAL_NONTPJNL_REC_SIZE + JNL_FILE_TAIL_PRESERVE) <= (JNL_AUTOSWITCHLIMIT_MIN * DISK_BLOCK_SIZE));
#		else
		assert((CDB_CW_SET_SIZE * MAX_JNL_REC_SIZE + MAX_LOGI_JNL_REC_SIZE +
			MIN_TOTAL_NONTPJNL_REC_SIZE + JNL_FILE_TAIL_PRESERVE) <= (JNL_AUTOSWITCHLIMIT_MIN * DISK_BLOCK_SIZE));
#		endif
		DEBUG_ONLY(tot_jrec_size = MAX_REQD_JNL_FILE_SIZE(total_jnl_rec_size));
		assert(tot_jrec_size <= csd->autoswitchlimit);
		/* The SET_GBL_JREC_TIME done below should be done before any journal writing activity
		 * on this region's journal file. This is because all the jnl record writing routines assume
		 * jgbl.gbl_jrec_time is initialized appropriately.
		 */
		assert(!jgbl.forw_phase_recovery || jgbl.dont_reset_gbl_jrec_time);
		if (!jgbl.dont_reset_gbl_jrec_time)
			SET_GBL_JREC_TIME;	/* initializes jgbl.gbl_jrec_time */
		assert(jgbl.gbl_jrec_time);
	}
	block_saved = FALSE;
	ESTABLISH_RET(t_ch, 0);
	assert(!csa->hold_onto_crit || csa->now_crit);
	if (!csa->now_crit)
	{
		if (update_trans)
		{	/* Get more space if needed. This is done outside crit so that
			 * any necessary IO has a chance of occurring outside crit.
			 * The available space must be double-checked inside crit. */
			if (!is_mm && (cnl->wc_in_free < (int4)(cw_set_depth + 1))
				   && !wcs_get_space(gv_cur_region, cw_set_depth + 1, NULL))
				assert(FALSE);	/* wcs_get_space should have returned TRUE unconditionally in this case */
			for (;;)
			{
				grab_crit(gv_cur_region);
				if (FALSE == csd->freeze)
					break;
				rel_crit(gv_cur_region);
				/* We are about to wait for freeze. Assert that we are not in phase2 of a bitmap free operation
				 * (part of an M-kill or REORG operation). The freeze must have waited for the phase2 to complete.
				 */
				assert((inctn_bmp_mark_free_gtm != inctn_opcode) && (inctn_bmp_mark_free_mu_reorg != inctn_opcode));
				while (csd->freeze)
					hiber_start(1000);
			}
		} else
			grab_crit(gv_cur_region);
	} else
	{	/* We expect the process to be in its final retry as it is holding crit. The only exception is if hold_onto_crit
		 * is TRUE but in that case we dont expect csd->freeze to be TRUE so we dont care much about that case. The other
		 * exception is if this is DSE which gets crit even without being in the final retry. In that case, skip the check
		 * about whether we are about to update a frozen db. DSE is the only utility allowed to update frozen databases.
		 */
		assert((CDB_STAGNATE == t_tries) || csa->hold_onto_crit || IS_DSE_IMAGE);
		if (csd->freeze && update_trans && !IS_DSE_IMAGE)
		{	/* We are about to update a frozen database. This is possible in rare cases even though
			 * we waited for the freeze to be lifted in t_retry (see GTM-7004). Restart in this case.
			 */
			status = cdb_sc_needcrit;
			goto failed;
		}
	}
	com_csum = 0;
	/* We should never proceed to update a frozen database. Only exception is DSE */
	assert(!update_trans || !csd->freeze || IS_DSE_IMAGE);
#	ifdef UNIX
	/* We never expect to come here with file_corrupt set to TRUE (in case of an online rollback) because
	 * grab_crit done above will make sure of that. The only exception is RECOVER/ROLLBACK itself coming
	 * here in the forward phase
	 */
	assert(!csd->file_corrupt || mupip_jnl_recover);
	if (MISMATCH_ROOT_CYCLES(csa, cnl))
	{
		status = cdb_sc_gvtrootmod2;
		if (MISMATCH_ONLN_RLBK_CYCLES(csa, cnl))
		{
			assert(!mupip_jnl_recover);
			status = ONLN_RLBK_STATUS(csa, cnl);
			SYNC_ONLN_RLBK_CYCLES;
			if (!TREF(in_gvcst_bmp_mark_free) || mu_reorg_process)
				SYNC_ROOT_CYCLES(NULL);
		} else
			SYNC_ROOT_CYCLES(csa);
		goto failed;
	}
	/* We should never proceed to commit if the global variable - only_reset_clues_if_onln_rlbk - is TRUE AND if the prior
	 * retry was due to ONLINE ROLLBACK. This way, we ensure that, whoever set the global variable knows to handle ONLINE
	 * ROLLBACK and resets it before returning control to the application.
	 */
	DEBUG_ONLY(prev_status = LAST_RESTART_CODE);
	assert((cdb_sc_normal == prev_status) || ((cdb_sc_onln_rlbk1 != prev_status) && (cdb_sc_onln_rlbk2 != prev_status))
		|| (!TREF(in_gvcst_bmp_mark_free) || mu_reorg_process));
#	endif
	if (is_mm && ((csa->hdr != csd) || (csa->total_blks != csa->ti->total_blks)))
        {       /* If MM, check if wcs_mm_recover was invoked as part of the grab_crit done above OR if
                 * the file has been extended. If so, restart.
                 */
                status = cdb_sc_helpedout;      /* force retry with special status so philanthropy isn't punished */
                goto failed;
        }
	assert(!cw_depth || update_trans);
#	ifdef GTM_TRIGGER
	if (!skip_dbtriggers)
	{
		cycle = csd->db_trigger_cycle;
		if (csa->db_trigger_cycle != cycle)
		{	/* the process' view of the triggers could be potentially stale. restart to be safe. */
			/* On an originating instance, in addition to the run-time, utilities can collide with
			 * with concurrent triggers definition updates
			 * The following asserts verify that:
			 * (1) Activities on a replicating instance don't see concurrent trigger changes as update
			 * process is the only updater in the replicating instance. The only exception is if this
			 * is a supplementary root primary instance. In that case, the update process coexists with
			 * GT.M processes and hence can see restarts due to concurrent trigger changes.
			 * (2) Journal recover operates in standalone mode. So, it should NOT see any concurrent
			 * trigger changes as well
			 */
			assert(!is_updproc || jnlpool.repl_inst_filehdr->is_supplementary && !jnlpool.jnlpool_ctl->upd_disabled);
			assert(!jgbl.forw_phase_recovery);
			assert(cycle > csa->db_trigger_cycle);
			/* csa->db_trigger_cycle will be set to csd->db_trigger_cycle in t_retry */
			status = cdb_sc_triggermod;
			goto failed;
		}
	}
#	endif
	/* If inctn_opcode has a valid value, then we better be doing an update. The only exception to this rule is if we are
	 * in MUPIP REORG UPGRADE/DOWNGRADE (mu_reorg_upgrd_dwngrd.c) where update_trans is explicitly set to 0 in some cases.
	 */
	assert((inctn_invalid_op == inctn_opcode) || mu_reorg_upgrd_dwngrd_in_prog || update_trans);
	if (update_trans)
	{
		if (JNL_ALLOWED(csa))
		{
			if ((csa->jnl_state != csd->jnl_state) || (csa->jnl_before_image != csd->jnl_before_image))
			{ 	/* csd->jnl_state or csd->jnl_before_image changed since last time
				 * 	csa->jnl_before_image and csa->jnl_state got set */
				csa->jnl_before_image = csd->jnl_before_image;
				csa->jnl_state = csd->jnl_state;
				/* jnl_file_lost causes a jnl_state transition from jnl_open to jnl_closed
				 * and additionally causes a repl_state transition from repl_open to repl_closed
				 * all without standalone access. This means that csa->repl_state might be repl_open
				 * while csd->repl_state might be repl_closed. update csa->repl_state in this case
				 * as otherwise the rest of the code might look at csa->repl_state and incorrectly
				 * conclude replication is on and generate sequence numbers when actually no journal
				 * records are being generated. [C9D01-002219]
				 */
				csa->repl_state = csd->repl_state;
				status = cdb_sc_jnlstatemod;
				goto failed;
			}
		}
		/* Flag retry, if other mupip activities like BACKUP, INTEG or FREEZE are in progress.
		 * If in final retry, go ahead with kill. BACKUP/INTEG/FREEZE will wait for us to be done.
		 */
		if (need_kip_incr && (0 < cnl->inhibit_kills) && (CDB_STAGNATE > t_tries))
		{
			status = cdb_sc_inhibitkills;
			goto failed;
		}
		ss_need_to_restart = new_bkup_started = FALSE;
		GTM_SNAPSHOT_ONLY(
			if (update_trans)
				CHK_AND_UPDATE_SNAPSHOT_STATE_IF_NEEDED(csa, cnl, ss_need_to_restart);
		)
		if (cw_depth)
		{
			assert(update_trans);
			CHK_AND_UPDATE_BKUP_STATE_IF_NEEDED(cnl, csa, new_bkup_started);
			/* recalculate based on the new values of snapshot_in_prog and backup_in_prog. Since read_before_image used
			 * only in the context of acquired blocks, recalculation should happen only for non-zero cw_depth
			 */
			read_before_image = ((JNL_ENABLED(csa) && csa->jnl_before_image)
					     || csa->backup_in_prog
					     || SNAPSHOTS_IN_PROG(csa));
		}
		if ((cw_depth && new_bkup_started) || (update_trans && ss_need_to_restart))
		{
			if (ss_need_to_restart || (new_bkup_started && !(JNL_ENABLED(csa) && csa->jnl_before_image)))
			{
				/* If online backup is in progress now and before-image journaling is not enabled,
				 * we would not have read before-images for created blocks. Although it is possible
				 * that this transaction might not have blocks with gds_t_create at all, we expect
				 * this backup_in_prog state change to be so rare that it is ok to restart.
				 */
				status = cdb_sc_bkupss_statemod;
				goto failed;
			}
		}
		/* in crit, ensure cache-space is available. the out-of-crit check done above might not have been enough */
		if (!is_mm && (cnl->wc_in_free < (int4)(cw_set_depth + 1))
			   && !wcs_get_space(gv_cur_region, cw_set_depth + 1, NULL))
		{
			assert(cnl->wc_blocked);	/* only reason we currently know why wcs_get_space could fail */
			assert(gtm_white_box_test_case_enabled);
			SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
			BG_TRACE_PRO_ANY(csa, wc_blocked_t_end_hist);
			SET_CACHE_FAIL_STATUS(status, csd);
			goto failed;
		}
		if (inctn_invalid_op != inctn_opcode)
		{
			assert(cw_set_depth || mu_reorg_process);
			write_inctn = TRUE;	/* mupip reorg or gvcst_bmp_mark_free or extra block split in gvcstput */
			decremented_currtn = FALSE;
			if (jgbl.forw_phase_recovery && !JNL_ENABLED(csa))
			{	/* forward recovery (deduced above from the fact that journaling is not enabled) is supposed
				 * to accurately simulate GT.M runtime activity for every transaction number. The way it does
				 * this is by incrementing transaction numbers for all inctn records that GT.M wrote and not
				 * incrementing transaction number for any inctn activity that forward recovery internally needs
				 * to do. all inctn activity done outside of t_end has already been protected against incrementing
				 * transaction number in case of forward recovery. t_end is a little bit tricky since in this case
				 * a few database blocks get modified with the current transaction number and not incrementing the
				 * transaction number might result in the database transaction number being lesser than the block
				 * transaction number. we work around this problem by decrementing the database transaction number
				 * just before the commit so the database block updates for the inctn transaction get the
				 * transaction number of the previous transaction effectively merging the inctn transaction with
				 * the previous transaction.
				 */
				/* cw_set_depth is 1 for all INCTN operations except for block free operations when it can be 2 */
				assert((inctn_gvcstput_extra_blk_split == inctn_opcode)
					|| (inctn_bmp_mark_free_gtm == inctn_opcode)
					|| (inctn_bmp_mark_free_mu_reorg == inctn_opcode) || (1 == cw_set_depth));
				csa->ti->curr_tn--;
				csa->ti->early_tn--;
				decremented_currtn = TRUE;
			}
		}
	}
	assert(csd == csa->hdr);
	valid_thru = dbtn = csa->ti->curr_tn;
	if (!is_mm)
		oldest_hist_tn = OLDEST_HIST_TN(csa);
	if (update_trans)
		valid_thru++;
	n_blks_validated = 0;
	for (hist = hist1;  (NULL != hist);  hist = (hist == hist1) ? hist2 : NULL)
	{
		for (t1 = hist->h;  t1->blk_num;  t1++)
		{
			if (is_mm)
			{
				if (t1->tn <= ((blk_hdr_ptr_t)(t1->buffaddr))->tn)
				{
					assert(CDB_STAGNATE > t_tries);
					status = cdb_sc_blkmod;
					goto failed;
				}
				t1->cse = NULL;	/* reset for next transaction */
			} else
			{
				bt = bt_get(t1->blk_num);
				if (NULL == bt)
				{
					if (t1->tn <= oldest_hist_tn)
					{
						assert(CDB_STAGNATE > t_tries);
						status = cdb_sc_losthist;
						goto failed;
					}
					cr = db_csh_get(t1->blk_num);
				} else
				{
					if (BLOCK_FLUSHING(bt))
					{
						assert(CDB_STAGNATE > t_tries);
						status = cdb_sc_blockflush;
						goto failed;
					}
					if (CR_NOTVALID == bt->cache_index)
						cr = db_csh_get(t1->blk_num);
					else
					{
						cr = (cache_rec_ptr_t)GDS_REL2ABS(bt->cache_index);
						if (cr->blk != bt->blk)
						{
							assert(FALSE);
							SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
							BG_TRACE_PRO_ANY(csa, wc_blocked_t_end_crbtmismatch1);
							status = cdb_sc_crbtmismatch;
							goto failed;
						}
					}
					assert(bt->killtn <= bt->tn);
					if (t1->tn <= bt->tn)
					{
						assert((CDB_STAGNATE > t_tries) || (cdb_sc_helpedout == t_fail_hist[t_tries]));
						assert(!IS_DOLLAR_INCREMENT || !write_inctn);
						/* If the current operation is a $INCREMENT, then try to optimize by recomputing
						 * the update array. Do this only as long as ALL the following conditions are met.
						 *	a) the current history's block is a leaf level block we intend to modify
						 *	b) update is restricted to the data block
						 *	c) no block splits are involved.
						 *	d) this does not end up creating a new global variable tree
						 *	e) this is not a case of $INCR about to signal a GVUNDEF
						 *		cw_set_depth is 0 in that case
						 * Conveniently enough, a simple check of (1 != cw_set_depth) is enough
						 * to categorize conditions (c) to (e)
						 *
						 * Future optimization : It is possible that M-SETs (not just $INCR)
						 * that update only one data block can benefit from this optimization.
						 * But that has to be carefully thought out.
						 */
						if (!IS_DOLLAR_INCREMENT
							|| t1->level || (1 != cw_set_depth) || (t1->blk_num != cw_set[0].blk))
						{
							status = cdb_sc_blkmod;
							goto failed;
						} else
						{
							status = gvincr_recompute_upd_array(t1, cw_set, cr);
							if (cdb_sc_normal != status)
							{
								status = cdb_sc_blkmod;
								goto failed;
							}
						}
					}
				}
				if ((cache_rec_ptr_t)CR_NOTVALID == cr)
				{
					SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
					BG_TRACE_PRO_ANY(csa, wc_blocked_t_end_hist);
					SET_CACHE_FAIL_STATUS(status, csd);
					goto failed;
				}
				if ((NULL == cr) || (cr->cycle != t1->cycle)
					|| ((sm_long_t)GDS_REL2ABS(cr->buffaddr) != (sm_long_t)t1->buffaddr))
				{
					if ((NULL != cr) && (NULL != bt) && (cr->blk != bt->blk))
					{
						assert(FALSE);
						SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
						BG_TRACE_PRO_ANY(csa, wc_blocked_t_end_crbtmismatch2);
						status = cdb_sc_crbtmismatch;
						goto failed;
					}
					assert(CDB_STAGNATE > t_tries);
					status = cdb_sc_lostcr;
					goto failed;
				}
				assert(0 == cr->in_tend);
				/* Pin those buffers that we are planning on updating. Those are easily identified as the ones
				 * where the history has a non-zero cw-set-element.
				 */
				cs = t1->cse;
				if (cs)
				{
					if (n_gds_t_op > cs->mode && gds_t_recycled2free != cs->mode) /* don't pass histories... */
					{
						assert(update_trans);
						assert(gds_t_busy2free  > gds_t_committed);
						assert(gds_t_busy2free  < n_gds_t_op);
						assert(gds_t_write_root > gds_t_committed);
						assert(gds_t_write_root < n_gds_t_op);
						assert(gds_t_recycled2free > gds_t_committed);
						assert(gds_t_recycled2free < n_gds_t_op);
						assert((gds_t_committed > cs->mode)
							|| (busy2free_seen && (gds_t_busy2free == cs->mode))
							|| (recycled2free_seen && gds_t_recycled2free == cs->mode));
						PIN_CACHE_RECORD(cr, cr_array, cr_array_index);
						/* If cs->mode is gds_t_busy2free, then the corresponding cache-record needs
						 * to be pinned to write the before-image right away but this cse is not going
						 * to go through bg_update. So remember to unpin the cache-record before phase2
						 * as otherwise the pre-phase2 check (that we have pinned only those cache-records
						 * that we are planning to update) will fail. But to do that, we rely on the fact
						 * that the cache-record corresponding to the gds_t_busy2free cse is always the
						 * first one in the cr_array.
						 */
						assert((gds_t_busy2free != cs->mode) || (1 == cr_array_index));
						assert((gds_t_recycled2free != cs->mode) || (1 == cr_array_index));
					}
					t1->cse = NULL;	/* reset for next transaction */
				}
			}
			t1->tn = valid_thru;
			n_blks_validated++;
		}
		assert((hist != hist2) || (t1 != hist->h));
	}
#	ifdef DEBUG
	/* If clue is non-zero, validate it (BEFORE this could be used in a future transaction). The only exception is reorg
	 * where we could have an invalid clue (e.g. last_rec < first_rec etc.). This is because reorg shuffles records around
	 * heavily and therefore it is hard to maintain an uptodate clue. reorg therefore handles this situation by actually
	 * resetting the clue just before doing the next gvcst_search. The mu_reorg* routines already take care of this reset
	 * (in fact, this is asserted in gvcst_search too). So we can allow invalid clues here in that special case.
	 */
	if (!mu_reorg_process && (NULL != gv_target) && gv_target->clue.end)
		/* gv_target can be NULL in case of DSE MAPS etc. */
		DEBUG_GVT_CLUE_VALIDATE(gv_target);	/* Validate that gvt has valid first_rec, clue & last_rec fields */
#	endif
	/* Assert that if gtm_gvundef_fatal is non-zero, then we better not be about to signal a GVUNDEF */
	assert(!TREF(gtm_gvundef_fatal) || !ready2signal_gvundef_lcl);
	/* check bit maps for usage */
	if (0 != cw_map_depth)
	{	/* Bit maps on end from mu_reorg (from a call to mu_swap_blk) or mu_reorg_upgrd_dwngrd */
		prev_cw_set_depth = cw_set_depth;
		prev_cr_array_index = cr_array_index;	/* note down current depth of pinned cache-records */
		cw_set_depth = cw_map_depth;
	}
	for (cs = &cw_set[cw_bmp_depth], cs_top = &cw_set[cw_set_depth]; cs < cs_top; cs++)
	{
		assert(0 == cs->jnl_freeaddr);	/* ensure haven't missed out resetting jnl_freeaddr for any cse in
						 * t_write/t_create/{t,mu}_write_map/t_write_root [D9B11-001991]
						 */
		/* A bitmap block update will cause us to restart with "cdb_sc_bmlmod". TP transactions on the other hand
		 * try reallocating blocks using the function "reallocate_bitmap". That is not presently used here because
		 * there are cases like MUPIP REORG or MUPIP REORG UPGRADE etc. where we do not want this functionality.
		 * Also, non-TP restarts due to bitmap collisions are currently assumed to be negligible. Hence no
		 * reallocation done in the non-TP case. Reconsider if this assumption is invalidated.
		 */
		if (is_mm)
		{
			if (cs->tn <= ((blk_hdr_ptr_t)(cs->old_block))->tn)
			{
				assert(CDB_STAGNATE > t_tries);
				status = cdb_sc_bmlmod;
				goto failed;
			}
		} else
		{
			bt = bt_get(cs->blk);
			if (NULL == bt)
			{
				if (cs->tn <= oldest_hist_tn)
				{
					assert(CDB_STAGNATE > t_tries);
					status = cdb_sc_lostbmlhist;
					goto failed;
				}
				cr = db_csh_get(cs->blk);
			} else
			{
				if (BLOCK_FLUSHING(bt))
				{
					assert(CDB_STAGNATE > t_tries);
					status = cdb_sc_blockflush;
					goto failed;
				}
				if (cs->tn <= bt->tn)
				{
					assert(CDB_STAGNATE > t_tries);
					status = cdb_sc_bmlmod;
					goto failed;
				}
				if (CR_NOTVALID == bt->cache_index)
					cr = db_csh_get(cs->blk);
				else
				{
					cr = (cache_rec_ptr_t)GDS_REL2ABS(bt->cache_index);
					if (cr->blk != bt->blk)
					{
						assert(FALSE);
						SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
						BG_TRACE_PRO_ANY(csa, wc_blocked_t_end_crbtmismatch3);
						status = cdb_sc_crbtmismatch;
						goto failed;
					}
				}
			}
			if ((cache_rec_ptr_t)CR_NOTVALID == cr)
			{
				SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
				BG_TRACE_PRO_ANY(csa, wc_blocked_t_end_bitmap_nullbt);
				SET_CACHE_FAIL_STATUS(status, csd);
				goto failed;
			}
			if ((NULL == cr)  || (cr->cycle != cs->cycle) ||
				((sm_long_t)GDS_REL2ABS(cr->buffaddr) != (sm_long_t)cs->old_block))
			{
				assert(CDB_STAGNATE > t_tries);
				status = cdb_sc_lostbmlcr;
				goto failed;
			}
			PIN_CACHE_RECORD(cr, cr_array, cr_array_index);
		}
	}
	if ((0 != cw_map_depth) && mu_reorg_upgrd_dwngrd_in_prog)
	{	/* Bit maps on end from mu_reorg_upgrd_dwngrd. Bitmap history has been validated.
		 * But we do not want bitmap cse to be considered for bg_update. Reset cw_set_depth accordingly.
		 */
		cw_set_depth = prev_cw_set_depth;
		assert(1 >= cw_set_depth);
		assert(2 >= cw_map_depth);
		/* UNPIN the bitmap cache record we no longer need */
		assert(prev_cr_array_index <= cr_array_index);
		if (prev_cr_array_index < cr_array_index)
		{
			cr_array_index--;
			assert(prev_cr_array_index == cr_array_index);
			assert(process_id == cr_array[cr_array_index]->in_cw_set);
			UNPIN_CACHE_RECORD(cr_array[cr_array_index]);
		}
	}
	assert(csd == csa->hdr);
	assert(!need_kip_incr || update_trans UNIX_ONLY(|| TREF(in_gvcst_redo_root_search)));
	if (update_trans)
	{
		if (cw_depth && read_before_image && !is_mm)
		{
			assert(!(JNL_ENABLED(csa) && csa->jnl_before_image) || jbbp == csa->jnl->jnl_buff);
			assert((JNL_ENABLED(csa) && csa->jnl_before_image) || (NULL == jbbp));
			for (cs = cw_set, cs_top = cs + cw_set_depth;  cs < cs_top;  ++cs)
			{	/* have already read old block for creates before we got crit, make sure
				 * cache record still has correct block. if not, reset "cse" fields to
				 * point to correct cache-record. this is ok to do since we only need the
				 * prior content of the block (for online backup or before-image journaling)
				 * and did not rely on it for constructing the transaction. Restart if
				 * block is not present in cache now or is being read in currently.
				 */
				if ((gds_t_acquired == cs->mode || gds_t_recycled2free == cs->mode) && (NULL != cs->old_block))
				{
					assert(read_before_image == ((JNL_ENABLED(csa) && csa->jnl_before_image)
								     || csa->backup_in_prog
								     || SNAPSHOTS_IN_PROG(csa)));
					cr = db_csh_get(cs->blk);
					if ((cache_rec_ptr_t)CR_NOTVALID == cr)
					{
						SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
						BG_TRACE_PRO_ANY(csa, wc_blocked_t_end_jnl_cwset);
						SET_CACHE_FAIL_STATUS(status, csd);
						goto failed;
					}
					/* It is possible that cr->in_cw_set is non-zero in case a concurrent MUPIP REORG
					 * UPGRADE/DOWNGRADE is in PHASE2 touching this very same block. In that case,
					 * we cannot reuse this block so we restart. We could try finding a different block
					 * to acquire instead and avoid a restart (tracked as part of C9E11-002651).
					 * Note that in_cw_set is set to 0 ahead of in_tend in bg_update_phase2. Therefore
					 * it is possible that we see in_cw_set 0 but in_tend is still non-zero. In that case,
					 * we cannot proceed with pinning this cache-record as the cr is still locked by
					 * the other process. We can choose to wait here but instead decide to restart.
					 */
					if ((NULL == cr) || (0 <= cr->read_in_progress)
						|| (0 != cr->in_cw_set) || (0 != cr->in_tend))
					{
						assert(CDB_STAGNATE > t_tries);
						status = cdb_sc_lostbefor;
						goto failed;
					}
					PIN_CACHE_RECORD(cr, cr_array, cr_array_index);
					cs->ondsk_blkver = cr->ondsk_blkver;
					old_block = (blk_hdr_ptr_t)GDS_REL2ABS(cr->buffaddr);
					assert((cs->cr != cr) || (cs->old_block == (sm_uc_ptr_t)old_block));
					old_block_tn = old_block->tn;
					/* Need checksums if before imaging and if a PBLK record is going to be written. However,
					 * while doing the bm_getfree, if we got a free block, then no need to compute checksum
					 * as we would NOT be writing before images of free blocks to journal files
					 */
					cksum_needed = (!WAS_FREE(cs) && (NULL != jbbp) && (old_block_tn < jbbp->epoch_tn));
					if ((cs->cr != cr) || (cs->cycle != cr->cycle))
					{	/* Block has relocated in the cache. Adjust pointers to new location. */
						cs->cr = cr;
						cs->cycle = cr->cycle;
						cs->old_block = (sm_uc_ptr_t)old_block;
						/* PBLK checksum was computed outside-of-crit when block was read but
						 * block has relocated in the cache since then so recompute the checksum
						 * if this block needs a checksum in the first place (cksum_needed is TRUE).
						 */
						recompute_cksum = cksum_needed;
					} else if (cksum_needed)
					{	/* We have determined that a checksum is needed for this block. If we have not
						 * previously computed one outside crit OR if the block contents have changed
						 * since the checksum was previously computed, we need to recompute it.
						 * Otherwise, the out-of-crit computed value can be safely used.
						 * Note that cs->tn is valid only if a checksum was computed outside of crit.
						 * So make sure it is used only if checksum is non-zero. There is a rare chance
						 * that the computed checksum could be zero in which case we will recompute
						 * unnecessarily. Since that is expected to be very rare, it is considered ok.
						 */
						recompute_cksum = (!cs->blk_checksum || (cs->tn <= old_block_tn));
					}
					if (!cksum_needed)
						cs->blk_checksum = 0;	/* zero any out-of-crit computed checksum */
					else if (recompute_cksum)
					{	/* We hold crit at this point so we are guaranteed valid bsiz field.
						 * Hence we do not need to take MIN(bsiz, csd->blk_size) like we did
						 * in the earlier call to jnl_get_checksum.
						 */
						assert(NULL != jbbp);
						assert(SIZEOF(bsiz) == SIZEOF(old_block->bsiz));
						bsiz = old_block->bsiz;
						assert(bsiz <= csd->blk_size);
						cs->blk_checksum = jnl_get_checksum((uint4*)old_block, csa, bsiz);
					}
					DEBUG_ONLY(
					else
						assert(cs->blk_checksum == jnl_get_checksum((uint4 *)old_block,
												csa, old_block->bsiz));
					)
					assert(cs->cr->blk == cs->blk);
				}
			}
		}
		/* if we are not writing an INCTN record, we better have a non-zero cw_depth.
		 * the only known exceptions are
		 * 	a) if we were being called from gvcst_put for a duplicate SET
		 * 	b) if we were being called from gvcst_kill for a duplicate KILL
		 * 	c) if we were called from DSE MAPS
		 * 	d) if we were being called from gvcst_jrt_null.
		 * in case (a) and (b), we want to write logical SET or KILL journal records and replicate them.
		 * in case (c), we do not want to replicate them. we want to assert that is_replicator is FALSE in this case.
		 * the following assert achieves that purpose.
		 */
		assert((inctn_invalid_op != inctn_opcode) || cw_depth
				|| !is_replicator						/* exception case (c) */
				|| (ERR_GVPUTFAIL == t_err) && gvdupsetnoop			/* exception case (a) */
				|| (ERR_JRTNULLFAIL == t_err)					/* exception case (d) */
				|| (ERR_GVKILLFAIL == t_err) && gv_play_duplicate_kills);	/* exception case (b) */
		if (REPL_ALLOWED(csa) && (NULL != jnlpool_ctl))
		{
			repl_csa = &FILE_INFO(jnlpool.jnlpool_dummy_reg)->s_addrs;
			if (!repl_csa->hold_onto_crit)
				grab_lock(jnlpool.jnlpool_dummy_reg, ASSERT_NO_ONLINE_ROLLBACK);
			assert(repl_csa->now_crit);
			jnlpool_crit_acquired = TRUE;
#			ifdef UNIX
			/* With jnlpool lock held, check instance freeze, and retry if set. */
			if (jnlpool.jnlpool_ctl->freeze)
			{
				status = cdb_sc_instancefreeze;
				goto failed;
			}
#			endif
			if (is_replicator && (inctn_invalid_op == inctn_opcode))
			{
				jpl = jnlpool_ctl;
				tjpl = temp_jnlpool_ctl;
				replication = TRUE;
				tjpl->write_addr = jpl->write_addr;
				tjpl->write = jpl->write;
				tjpl->jnl_seqno = jpl->jnl_seqno;
#				ifdef UNIX
				if (INVALID_SUPPL_STRM != strm_index)
				{	/* Need to also update supplementary stream seqno */
					supplementary = TRUE;
					assert(0 <= strm_index);
					/* assert(strm_index < ARRAYSIZE(tjpl->strm_seqno)); */
					strm_seqno = jpl->strm_seqno[strm_index];
					ASSERT_INST_FILE_HDR_HAS_HISTREC_FOR_STRM(strm_index);
				} else
					supplementary = FALSE;
#				endif
				INT8_ONLY(assert(tjpl->write == tjpl->write_addr % tjpl->jnlpool_size));
				assert(jgbl.cumul_jnl_rec_len);
				tmp_cumul_jnl_rec_len = (uint4)(jgbl.cumul_jnl_rec_len + SIZEOF(jnldata_hdr_struct));
				tjpl->write += SIZEOF(jnldata_hdr_struct);
				if (tjpl->write >= tjpl->jnlpool_size)
				{
					assert(tjpl->write == tjpl->jnlpool_size);
					tjpl->write = 0;
				}
				assert(jpl->early_write_addr == jpl->write_addr);
				jpl->early_write_addr = jpl->write_addr + tmp_cumul_jnl_rec_len;
				/* Source server does not read in crit. It relies on early_write_addr, the transaction
				 * data, lastwrite_len, write_addr being updated in that order. To ensure this order,
				 * we have to force out early_write_addr to its coherency point now. If not, the source
				 * server may read data that is overwritten (or stale). This is true only on
				 * architectures and OSes that allow unordered memory access
				 */
				SHM_WRITE_MEMORY_BARRIER;
			}
		}
		assert(cw_set_depth < CDB_CW_SET_SIZE);
		ASSERT_CURR_TN_EQUALS_EARLY_TN(csa, dbtn);
		CHECK_TN(csa, csd, dbtn);	/* can issue rts_error TNTOOLARGE */
		if (JNL_ENABLED(csa))
		{	/* Since we got the system time (jgbl.gbl_jrec_time) outside of crit, it is possible that
			 * journal records were written concurrently to this file with a timestamp that is future
			 * relative to what we recorded. In that case, adjust our recorded time to match this.
			 * This is necessary to ensure that timestamps of successive journal records for each
			 * database file are in non-decreasing order. A side-effect of this is that our recorded
			 * time might not accurately reflect the current system time but that is considered not
			 * an issue since we dont expect to be off by more than a second or two if at all.
			 * Another side effect is that even if the system time went back, we will never write
			 * out-of-order timestamped journal records in the lifetime of this database shared memory.
			 */
			jpc = csa->jnl;
			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);
			if (replication)
			{	/* Make sure timestamp of this seqno is >= timestamp of previous seqno. Note: The below macro
				 * invocation should be done AFTER the ADJUST_GBL_JREC_TIME call as the below resets
				 * jpl->prev_jnlseqno_time. Doing it the other way around would mean the reset will happen
				 * with a potentially lower value than the final adjusted time written in the jnl record.
				 */
				ADJUST_GBL_JREC_TIME_JNLPOOL(jgbl, jpl);
			}
			/* Note that jnl_ensure_open can call cre_jnl_file which
			 * in turn assumes jgbl.gbl_jrec_time is set. Also jnl_file_extend can call
			 * jnl_write_epoch_rec which in turn assumes jgbl.gbl_jrec_time is set.
			 * In case of forw-phase-recovery, mur_output_record would have already set this.
			 */
			assert(jgbl.gbl_jrec_time);
			jnl_status = jnl_ensure_open();
			GTM_WHITE_BOX_TEST(WBTEST_T_END_JNLFILOPN, jnl_status, ERR_JNLFILOPN);
			if (jnl_status == 0)
			{	/* tmp_cw_set_depth was used to do TOTAL_NONTPJNL_REC_SIZE calculation earlier in this function.
				 * It is now though that the actual jnl record write occurs. Ensure that the current value of
				 * cw_set_depth does not entail any change in journal record size than was calculated.
				 * Same case with csa->jnl_before_images & jbp->before_images.
				 * The only exception is that in case of mu_reorg_upgrd_dwngrd_in_prog cw_set_depth will be
				 * LESSER than tmp_cw_set_depth (this is still fine as there is more size allocated than used).
				 */
				assert(cw_set_depth == tmp_cw_set_depth
					|| mu_reorg_upgrd_dwngrd_in_prog && cw_map_depth && cw_set_depth < tmp_cw_set_depth);
				assert(jbp->before_images == csa->jnl_before_image);
				assert((csa->jnl_state == csd->jnl_state) && (csa->jnl_before_image == csd->jnl_before_image));
				if (DISK_BLOCKS_SUM(jbp->freeaddr, total_jnl_rec_size) > jbp->filesize)
				{	/* Moved as part of change to prevent journal records splitting
					 * across multiple generation journal files. */
					if (SS_NORMAL != (jnl_status = jnl_flush(jpc->region)))
					{
						send_msg(VARLSTCNT(9) ERR_JNLFLUSH, 2, JNL_LEN_STR(csd),
							ERR_TEXT, 2, RTS_ERROR_TEXT("Error with journal flush during t_end"),
							jnl_status);
						assert((!JNL_ENABLED(csd)) && (JNL_ENABLED(csa)));
						status = cdb_sc_jnlclose;
						goto failed;
					} else if (EXIT_ERR == jnl_file_extend(jpc, total_jnl_rec_size))
					{
						assert(csd == csa->hdr);	/* jnl_file_extend() shouldn't reset csd in MM */
						assert((!JNL_ENABLED(csd)) && (JNL_ENABLED(csa)));
						status = cdb_sc_jnlclose;
						goto failed;
					}
					assert(csd == csa->hdr);	/* If MM, csd shouldn't have been reset */
				}
				assert(jgbl.gbl_jrec_time >= jbp->prev_jrec_time);
				if (0 == jpc->pini_addr)
					jnl_put_jrt_pini(csa);
				if (JNL_HAS_EPOCH(jbp))
				{
					if ((jbp->next_epoch_time <= jgbl.gbl_jrec_time) UNCONDITIONAL_EPOCH_ONLY(|| TRUE))
					{	/* Flush the cache. Since we are in crit, defer syncing epoch */
						if (!wcs_flu(WCSFLU_FLUSH_HDR | WCSFLU_WRITE_EPOCH | WCSFLU_IN_COMMIT
												| WCSFLU_SPEEDUP_NOBEFORE))
						{
							SET_WCS_FLU_FAIL_STATUS(status, csd);
							SET_TRACEABLE_VAR(cnl->wc_blocked, TRUE);
							BG_TRACE_PRO_ANY(csa, wc_blocked_t_end_jnl_wcsflu);
							goto failed;
						}
						assert(csd == csa->hdr);
						VMS_ONLY(
							if (csd->clustered  &&
								!CCP_SEGMENT_STATE(cnl, CCST_MASK_HAVE_DIRTY_BUFFERS))
							{
								CCP_FID_MSG(gv_cur_region, CCTR_FLUSHLK);
								ccp_userwait(gv_cur_region,
									CCST_MASK_HAVE_DIRTY_BUFFERS, 0, cnl->ccp_cycle);
							}
						)
					}
				}
			} else
			{
				if (SS_NORMAL != jpc->status)
					rts_error(VARLSTCNT(7) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region),
						jpc->status);
				else
					rts_error(VARLSTCNT(6) jnl_status, 4, JNL_LEN_STR(csd), DB_LEN_STR(gv_cur_region));
			}
		}
		assert(!TREF(donot_commit));	/* We should never commit a transaction that was determined restartable */
		assert(TN_NOT_SPECIFIED > MAX_TN_V6); /* Ensure TN_NOT_SPECIFIED isn't a valid TN number */
		blktn = (TN_NOT_SPECIFIED == ctn) ? dbtn : ctn;
		csa->ti->early_tn = dbtn + 1;
		if (JNL_ENABLED(csa))
		{
			/* At this point we know tn,pini_addr and jrec_time; so calculate the checksum for the transaction once
			   reuse it for all the updates
			*/
			if(!com_csum)
			{
				ADJUST_CHECKSUM_TN(INIT_CHECKSUM_SEED, &dbtn, com_csum);
				ADJUST_CHECKSUM(com_csum, csa->jnl->pini_addr, com_csum);
				ADJUST_CHECKSUM(com_csum, jgbl.gbl_jrec_time, com_csum);
			}
			DEBUG_ONLY(save_gbl_jrec_time = jgbl.gbl_jrec_time;)
			if (jbp->before_images)
			{	/* do not write PBLKs if MUPIP REORG UPGRADE/DOWNGRADE with -NOSAFEJNL */
				if (!mu_reorg_upgrd_dwngrd_in_prog || !mu_reorg_nosafejnl)
				{
					epoch_tn = jbp->epoch_tn; /* store in a local as it is used in a loop below */
					for (cs = cw_set, cs_top = cs + cw_set_depth;  cs < cs_top;  ++cs)
					{
						/* PBLK computations for FREE blocks are not needed */
						if (WAS_FREE(cs))
							continue;
						/* write out before-update journal image records */
						mode = cs->mode;
						if (gds_t_committed < mode)
						{	/* There are two possibilities at this point.
							 * a) gds_t_write_root : In this case no need to write PBLK.
							 * b) gds_t_busy2free : This is set by gvcst_bmp_mark_free to indicate
							 *	that a block has to be freed right away instead of taking it
							 *	through the RECYCLED state. This should be done only if
							 *	csd->db_got_to_v5_once has not yet become TRUE. Once it is
							 *	TRUE, block frees will write PBLK only when the block is reused.
							 */
							assert((gds_t_write_root == mode) || (gds_t_busy2free == mode)
								|| (gds_t_recycled2free == mode));
							if ((gds_t_write_root == mode)
								|| (gds_t_busy2free == mode) && csd->db_got_to_v5_once)
							continue;
						}
						old_block = (blk_hdr_ptr_t)cs->old_block;
						ASSERT_IS_WITHIN_SHM_BOUNDS((sm_uc_ptr_t)old_block, csa);
						DBG_ENSURE_OLD_BLOCK_IS_VALID(cs, is_mm, csa, csd);
						if ((NULL != old_block) && (old_block->tn < epoch_tn))
						{
							bsiz = old_block->bsiz;
							assert((bsiz <= csd->blk_size) || IS_DSE_IMAGE);
							assert(bsiz >= SIZEOF(blk_hdr) || IS_DSE_IMAGE);
							/* For acquired or gds_t_busy2free blocks, we should have computed
							 * checksum already. The only exception is if we found no need to
							 * compute checksum outside of crit but before we got crit, an
							 * EPOCH got written concurrently so we have to write a PBLK (and
							 * hence compute the checksum as well) when earlier we thought none
							 * was necessary. An easy way to check this is that an EPOCH was
							 * written AFTER we started this transaction.
							 */
							assert((gds_t_acquired != cs->mode) || (gds_t_busy2free != cs->mode)
								|| cs->blk_checksum || (epoch_tn >= start_tn));
							/* It is possible that the block has a bad block-size.
							 * Before computing checksum ensure bsiz passed is safe.
							 * The checks done here for "bsiz" assignment are
							 * similar to those done in jnl_write_pblk/jnl_write_aimg.
							 */
							if (IS_DSE_IMAGE)
								bsiz = MIN(bsiz, csd->blk_size);
							assert(!cs->blk_checksum ||
								(cs->blk_checksum == jnl_get_checksum((uint4 *)old_block,
													csa,
													bsiz)));
							if (!cs->blk_checksum)
								cs->blk_checksum = jnl_get_checksum((uint4 *)old_block,
													csa,
													bsiz);
#							ifdef GTM_CRYPT
							if (csd->is_encrypted)
							{
								DBG_ENSURE_PTR_IS_VALID_GLOBUFF(csa, csd, (sm_uc_ptr_t)old_block);
								DEBUG_ONLY(save_old_block = old_block;)
								old_block = (blk_hdr_ptr_t)GDS_ANY_ENCRYPTGLOBUF(old_block,
														      csa);
								/* Ensure that the unencrypted block and it's twin counterpart are
								 * in sync.
								 */
								assert(save_old_block->tn == old_block->tn);
								assert(save_old_block->bsiz == old_block->bsiz);
								assert(save_old_block->levl == old_block->levl);
								DBG_ENSURE_PTR_IS_VALID_ENCTWINGLOBUFF(csa,
												       csd,
												       (sm_uc_ptr_t)old_block);
							}
#							endif
							jnl_write_pblk(csa, cs, old_block, com_csum);
							cs->jnl_freeaddr = jbp->freeaddr;
						}
						DEBUG_ONLY(
						else
							assert(0 == cs->jnl_freeaddr);
						)
					}
				}
			}
			if (write_after_image)
			{	/* either DSE or MUPIP RECOVER playing an AIMG record */
				assert(1 == cw_set_depth); /* only one block at a time */
				assert(!replication);
				cs = cw_set;
				jnl_write_aimg_rec(csa, cs, com_csum);
			} else if (write_inctn)
			{
				assert(!replication);
				if ((inctn_blkupgrd == inctn_opcode) || (inctn_blkdwngrd == inctn_opcode))
				{
					assert(1 == cw_set_depth); /* upgrade/downgrade one block at a time */
					cs = cw_set;
					assert(inctn_detail.blknum_struct.blknum == cs->blk);
					assert(mu_reorg_upgrd_dwngrd_blktn < dbtn);
					if (mu_reorg_nosafejnl)
					{
						blktn = mu_reorg_upgrd_dwngrd_blktn;
						/* if NOSAFEJNL and there is going to be a block format change
						 * as a result of this update, note it down in the inctn opcode
						 * (for recovery) as there is no PBLK record for it to rely on.
						 */
						if (cs->ondsk_blkver != csd->desired_db_format)
							inctn_opcode = (inctn_opcode == inctn_blkupgrd)
									? inctn_blkupgrd_fmtchng : inctn_blkdwngrd_fmtchng;
					}
				}
				jnl_write_inctn_rec(csa);
			} else if (0 == jnl_fence_ctl.level)
			{
				assert(!replication || !jgbl.forw_phase_recovery);
				if (replication)
				{
					jnl_fence_ctl.token = tjpl->jnl_seqno;
					UNIX_ONLY(
						if (supplementary)
							jnl_fence_ctl.strm_seqno = SET_STRM_INDEX(strm_seqno, strm_index);
					)
				} else if (!jgbl.forw_phase_recovery)
					jnl_fence_ctl.token = seq_num_zero;
				/* In case of forw-phase of recovery, token would have been set by mur_output_record */
				jnl_write_logical(csa, non_tp_jfb_ptr, com_csum);
			} else
				jnl_write_ztp_logical(csa, non_tp_jfb_ptr, com_csum);
			/* Ensure jgbl.gbl_jrec_time did not get reset by any of the jnl writing functions */
			assert(save_gbl_jrec_time == jgbl.gbl_jrec_time);
		} else if (replication)
		{	/* Case where JNL_ENABLED(csa) is FALSE but REPL_WAS_ENABLED(csa) is TRUE and therefore we need to
			 * write logical jnl records in the journal pool (no need to write in journal buffer or journal file).
			 */
			assert(!JNL_ENABLED(csa) && REPL_WAS_ENABLED(csa));
			if (0 == jnl_fence_ctl.level)
			{
				jnl_fence_ctl.token = tjpl->jnl_seqno;
				UNIX_ONLY(
					if (supplementary)
						jnl_fence_ctl.strm_seqno = SET_STRM_INDEX(strm_seqno, strm_index);
				)
				jnl_write_logical(csa, non_tp_jfb_ptr, com_csum);
			} else
				jnl_write_ztp_logical(csa, non_tp_jfb_ptr, com_csum);
		}
		if (replication)
		{
			tjpl->jnl_seqno++;
			assert(csa->hdr->reg_seqno < tjpl->jnl_seqno);
			csa->hdr->reg_seqno = tjpl->jnl_seqno;
			UNIX_ONLY(
				if (supplementary)
				{
					next_strm_seqno = strm_seqno + 1;
					/* tjpl->strm_seqno[strm_index] = next_strm_seqno; */
					csa->hdr->strm_reg_seqno[strm_index] = next_strm_seqno;
				}
			)
			VMS_ONLY(
				if (is_updproc)
				{
					jgbl.max_resync_seqno++;
					csa->hdr->resync_seqno = jgbl.max_resync_seqno;
				}
			)
		}
		csa->prev_free_blks = csa->ti->free_blocks;
		csa->t_commit_crit = T_COMMIT_CRIT_PHASE1;
		if (cw_set_depth)
		{
			if (!is_mm)	/* increment counter of # of processes that are actively doing two-phase commit */
				INCR_WCS_PHASE2_COMMIT_PIDCNT(csa, cnl);
#			ifdef DEBUG
			/* Assert that cs->old_mode if uninitialized, never contains a negative value (relied by secshr_db_clnup) */
			for (cs = cw_set, cs_top = cs + cw_set_depth;  cs < cs_top;  ++cs)
				assert(0 <= cs->old_mode);
#			endif
			for (cs = cw_set, cs_top = cs + cw_set_depth;  cs < cs_top;  ++cs)
			{
				mode = cs->mode;
				assert((gds_t_write_root != mode) || ((cs - cw_set) + 1 == cw_depth));
				cs->old_mode = (int4)mode;	/* note down before being reset to gds_t_committed */
				assert(gds_t_committed < gds_t_write_root);
				assert(gds_t_committed < gds_t_busy2free);
				assert((n_gds_t_op != mode) && (gds_t_committed != mode));
				assert((kill_t_write != mode) && (kill_t_create != mode));
				if (gds_t_committed > mode)
				{
					DEBUG_ONLY(
						/* Check bitmap status of block we are about to modify.
						 * Two exceptions are
						 *	a) DSE which can modify bitmaps at will.
						 *	b) MUPIP RECOVER writing an AIMG. In this case it is playing
						 *		forward a DSE action so is effectively like DSE doing it.
						 */
						if (!IS_DSE_IMAGE && !write_after_image)
							bml_status_check(cs);
					)
					if (is_mm)
						status = mm_update(cs, dbtn, blktn, dummysi);
					else
					{
						if (csd->dsid)
						{
							if (ERR_GVKILLFAIL == t_err)
							{
								if (cs == cw_set)
								{
									if ((gds_t_acquired == mode) ||
									    ((cw_set_depth > 1) && (0 == cw_set[1].level)))
										rc_cpt_inval();
									else
										rc_cpt_entry(cs->blk);
								}
							} else	if (0 == cs->level)
								rc_cpt_entry(cs->blk);
						}
						/* Do phase1 of bg_update while holding crit on the database.
						 * This will lock the buffers that need to be changed.
						 * Once crit is released, invoke phase2 which will update those locked buffers.
						 * The exception is if it is a bitmap block. In that case we also do phase2
						 * while holding crit so the next process to use this bitmap will see a
						 * consistent copy of this bitmap when it gets crit for commit. This avoids
						 * the reallocate_bitmap routine from restarting or having to wait for a
						 * concurrent phase2 construction to finish. When the change request C9E11-002651
						 * (to reduce restarts due to bitmap collisions) is addressed, we can reexamine
						 * whether it makes sense to move bitmap block builds back to phase2.
						 */
						status = bg_update_phase1(cs, dbtn, dummysi);
						if ((cdb_sc_normal == status) && (gds_t_writemap == mode))
						{
							status = bg_update_phase2(cs, dbtn, blktn, dummysi);
							if (cdb_sc_normal == status)
								cs->mode = gds_t_committed;
						}
					}
					if (cdb_sc_normal != status)
					{	/* the database is probably in trouble */
						INVOKE_T_COMMIT_CLEANUP(status, csa);
						assert(cdb_sc_normal == status);
						/* At this time "cr_array_index" could be non-zero and a few cache-records might
						 * have their "in_cw_set" field set to TRUE. We should not reset "in_cw_set" as we
						 * don't hold crit at this point and also because we might still need those buffers
						 * pinned until their before-images are backed up in wcs_recover (in case an
						 * online backup was running while secshr_db_clnup did its job). Reset the
						 * local variable "cr_array_index" though so we do not accidentally reset the
						 * "in_cw_set" fields ourselves before the wcs_recover.
						 */
						cr_array_index = 0;
						goto skip_cr_array;	/* hence skip until past "cr_array_index" processing */
					}
				}
			}
		}
		/* signal secshr_db_clnup/t_commit_cleanup, roll-back is no longer possible */
		update_trans |= UPDTRNS_TCOMMIT_STARTED_MASK;
		assert(cdb_sc_normal == status);
		/* should never increment curr_tn on a frozen database except if DSE */
		assert(!(csd->freeze UNIX_ONLY(|| (replication && jnlpool.jnlpool_ctl->freeze))) || IS_DSE_IMAGE);
		INCREMENT_CURR_TN(csd);
		csa->t_commit_crit = T_COMMIT_CRIT_PHASE2;	/* set this BEFORE releasing crit but AFTER incrementing curr_tn */
		/* If db is journaled, then db header is flushed periodically when writing the EPOCH record,
		 * otherwise do it here every HEADER_UPDATE_COUNT transactions.
		 */
		assert(!JNL_ENABLED(csa) || (jbp == csa->jnl->jnl_buff));
		if ((!JNL_ENABLED(csa) || !JNL_HAS_EPOCH(jbp)) && !(csd->trans_hist.curr_tn & (HEADER_UPDATE_COUNT - 1)))
			fileheader_sync(gv_cur_region);
		UNIX_ONLY(assert(!TREF(in_mu_swap_root) || need_kip_incr));
		if (need_kip_incr)		/* increment kill_in_prog */
		{
			INCR_KIP(csd, csa, kip_csa);
			need_kip_incr = FALSE;
#			ifdef UNIX
			if (TREF(in_mu_swap_root))
			{	/* Increment root_search_cycle to let other processes know that they should redo_root_search. */
				assert((0 != cw_map_depth) && !TREF(in_gvcst_redo_root_search));
				csa->nl->root_search_cycle++;
			}
#			endif
		}
		start_tn = dbtn; /* start_tn temporarily used to store currtn (for bg_update_phase2) before releasing crit */
	}
	if (!is_mm && busy2free_seen)
	{
		assert((gds_t_busy2free == cw_set[0].mode) && cr_array_index && (cw_set[0].blk == cr_array[0]->blk));
		assert(process_id == cr_array[0]->in_cw_set);
		UNPIN_CACHE_RECORD(cr_array[0]); /* need to do this BEFORE releasing crit as we have no other lock on this buffer */
	} else if (recycled2free_seen)
	{
		assert(gds_t_recycled2free == cw_set[0].mode);
		assert(!is_mm);
		if (read_before_image)
		{
			assert(cr_array_index == 2);
			assert(cw_set[0].blk = cr_array[1]->blk);
			assert(process_id == cr_array[1]->in_cw_set);
			UNPIN_CACHE_RECORD(cr_array[1]);
		} else
		{
			assert(cr_array_index == 1);
			/* Never pinned it in the first place */
		}
	}
	if (!csa->hold_onto_crit)
		rel_crit(gv_cur_region);
	assert(!replication || update_trans);
	if (replication)
	{
		assert(jpl->early_write_addr > jpl->write_addr);
		assert(tmp_cumul_jnl_rec_len == (tjpl->write - jpl->write + (tjpl->write > jpl->write ? 0 : jpl->jnlpool_size)));
		/* the following statements should be atomic */
		jnl_header = (jnldata_hdr_ptr_t)(jnlpool.jnldata_base + jpl->write);
		jnl_header->jnldata_len = tmp_cumul_jnl_rec_len;
		jnl_header->prev_jnldata_len = jpl->lastwrite_len;
		/* The following assert should be an == rather than a >= (as in tp_tend) because, we have
		 * either one or no update.  If no update, we would have no cw_depth and we wouldn't enter
		 * this path.  If there is an update, then both the indices should be 1.
		 */
		INT8_ONLY(assert(jgbl.cumul_index == jgbl.cu_jnl_index));
		UNIX_ONLY(
			if (supplementary)
				jpl->strm_seqno[strm_index] = next_strm_seqno;
		)
		jpl->lastwrite_len = jnl_header->jnldata_len;
		/* For systems with UNORDERED memory access (example, ALPHA, POWER4, PA-RISC 2.0), on a
		 * multi processor system, it is possible that the source server notices the change in
		 * write_addr before seeing the change to jnlheader->jnldata_len, leading it to read an
		 * invalid transaction length. To avoid such conditions, we should commit the order of
		 * shared memory updates before we update write_addr. This ensures that the source server
		 * sees all shared memory updates related to a transaction before the change in write_addr
		 */
		SHM_WRITE_MEMORY_BARRIER;
		jpl->write = tjpl->write;
		/* jpl->write_addr should be updated before updating jpl->jnl_seqno as secshr_db_clnup relies on this */
		jpl->write_addr += jnl_header->jnldata_len;
		assert(jpl->early_write_addr == jpl->write_addr);
		jpl->jnl_seqno = tjpl->jnl_seqno;
	}
	if (jnlpool_crit_acquired)
	{
		assert((NULL != jnlpool_ctl) && repl_csa->now_crit && REPL_ALLOWED(csa));
		rel_lock(jnlpool.jnlpool_dummy_reg);
	}
	/* If BG, check that we have not pinned any more buffers than we are updating */
	DBG_CHECK_PINNED_CR_ARRAY_CONTENTS(is_mm, cr_array, cr_array_index, csd->bplmap);
	if (cw_set_depth)
	{	/* Finish 2nd phase of commit for BG (updating the buffers in phase1) now that CRIT has been released.
		 * For MM, only thing needed is to set cs->mode to gds_t_committed.
		 */
		for (cs = cw_set, cs_top = cs + cw_set_depth;  cs < cs_top;  ++cs)
		{
			mode = cs->mode;
			assert((gds_t_write_root != mode) || ((cs - cw_set) + 1 == cw_depth));
			assert((kill_t_write != mode) && (kill_t_create != mode));
			if (gds_t_committed > mode)
			{
				if (!is_mm)
				{	/* Validate old_mode noted down in first phase is the same as the current mode.
					 * Note that cs->old_mode is negated by bg_update_phase1 (to help secshr_db_clnup).
					 */
					assert(-cs->old_mode == mode);
					status = bg_update_phase2(cs, dbtn, blktn, dummysi);
					if (cdb_sc_normal != status)
					{	/* the database is probably in trouble */
						INVOKE_T_COMMIT_CLEANUP(status, csa);
						assert(cdb_sc_normal == status);
						/* At this time "cr_array_index" could be non-zero and a few cache-records might
						 * have their "in_cw_set" field set to TRUE. We should not reset "in_cw_set" as we
						 * don't hold crit at this point and also because we might still need those buffers
						 * pinned until their before-images are backed up in wcs_recover (in case an
						 * online backup was running while secshr_db_clnup did its job). Reset the
						 * local variable "cr_array_index" though so we do not accidentally reset the
						 * "in_cw_set" fields ourselves before the wcs_recover.
						 */
						cr_array_index = 0;
						/* Note that seshr_db_clnup (invoked by t_commit_cleanup above) would have
						 * done a lot of cleanup for us including decrementing the wcs_phase2_commit_pidcnt
						 * so it is ok to skip all that processing below and go directly to skip_cr_array.
						 */
						goto skip_cr_array;	/* hence skip until past "cr_array_index" processing */
					}
				}
			}
			cs->mode = gds_t_committed;
		}
		if (!is_mm)	/* now that two-phase commit is done, decrement counter */
			DECR_WCS_PHASE2_COMMIT_PIDCNT(csa, cnl);
	}
	UNPIN_CR_ARRAY_ON_COMMIT(cr_array, cr_array_index);
	assert(!cr_array_index);
	csa->t_commit_crit = FALSE;
	/* Phase 2 commits are completed. See if we had done a snapshot init (csa->snapshot_in_prog == TRUE). If so,
	 * try releasing the resources obtained while snapshot init.
	 */
	if (SNAPSHOTS_IN_PROG(csa))
	{
		assert(update_trans);
		SS_RELEASE_IF_NEEDED(csa, cnl);
	}

skip_cr_array:
	assert(!csa->now_crit || csa->hold_onto_crit);
	assert(cdb_sc_normal == status);
	REVERT;	/* no need for t_ch to be invoked if any errors occur after this point */
	DEFERRED_EXIT_HANDLING_CHECK; /* now that all crits are released, check if deferred signal/exit handling needs to be done */
	if (block_saved)
		backup_buffer_flush(gv_cur_region);
	if (unhandled_stale_timer_pop)
		process_deferred_stale();
	if (update_trans)
	{
		wcs_timer_start(gv_cur_region, TRUE);
		if (REPL_ALLOWED(csa) && IS_DSE_IMAGE)
		{
			temp_tn = dbtn + 1;
			send_msg(VARLSTCNT(6) ERR_NOTREPLICATED, 4, &temp_tn, LEN_AND_LIT("DSE"), process_id);
		}
		INCR_GVSTATS_COUNTER(csa, cnl, n_nontp_readwrite, 1);
		INCR_GVSTATS_COUNTER(csa, cnl, n_nontp_blkread, n_blks_validated);
		INCR_GVSTATS_COUNTER(csa, cnl, n_nontp_blkwrite, cw_set_depth);
		GVSTATS_SET_CSA_STATISTIC(csa, db_curr_tn, dbtn);
	} else
	{
		INCR_GVSTATS_COUNTER(csa, cnl, n_nontp_readonly, 1);
		INCR_GVSTATS_COUNTER(csa, cnl, n_nontp_blkread, n_blks_validated);
	}
	/* "secshr_db_clnup/t_commit_cleanup" assume an active non-TP transaction if cw_set_depth is non-zero
	 * or if update_trans is set to T_COMMIT_STARTED. Now that the transaction is complete, reset these fields.
	 */
	DEBUG_ONLY(jgbl.cumul_index = jgbl.cu_jnl_index = 0;) 	/* symmetrical with TP and makes op_tstart checks happy */
	cw_set_depth = 0;
	update_trans = 0;
	CWS_RESET;
	/* although we have the same assert at the beginning of "skip_cr_array" label, the below assert ensures that we did not grab
	 * crit in between (in process_deferred_stale() or backup_buffer_flush() or wcs_timer_start()). The assert at the beginning
	 * of the loop is needed as well to ensure that if we are done with the commit, we should have released crit
	 */
	assert(!csa->now_crit || csa->hold_onto_crit);
	t_tries = 0;	/* commit was successful so reset t_tries */
	assert(0 == cr_array_index);
	return dbtn;
failed:
	assert(cdb_sc_normal != status);
	REVERT;
failed_skip_revert:
	RESTORE_CURRTN_IF_NEEDED(csa, write_inctn, decremented_currtn);
	retvalue = t_commit_cleanup(status, 0);	/* we expect to get a return value indicating update was NOT underway */
	assert(!retvalue); 			/* if it was, then we would have done a "goto skip_cr_array:" instead */
	if ((NULL != hist1) && (NULL != (gvnh = hist1->h[0].blk_num ? hist1->h[0].blk_target : NULL)))
		gvnh->clue.end = 0;
	if ((NULL != hist2) && (NULL != (gvnh = hist2->h[0].blk_num ? hist2->h[0].blk_target : NULL)))
		gvnh->clue.end = 0;
	DEBUG_ONLY(
		/* Ensure we dont have t1->cse set for any gv_targets that also have their clue non-zero.
		 * As this can cause following transactions to rely on out-of-date information and do wrong things.
		 * (e.g. in t_end of the following transaction, we will see t1->cse non-NULL and conclude the buffer
		 * needs to be pinned when actually it is not necessary).
		 */
		for (gvnh = gv_target_list; NULL != gvnh; gvnh = gvnh->next_gvnh)
		{
			if (gvnh->clue.end)
			{
				for (t1 = &gvnh->hist.h[0]; t1->blk_num; t1++)
					assert(NULL == t1->cse);
			}
		}
	)
	/* t_commit_cleanup releases crit as long as the transition is from 2nd to 3rd retry or 3rd to 3rd retry. The only exception
	 * is if hold_onto_crit is set to TRUE in which case t_commit_cleanup honors it. Assert accordingly.
	 */
	assert(!csa->now_crit || !NEED_TO_RELEASE_CRIT(t_tries, status) || csa->hold_onto_crit);
	DEFERRED_EXIT_HANDLING_CHECK; /* now that all crits are released, check if deferred signal/exit handling needs to be done */
	t_retry(status);
	/* in the retry case, we do not do a CWS_RESET as cw_stagnate is used only in the
	 * final retry in which case t_end will succeed and do a CWS_RESET
	 */
	cw_map_depth = 0;
	assert(0 == cr_array_index);
	return 0;
}
