fis-gtm/sr_port/op_trollback.c

177 lines
6.7 KiB
C

/****************************************************************
* *
* Copyright 2001, 2011 Fidelity Information Services, Inc *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gdsroot.h"
#include "gdsblk.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 "tp_frame.h"
#include "tp_timeout.h"
#include "lv_val.h" /* needed for tp_unwind.h */
#include "tp_unwind.h"
#include "op.h"
#include "jobinterrupt_process.h"
GBLREF uint4 dollar_tlevel;
GBLREF uint4 dollar_trestart;
GBLREF gv_key *gv_currkey;
GBLREF gv_namehead *gv_target;
GBLREF tp_region *tp_reg_list; /* Chained list of regions used in this transaction not cleared on tp_restart */
GBLREF gd_region *gv_cur_region;
GBLREF sgmnt_data_ptr_t cs_data;
GBLREF sgmnt_addrs *cs_addrs;
GBLREF void (*tp_timeout_clear_ptr)(void);
GBLREF int process_exiting;
#ifdef GTM_TRIGGER
GBLREF int4 gtm_trigger_depth;
GBLREF int4 tstart_trigger_depth;
#endif
#ifdef DEBUG
GBLREF boolean_t donot_INVOKE_MUMTSTART;
#endif
GBLREF boolean_t implicit_trollback;
GBLREF tp_frame *tp_pointer;
error_def(ERR_TLVLZERO);
error_def(ERR_TROLLBK2DEEP);
error_def(ERR_INVROLLBKLVL);
#define RESTORE_GV_CUR_REGION \
{ \
gv_cur_region = save_cur_region; \
TP_CHANGE_REG(gv_cur_region); \
}
void op_trollback(int rb_levels) /* rb_levels -> # of transaction levels by which we need to rollback : BYPASSOK */
{
boolean_t lcl_implicit_trollback = FALSE;
uint4 newlevel;
gd_region *save_cur_region; /* saved copy of gv_cur_region before tp_clean_up/tp_incr_clean_up modifies it */
gd_region *curreg;
gv_key *gv_orig_key_ptr;
sgmnt_addrs *csa;
tp_region *tr;
if (implicit_trollback)
{
/* Unlike the call to "op_trollback" from generated code, this invocation of "op_trollback" is from C runtime code.
* Set the global variable to FALSE right away to avoid incorrect values from persisting in case of errors
* down below. Before the reset of the global variable, copy it into a local variable.
*/
lcl_implicit_trollback = implicit_trollback;
implicit_trollback = FALSE;
}
if (!dollar_tlevel)
rts_error(VARLSTCNT(1) ERR_TLVLZERO);
if (0 > rb_levels)
{
if (dollar_tlevel < -rb_levels)
rts_error(VARLSTCNT(4) ERR_TROLLBK2DEEP, 2, -rb_levels, dollar_tlevel);
} else if (dollar_tlevel <= rb_levels)
rts_error(VARLSTCNT(4) ERR_INVROLLBKLVL, 2, rb_levels, dollar_tlevel);
newlevel = (0 > rb_levels) ? dollar_tlevel + rb_levels : rb_levels;
/* The DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC macro is used at various points in the database code to check that
* gv_target and cs_addrs are in sync. This is because op_gvname relies on this in order to avoid a gv_bind_name
* function call (if incoming key matches gv_currkey from previous call, it uses gv_target and cs_addrs right
* away instead of recomputing them). We want to check that here as well. The only exception is if we were
* interrupted in the middle of TP transaction by an external signal which resulted in us terminating right away.
* In this case, we are guaranteed not to make a call to op_gvname again (because we are exiting) so it is ok
* not to do this check.
*/
DEBUG_ONLY(
if (!process_exiting)
{
DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC;
}
)
save_cur_region = gv_cur_region;
GTMTRIG_ONLY(assert(tstart_trigger_depth <= gtm_trigger_depth);) /* see similar assert in op_tcommit.c for why */
if (!newlevel)
{
(*tp_timeout_clear_ptr)(); /* Cancel or clear any pending TP timeout */
/* Do a rollback type cleanup (invalidate gv_target clues of read as well as
* updated blocks). This is typically needed for a restart.
*/
tp_clean_up(TRUE);
for (tr = tp_reg_list; NULL != tr; tr = tr->fPtr)
{
curreg = tr->reg;
if (!curreg->open)
continue;
csa = &FILE_INFO(curreg)->s_addrs;
INCR_GVSTATS_COUNTER(csa, csa->nl, n_tp_rolledback, 1);
if (csa->now_crit && !csa->hold_onto_crit)
rel_crit(curreg); /* release any crit regions */
}
if (lcl_implicit_trollback && tp_pointer->implicit_tstart)
{ /* This is an implicit TROLLBACK of an implicit TSTART started for a non-tp explicit update.
* gv_currkey needs to be restored to the value it was at the beginning of the implicit TSTART.
* This is necessary so as to maintain $reference accurately in case of an error during the
* ^#t processing initiated by an explicit non-tp update.
*/
assert(NULL != gv_currkey);
assert(tp_pointer && tp_pointer->orig_key);
gv_orig_key_ptr = tp_pointer->orig_key;
/* At this point we expect tp_pointer->orig_key and gv_currkey to be in sync. However there are two
* exceptions to this.
* (a) If MUPIP TRIGGER detects that all of the triggers are erroneous and attempts an "op_trollback",
* gv_currkey would be pointing to ^#t. However, tp_pointer->orig_key would remain at "" (initialized
* during op_tstart).
* (b) If an M program defines $etrap to do a halt and an explicit Non-TP update causes a trigger to be
* executed that further causes a runtime error which will now invoke the $etrap code. Since the
* $etrap code does a halt, gtm_exit_handler will invoke "op_trollback" (since dollar_tlevel is > 0).
* At this point, tp_pointer->orig_key and gv_currkey need not be in sync.
* To maintain $reference accurately, we need to restore gv_currkey from tp_pointer->orig_key.
*/
memcpy(gv_currkey->base, gv_orig_key_ptr->base, gv_orig_key_ptr->end);
} else if (gv_currkey != NULL)
{
gv_currkey->base[0] = '\0';
gv_currkey->end = gv_currkey->prev = 0;
}
if (NULL != gv_target)
gv_target->clue.end = 0;
tp_unwind(newlevel, ROLLBACK_INVOCATION, NULL);
/* Now that we are out of TP, reset the debug-only global variable that is relevant only if we are in TP */
DEBUG_ONLY(donot_INVOKE_MUMTSTART = FALSE;)
dollar_trestart = 0;
RESTORE_GV_CUR_REGION;
JOBINTR_TP_RETHROW; /* rethrow job interrupt($ZINT) if $ZTEXIT, when coerced to boolean, is true */
} else
{
tp_incr_clean_up(newlevel);
if (gv_currkey != NULL)
{
gv_currkey->base[0] = '\0';
gv_currkey->end = gv_currkey->prev = 0;
}
RESTORE_GV_CUR_REGION;
tp_unwind(newlevel, ROLLBACK_INVOCATION, NULL);
}
DEBUG_ONLY(
if (!process_exiting)
{
DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC;
}
)
}