597 lines
23 KiB
C
597 lines
23 KiB
C
/****************************************************************
|
|
* *
|
|
* 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 <stdarg.h>
|
|
#include <stddef.h> /* for OFFSETOF macro */
|
|
#include "gtm_string.h"
|
|
#include "gtm_stdio.h"
|
|
|
|
#include "gdsroot.h"
|
|
#include "gdsblk.h"
|
|
#include "gtm_facility.h"
|
|
#include "fileinfo.h"
|
|
#include "gdsbt.h"
|
|
#include "gdsfhead.h"
|
|
#include "filestruct.h"
|
|
#include "gdscc.h"
|
|
#include "gdskill.h"
|
|
#include "cdb_sc.h"
|
|
#include "lv_val.h"
|
|
#include "jnl.h"
|
|
#include "mlkdef.h"
|
|
#include <rtnhdr.h>
|
|
#include "mv_stent.h"
|
|
#include "stack_frame.h"
|
|
#include "tp_frame.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 "tp_timeout.h"
|
|
#include "op.h"
|
|
#include "have_crit.h"
|
|
#include "gtm_caseconv.h"
|
|
#include "gvcst_protos.h" /* for gvcst_tp_init prototype */
|
|
#include "dpgbldir.h"
|
|
#include "longset.h" /* needed for cws_insert.h */
|
|
#include "cws_insert.h" /* for cw_stagnate_reinitialized */
|
|
#include "alias.h"
|
|
#ifdef GTM_TRIGGER
|
|
#include "gtm_trigger_trc.h"
|
|
#endif
|
|
|
|
#ifdef UNICODE_SUPPORTED
|
|
#include "gtm_icu_api.h" /* needed by *TYPEMASK* macros defined in gtm_utf8.h */
|
|
#include "gtm_utf8.h"
|
|
#endif
|
|
|
|
error_def(ERR_STACKCRIT);
|
|
error_def(ERR_STACKOFLOW);
|
|
error_def(ERR_TPMIXUP);
|
|
error_def(ERR_TPTOODEEP);
|
|
|
|
GBLREF jnl_fence_control jnl_fence_ctl;
|
|
GBLREF uint4 dollar_tlevel;
|
|
GBLREF boolean_t dollar_truth;
|
|
GBLREF mval dollar_zgbldir;
|
|
GBLREF sgmnt_addrs *cs_addrs;
|
|
GBLREF gd_addr *gd_header;
|
|
GBLREF gv_key *gv_currkey;
|
|
GBLREF gv_namehead *gv_target;
|
|
GBLREF stack_frame *frame_pointer;
|
|
GBLREF tp_frame *tp_pointer;
|
|
GBLREF mv_stent *mv_chain;
|
|
GBLREF mlk_pvtblk *mlk_pvt_root;
|
|
GBLREF symval *curr_symval;
|
|
GBLREF unsigned char *msp, *stacktop, *stackwarn, *tpstackbase, *tpstacktop, *tp_sp, t_fail_hist[CDB_MAX_TRIES];
|
|
GBLREF unsigned int t_tries;
|
|
GBLREF tp_region *tp_reg_list, *tp_reg_free_list;
|
|
GBLREF trans_num local_tn; /* transaction number for THIS PROCESS */
|
|
GBLREF trans_num tstart_local_tn; /* copy of global variable "local_tn" at op_tstart time */
|
|
GBLREF boolean_t mupip_jnl_recover;
|
|
GBLREF void (*tp_timeout_start_timer_ptr)(int4 tmout_sec);
|
|
GBLREF sgm_info *first_sgm_info;
|
|
GBLREF global_tlvl_info *global_tlvl_info_head;
|
|
GBLREF buddy_list *global_tlvl_info_list;
|
|
GBLREF sgmnt_data_ptr_t cs_data;
|
|
GBLREF sgmnt_addrs *cs_addrs;
|
|
GBLREF gd_region *gv_cur_region;
|
|
GBLREF struct_jrec_tcom tcom_record;
|
|
GBLREF jnl_gbls_t jgbl;
|
|
GBLREF boolean_t tp_in_use;
|
|
GBLREF boolean_t gtm_utf8_mode;
|
|
GBLREF uint4 tstartcycle;
|
|
GBLREF char *update_array_ptr;
|
|
GBLREF ua_list *curr_ua, *first_ua;
|
|
#ifdef GTM_TRIGGER
|
|
GBLREF mval dollar_ztwormhole;
|
|
GBLREF int4 gtm_trigger_depth;
|
|
GBLREF int4 tstart_trigger_depth;
|
|
GBLREF mval dollar_ztslate;
|
|
LITREF mval literal_null;
|
|
#endif
|
|
#ifdef VMS
|
|
GBLREF boolean_t tp_has_kill_t_cse; /* cse->mode of kill_t_write or kill_t_create got created in this transaction */
|
|
#endif
|
|
|
|
#define NORESTART -1
|
|
#define ALLLOCAL -2
|
|
#define TP_STACK_SIZE ((TP_MAX_NEST + 1) * SIZEOF(tp_frame)) /* Size of TP stack frame with no-overflow pad */
|
|
|
|
void op_tstart(int implicit_flag, ...) /* value of $T when TSTART */
|
|
{
|
|
boolean_t serial; /* whether SERIAL keyword was present */
|
|
int prescnt, /* number of names to save, -1 = no restart, -2 = preserve all */
|
|
pres;
|
|
lv_val *lv;
|
|
mlk_pvtblk *pre_lock;
|
|
mlk_tp *lck_tp;
|
|
mval *preserve, /* list of names to save */
|
|
*tid, /* transaction id */
|
|
*mvname;
|
|
mv_stent *mv_st_ent, *mvst_tmp, *mvst_prev;
|
|
stack_frame *fp, *fp_fix;
|
|
tp_frame *tf;
|
|
unsigned char *old_sp, *top, *tstack_ptr, *ptrstart, *ptrend, *ptrinvalidbegin;
|
|
va_list varlst, lvname;
|
|
tp_region *tr, *tr_next;
|
|
sgm_info *si;
|
|
tlevel_info *tli, *new_tli, *prev_tli;
|
|
global_tlvl_info *gtli, *new_gtli, *prev_gtli;
|
|
kill_set *ks, *prev_ks;
|
|
jnl_format_buffer *jfb, *prev_jfb;
|
|
gd_region *r_top, *r_local;
|
|
gd_addr *addr_ptr;
|
|
unsigned char tp_bat[TP_BATCH_LEN];
|
|
mname_entry tpvent;
|
|
ht_ent_mname *tabent, *curent, *topent;
|
|
sgmnt_addrs *csa;
|
|
int4 shift_size;
|
|
boolean_t tphold_noshift = FALSE, implicit_tstart;
|
|
GTMTRIG_ONLY(boolean_t implicit_trigger;)
|
|
DCL_THREADGBL_ACCESS;
|
|
|
|
SETUP_THREADGBL_ACCESS;
|
|
implicit_tstart = 0 != (implicit_flag & IMPLICIT_TSTART);
|
|
GTMTRIG_ONLY(implicit_trigger = 0 != (implicit_flag & IMPLICIT_TRIGGER_TSTART));
|
|
GTMTRIG_ONLY(assert(!implicit_trigger || (implicit_trigger && implicit_tstart)));
|
|
GTMTRIG_ONLY(DBGTRIGR((stderr, "op_tstart: Entered - dollar_tlevel: %d, implicit_flag: %d\n",
|
|
dollar_tlevel, implicit_flag)));
|
|
if (implicit_tstart)
|
|
/* An implicit op_tstart is being done. In this case, even if we are in direct mode, we want to do
|
|
* regular TPHOLD processing (no setting of tphold in the parent frame and shifting of all mv_stents).
|
|
* This is ok because the life of the TP transaction will be done before the implicit operation()s are done
|
|
* done so it will not persist across M lines like it normally would in direct mode.
|
|
*/
|
|
tphold_noshift = TRUE;
|
|
/* If we haven't done any TP until now, turn the flag on to tell gvcst_init to
|
|
initialize it in any regions it opens from now on and initialize it in any
|
|
regions that are already open.
|
|
*/
|
|
if (!tp_in_use)
|
|
{
|
|
tp_in_use = TRUE;
|
|
for (addr_ptr = get_next_gdr(NULL); addr_ptr; addr_ptr = get_next_gdr(addr_ptr))
|
|
{
|
|
for (r_local = addr_ptr->regions, r_top = r_local + addr_ptr->n_regions; r_local < r_top; r_local++)
|
|
{
|
|
if (r_local->open && !r_local->was_open &&
|
|
(dba_bg == r_local->dyn.addr->acc_meth || dba_mm == r_local->dyn.addr->acc_meth))
|
|
{ /* Let's initialize those regions but only if it came through gvcst_init_sysops
|
|
(being a bg or mm region)
|
|
*/
|
|
gvcst_tp_init(r_local);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (0 != jnl_fence_ctl.level)
|
|
rts_error(VARLSTCNT(4) ERR_TPMIXUP, 2, "An M", "a fenced logical");
|
|
if (dollar_tlevel + 1 >= TP_MAX_NEST)
|
|
rts_error(VARLSTCNT(1) ERR_TPTOODEEP);
|
|
va_start(varlst, implicit_flag); /* no argument count first */
|
|
serial = va_arg(varlst, int);
|
|
tid = va_arg(varlst, mval *);
|
|
prescnt = va_arg(varlst, int);
|
|
MV_FORCE_STR(tid);
|
|
if (!dollar_tlevel)
|
|
{
|
|
jnl_fence_ctl.fence_list = JNL_FENCE_LIST_END;
|
|
jgbl.cumul_jnl_rec_len = 0;
|
|
# ifdef DEBUG
|
|
if (1 == jgbl.cumul_index) /* when 1 == jgbl.cumul_index and 0 == jgbl.cumul_index, non-TP morphed into TP */
|
|
jgbl.cumul_index = 0;
|
|
else
|
|
assert(0 == jgbl.cumul_index);
|
|
TREF(tp_restart_dont_counts) = 0;
|
|
# endif
|
|
assert(0 == jgbl.cu_jnl_index);
|
|
GTMTRIG_ONLY(memcpy(&dollar_ztslate, &literal_null, SIZEOF(mval))); /* Zap $ZTSLate at start of lvl 1 trans */
|
|
GTMTRIG_ONLY(if (!implicit_tstart || !implicit_trigger))
|
|
{ /* This is the path for all non-implicit-trigger type TP fences including the implicit fences
|
|
* created by the update process and by mupip recover forward.
|
|
* Note: For recover/rollback, t_tries is set to CDB_STAGNATE because unlike Non-TP restarts, TP restarts
|
|
* need more context to determine the restart point which is non-trivial for recover/rollback since the
|
|
* updates are done with journal records extracted by sequentially reading the journal file.
|
|
*/
|
|
t_tries = (FALSE == mupip_jnl_recover) ? 0 : CDB_STAGNATE;
|
|
t_fail_hist[t_tries] = cdb_sc_normal;
|
|
/* ensure that we don't have crit on any region at the beginning of a TP transaction (be it GT.M or MUPIP).
|
|
* The only exception is ONLINE ROLLBACK which holds crit for the entire duration
|
|
*/
|
|
assert(0 == have_crit(CRIT_HAVE_ANY_REG) UNIX_ONLY(|| jgbl.onlnrlbk));
|
|
}
|
|
# ifdef GTM_TRIGGER
|
|
else
|
|
{
|
|
/* This is an implicit TP wrap created for an explicit update. In such case, we do not want to reset
|
|
* t_tries for below reasons:
|
|
* (a) If an explicit non-tp update undergoes 3 restarts (t_tries = 3) and on the final retry gvcst_put
|
|
* sees that triggers are defined (due to concurrent $ZTRIGGER or MUPIP TRIGGER), it would have invoked
|
|
* op_tstart to create a TP wrap in final retry in which case we should NOT reset t_tries to zero as we
|
|
* would be holding crit at that time.
|
|
*
|
|
* (b) If an explicit non-tp update underwent 1 restart and on the next try(t_tries = 1), gvcst_put
|
|
* sees that triggers are defined (due to concurrent $ZTRIGGER or MUPIP TRIGGER), it would have invoked
|
|
* op_tstart to create a TP wrap in which case we should NOT reset t_tries. In the event, we did the
|
|
* reset of t_tries and no triggers were defined for the update in question, op_tcommit would be done
|
|
* and we will be back in non-TP BUT with t_tries reset to 0. The above can continue in a cycle causing
|
|
* a live spin-lock that does not let the final-retry optimistic -> pessimistic concurrency scheme from
|
|
* kicking in at all.
|
|
*/
|
|
/* recovery logic does not invoke triggers */
|
|
assert(!(SFF_IMPLTSTART_CALLD & frame_pointer->flags) || (FALSE == mupip_jnl_recover));
|
|
}
|
|
# endif
|
|
for (tr = tp_reg_list; NULL != tr; tr = tr_next)
|
|
{ /* start with empty list, place all existing entries on free list */
|
|
tp_reg_list = tr_next = tr->fPtr; /* Remove from queue */
|
|
tr->fPtr = tp_reg_free_list;
|
|
tp_reg_free_list = tr; /* Place on free queue */
|
|
}
|
|
++local_tn; /* Begin new local transaction */
|
|
tstart_local_tn = local_tn;
|
|
/* In journal recovery forward phase, we set jgbl.tp_ztp_jnl_upd_num to whatever update_num the journal record
|
|
* has so it is ok for the global variable to be a non-zero value at the start of a TP transaction (possible if
|
|
* ZTP of one process is in progress when TP of another process starts in the journal file). But otherwise
|
|
* (in GT.M runtime) we expect it to be 0 at beginning of each TP or ZTP.
|
|
*/
|
|
assert((0 == jgbl.tp_ztp_jnl_upd_num) || jgbl.forw_phase_recovery);
|
|
INCR_TSTARTCYCLE;
|
|
jgbl.wait_for_jnl_hard = TRUE;
|
|
GTMTRIG_ONLY(
|
|
assert(NULL == jgbl.prev_ztworm_ptr); /* should have been cleared by tp_clean_up of previous TP */
|
|
assert(NULL == jgbl.save_ztworm_ptr);
|
|
/* should have been NULL almost always except for a small window in gvcst_put/gvcst_kill */
|
|
tstart_trigger_depth = gtm_trigger_depth; /* note down what trigger depth an outermost tstart occurs in */
|
|
)
|
|
memset(tcom_record.jnl_tid, 0, TID_STR_SIZE);
|
|
if (0 != tid->str.len)
|
|
{
|
|
if (!gtm_utf8_mode)
|
|
{
|
|
if (tid->str.len > TID_STR_SIZE)
|
|
tid->str.len = TID_STR_SIZE;
|
|
}
|
|
# ifdef UNICODE_SUPPORTED
|
|
else
|
|
{ /* In UTF8 mode, take only as many valid multi-byte characters as can fit in TID_STR_SIZE */
|
|
if (gtm_utf8_mode)
|
|
{
|
|
MV_FORCE_LEN(tid); /* issues BADCHAR error if appropriate */
|
|
if (tid->str.len > TID_STR_SIZE)
|
|
{
|
|
ptrstart = (unsigned char *)tid->str.addr;
|
|
ptrend = ptrstart + TID_STR_SIZE;
|
|
UTF8_LEADING_BYTE(ptrend, ptrstart, ptrinvalidbegin)
|
|
tid->str.len = INTCAST(ptrinvalidbegin - ptrstart);
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
assert(TID_STR_SIZE >= tid->str.len);
|
|
memcpy(tcom_record.jnl_tid, (char *)tid->str.addr, tid->str.len);
|
|
if ((TP_BATCH_SHRT == tid->str.len) || (TP_BATCH_LEN == tid->str.len))
|
|
{
|
|
lower_to_upper(tp_bat, (uchar_ptr_t)tid->str.addr, (int)tid->str.len);
|
|
if (0 == memcmp(TP_BATCH_ID, tp_bat, tid->str.len))
|
|
jgbl.wait_for_jnl_hard = FALSE;
|
|
}
|
|
}
|
|
VMS_ONLY(tp_has_kill_t_cse = FALSE;)
|
|
assert(!TREF(donot_commit));
|
|
}
|
|
/* either cw_stagnate has not been initialized at all or previous-non-TP or tp_hist should have done CWS_RESET */
|
|
assert((0 == cw_stagnate.size) || cw_stagnate_reinitialized);
|
|
if (prescnt > 0)
|
|
{
|
|
VAR_COPY(lvname, varlst);
|
|
for (pres = 0; pres < prescnt; ++pres)
|
|
{
|
|
preserve = va_arg(lvname, mval *);
|
|
assert(MV_IS_STRING(preserve)); /* Check if this loop can be eliminated */
|
|
MV_FORCE_STR(preserve);
|
|
}
|
|
va_end(lvname);
|
|
}
|
|
if (NULL == gd_header)
|
|
gvinit();
|
|
assert(NULL != gd_header);
|
|
if (!tphold_noshift && (SFT_DM & frame_pointer->old_frame_pointer->type))
|
|
{ /* Put a TPHOLD underneath dmode frame */
|
|
assert(frame_pointer->old_frame_pointer->old_frame_pointer);
|
|
fp = frame_pointer->old_frame_pointer->old_frame_pointer;
|
|
top = (unsigned char *)(frame_pointer->old_frame_pointer + 1);
|
|
old_sp = msp;
|
|
shift_size = mvs_size[MVST_TPHOLD];
|
|
msp -= shift_size;
|
|
if (msp <= stackwarn)
|
|
{
|
|
va_end(varlst);
|
|
if (msp <= stacktop)
|
|
{
|
|
msp = old_sp;
|
|
rts_error(VARLSTCNT(1) ERR_STACKOFLOW);
|
|
} else
|
|
rts_error(VARLSTCNT(1) ERR_STACKCRIT);
|
|
}
|
|
memmove(msp, old_sp, top - (unsigned char *)old_sp); /* Shift stack w/possible overlapping ranges */
|
|
mv_st_ent = (mv_stent *)(top - shift_size);
|
|
mv_st_ent->mv_st_type = MVST_TPHOLD;
|
|
ADJUST_FRAME_POINTER(frame_pointer, shift_size);
|
|
for (fp_fix = frame_pointer; fp_fix != fp; fp_fix = fp_fix->old_frame_pointer)
|
|
{
|
|
if ((unsigned char *)fp_fix->l_symtab < top && (unsigned char *)fp_fix->l_symtab > stacktop)
|
|
fp_fix->l_symtab = (ht_ent_mname **)((char *)fp_fix->l_symtab - shift_size);
|
|
if (fp_fix->temps_ptr < top && fp_fix->temps_ptr > stacktop)
|
|
fp_fix->temps_ptr -= shift_size;
|
|
if (fp_fix->vartab_ptr < (char *)top && fp_fix->vartab_ptr > (char *)stacktop)
|
|
fp_fix->vartab_ptr -= shift_size;
|
|
if ((unsigned char *)fp_fix->old_frame_pointer < top &&
|
|
(char *)fp_fix->old_frame_pointer > (char *)stacktop)
|
|
{
|
|
ADJUST_FRAME_POINTER(fp_fix->old_frame_pointer, shift_size);
|
|
}
|
|
}
|
|
if ((unsigned char *)mv_chain >= top)
|
|
{
|
|
mv_st_ent->mv_st_next = (unsigned int)((char *)mv_chain - (char *)mv_st_ent);
|
|
mv_chain = mv_st_ent;
|
|
} else
|
|
{
|
|
top -= shift_size + SIZEOF(stack_frame);
|
|
mv_chain = (mv_stent *)((char *)mv_chain - shift_size);
|
|
mvst_tmp = mv_chain;
|
|
mvst_prev = (mv_stent *)((char *)mvst_tmp + mvst_tmp->mv_st_next);
|
|
while (mvst_prev < (mv_stent *)top)
|
|
{
|
|
mvst_tmp = mvst_prev;
|
|
mvst_prev = (mv_stent *)((char *)mvst_tmp + mvst_tmp->mv_st_next);
|
|
}
|
|
mvst_tmp->mv_st_next = (unsigned int)((char *)mv_st_ent - (char *)mvst_tmp);
|
|
mv_st_ent->mv_st_next = (unsigned int)((char *)mvst_prev - (char *)mv_st_ent + shift_size);
|
|
}
|
|
} else
|
|
{
|
|
PUSH_MV_STENT(MVST_TPHOLD);
|
|
mv_st_ent = mv_chain;
|
|
fp = frame_pointer;
|
|
}
|
|
mv_st_ent->mv_st_cont.mvs_tp_holder.tphold_tlevel = dollar_tlevel;
|
|
# ifdef GTM_TRIGGER
|
|
if (!dollar_tlevel)
|
|
/* We only save this on level 0 - Note if this is made further conditional, be sure to visit
|
|
* stp_gcol_src.h where it is GC'd and tp_restart() to adjust the conditions there as well.
|
|
*/
|
|
memcpy(&mv_st_ent->mv_st_cont.mvs_tp_holder.ztwormhole_save, &dollar_ztwormhole, SIZEOF(mval));
|
|
# endif
|
|
if (NULL == tpstackbase)
|
|
{
|
|
tstack_ptr = (unsigned char *)malloc(TP_STACK_SIZE);
|
|
tp_sp = tpstackbase = tstack_ptr + TP_STACK_SIZE;
|
|
tpstacktop = tstack_ptr;
|
|
tp_pointer = NULL;
|
|
}
|
|
|
|
/* Add a new tp_frame in the TP stack */
|
|
DBGRFCT((stderr, "\n*** op_tstart: *** Entering $TLEVEL = %d\n", dollar_tlevel + 1));
|
|
tf = (tp_frame *)(tp_sp -= SIZEOF(tp_frame));
|
|
assert((unsigned char *)tf > tpstacktop); /* Block should lie entirely within tp stack area */
|
|
tf->dlr_t = dollar_truth;
|
|
tf->restart_pc = fp->mpc;
|
|
tf->restart_ctxt = fp->ctxt;
|
|
tf->fp = fp;
|
|
tf->serial = serial;
|
|
tf->trans_id = *tid;
|
|
tf->restartable = (NORESTART != prescnt);
|
|
tf->old_locks = (NULL != mlk_pvt_root);
|
|
tf->orig_gv_target = gv_target;
|
|
DBG_CHECK_GVTARGET_CSADDRS_IN_SYNC;
|
|
# ifdef DEBUG
|
|
if (!jgbl.forw_phase_recovery)
|
|
{ /* In case of forward phase of journal recovery, gv_currkey is set by caller (mur_forward) only
|
|
* after the call to op_tstart so avoid doing gv_currkey check.
|
|
*/
|
|
DBG_CHECK_GVTARGET_GVCURRKEY_IN_SYNC;
|
|
}
|
|
# endif
|
|
/* If the TP structures have not yet been initialized, do that now. */
|
|
if (NULL == TREF(gv_tporigkey_ptr))
|
|
{ /* This need only be set once */
|
|
TREF(gv_tporigkey_ptr) = (gv_orig_key_array *)malloc(SIZEOF(gv_orig_key_array));
|
|
memset(TREF(gv_tporigkey_ptr), 0, SIZEOF(gv_orig_key_array));
|
|
}
|
|
tf->orig_key = (gv_key *)&((TREF(gv_tporigkey_ptr))->gv_orig_key[dollar_tlevel][0]);
|
|
assert(NULL != gv_currkey);
|
|
MEMCPY_KEY(tf->orig_key, gv_currkey);
|
|
tf->gd_header = gd_header;
|
|
tf->gd_reg = gv_cur_region;
|
|
tf->zgbldir = dollar_zgbldir;
|
|
tf->mvc = mv_st_ent;
|
|
tf->sym = curr_symval;
|
|
tf->tp_save_all_flg = FALSE;
|
|
if (NULL == tp_pointer)
|
|
{
|
|
tf->implicit_tstart = implicit_tstart;
|
|
GTMTRIG_ONLY(tf->implicit_trigger = implicit_trigger);
|
|
} else
|
|
{
|
|
tf->implicit_tstart = tp_pointer->implicit_tstart;
|
|
GTMTRIG_ONLY(tf->implicit_trigger = tp_pointer->implicit_trigger);
|
|
}
|
|
GTMTRIG_ONLY(tf->cannot_commit = FALSE;)
|
|
tf->vars = (tp_var *)NULL;
|
|
tf->old_tp_frame = tp_pointer;
|
|
tp_pointer = tf;
|
|
if (prescnt > 0)
|
|
{
|
|
VAR_COPY(lvname, varlst);
|
|
for (pres = 0; pres < prescnt; ++pres)
|
|
{
|
|
preserve = va_arg(lvname, mval *);
|
|
/* Note: the assumption (according to the comment below) is that this mval points into the literal table
|
|
and thus could not possibly be undefined. In that case, I do not understand why the earlier loop to
|
|
do MV_FORCE_STR on these variables. Future todo -- verify if that loop is needed. On the assumption
|
|
that it is not, the below assert will verify that the mval is defined to catch any NOUNDEF case.
|
|
*/
|
|
assert(MV_DEFINED(preserve));
|
|
/* The incoming 'preserve' is the pointer to a literal mval table entry. For the indirect code
|
|
* (eg. Direct Mode), since the literal table is no longer on the M stack, we should not shift
|
|
* the incoming va_arg pointer (C9D01-002205) */
|
|
mvname = preserve;
|
|
if (0 != mvname->str.len)
|
|
{ /* Convert mval to mident and see if it's in the symbol table */
|
|
tpvent.var_name.len = mvname->str.len;
|
|
tpvent.var_name.addr = mvname->str.addr;
|
|
COMPUTE_HASH_MNAME(&tpvent);
|
|
tpvent.marked = FALSE;
|
|
if (add_hashtab_mname_symval(&curr_symval->h_symtab, &tpvent, NULL, &tabent))
|
|
lv_newname(tabent, curr_symval);
|
|
lv = (lv_val *)tabent->value;
|
|
assert(lv);
|
|
assert(LV_IS_BASE_VAR(lv));
|
|
assert(0 < lv->stats.trefcnt);
|
|
assert(lv->stats.crefcnt <= lv->stats.trefcnt);
|
|
/* In order to allow restart of a sub-transaction, this should chain rather than back stop,
|
|
with appropriate changes to lv_var_clone and tp_unwind */
|
|
if (NULL == lv->tp_var)
|
|
{
|
|
TP_SAVE_RESTART_VAR(lv, tf, &tabent->key);
|
|
if (LV_HAS_CHILD(lv))
|
|
TPSAV_CNTNRS_IN_TREE(lv);
|
|
} else
|
|
{ /* We have saved this var previously. But check if it got saved via a container var
|
|
and therefore has no name associated with it. If so, update the key in the tp_var
|
|
structure so it gets its name restored properly if necessary.
|
|
*/
|
|
if (0 == lv->tp_var->key.var_name.len)
|
|
lv->tp_var->key = tabent->key;
|
|
}
|
|
}
|
|
}
|
|
va_end(lvname);
|
|
} else if (ALLLOCAL == prescnt)
|
|
{ /* Preserve all variables */
|
|
tf->tp_save_all_flg = TRUE;
|
|
++curr_symval->tp_save_all;
|
|
for (curent = curr_symval->h_symtab.base, topent = curr_symval->h_symtab.top; curent < topent; curent++)
|
|
{
|
|
if (HTENT_VALID_MNAME(curent, lv_val, lv) && ('$' != *curent->key.var_name.addr))
|
|
{
|
|
assert(lv);
|
|
assert(LV_IS_BASE_VAR(lv));
|
|
assert(0 < lv->stats.trefcnt);
|
|
assert(lv->stats.crefcnt <= lv->stats.trefcnt);
|
|
if (NULL == lv->tp_var)
|
|
{
|
|
TP_SAVE_RESTART_VAR(lv, tf, &curent->key);
|
|
if (LV_HAS_CHILD(lv))
|
|
TPSAV_CNTNRS_IN_TREE(lv);
|
|
} else
|
|
{ /* We have saved this var previously. But check if it got saved via a container var
|
|
and therefore has no name associated with it. If so, update the key in the tp_var
|
|
structure so it gets its name restored properly if necessary.
|
|
*/
|
|
if (0 == lv->tp_var->key.var_name.len)
|
|
lv->tp_var->key = curent->key;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
va_end(varlst);
|
|
/* Store existing state of locks */
|
|
for (pre_lock = mlk_pvt_root; NULL != pre_lock; pre_lock = pre_lock->next)
|
|
{
|
|
if ((NULL == pre_lock->tp) || (pre_lock->tp->level != pre_lock->level)
|
|
|| (pre_lock->tp->zalloc != pre_lock->zalloc))
|
|
{ /* Only stack locks that have changed since last TSTART */
|
|
lck_tp = (mlk_tp *)malloc(SIZEOF(mlk_tp));
|
|
lck_tp->tplevel = dollar_tlevel;
|
|
lck_tp->level = pre_lock->level;
|
|
lck_tp->zalloc = pre_lock->zalloc;
|
|
lck_tp->next = pre_lock->tp;
|
|
pre_lock->tp = lck_tp;
|
|
}
|
|
}
|
|
++dollar_tlevel;
|
|
/* Store the global (across all segments) dollar_tlevel specific information. */
|
|
if (NULL != first_sgm_info) /* database activity existed in prior levels */
|
|
{
|
|
for (prev_gtli = NULL, gtli = global_tlvl_info_head; gtli; gtli = gtli->next_global_tlvl_info)
|
|
prev_gtli = gtli;
|
|
new_gtli = (global_tlvl_info *)get_new_element(global_tlvl_info_list, 1);
|
|
new_gtli->global_tlvl_fence_info = jnl_fence_ctl.fence_list;
|
|
new_gtli->t_level = dollar_tlevel;
|
|
GTMTRIG_ONLY(new_gtli->tlvl_prev_ztworm_ptr = jgbl.prev_ztworm_ptr;)
|
|
new_gtli->tlvl_cumul_jrec_len = jgbl.cumul_jnl_rec_len;
|
|
DEBUG_ONLY(new_gtli->tlvl_cumul_index = jgbl.cumul_index;)
|
|
new_gtli->tlvl_tp_ztp_jnl_upd_num = jgbl.tp_ztp_jnl_upd_num;
|
|
/* Store current state of update_array global variables */
|
|
assert((NULL != first_ua) && (NULL != curr_ua)); /* Since first_sgm_info is NOT NULL, database activity existed */
|
|
new_gtli->curr_ua = (struct ua_list *)(curr_ua);
|
|
new_gtli->upd_array_ptr = update_array_ptr;
|
|
new_gtli->next_global_tlvl_info = NULL;
|
|
if (prev_gtli)
|
|
{
|
|
assert(prev_gtli->t_level + 1 == dollar_tlevel);
|
|
prev_gtli->next_global_tlvl_info = new_gtli;
|
|
} else
|
|
global_tlvl_info_head = new_gtli;
|
|
}
|
|
/* Store the dollar_tlevel specific information */
|
|
for (si = first_sgm_info; NULL != si; si = si->next_sgm_info)
|
|
{
|
|
for (prev_tli = NULL, tli = si->tlvl_info_head; (NULL != tli); tli = tli->next_tlevel_info)
|
|
prev_tli = tli;
|
|
new_tli = (tlevel_info *)get_new_element(si->tlvl_info_list, 1);
|
|
new_tli->t_level = dollar_tlevel;
|
|
for (prev_ks = NULL, ks = si->kill_set_head; ks; ks = ks->next_kill_set)
|
|
prev_ks = ks;
|
|
new_tli->tlvl_kill_set = prev_ks;
|
|
if (prev_ks)
|
|
new_tli->tlvl_kill_used = prev_ks->used;
|
|
else
|
|
new_tli->tlvl_kill_used = 0;
|
|
new_tli->tlvl_tp_hist_info = si->last_tp_hist;
|
|
/* Prepare for journaling logical records if journaling is enabled on this region OR if replication was
|
|
* allowed on this region (this is a case where replication was ON originally but later transitioned
|
|
* into WAS_ON state and journaling got turned OFF.
|
|
*/
|
|
csa = si->tp_csa;
|
|
if (JNL_WRITE_LOGICAL_RECS(csa))
|
|
{
|
|
assert((NULL != si->jnl_head) || (NULL == csa->next_fenced));
|
|
assert((NULL == si->jnl_head) || (NULL != csa->next_fenced));
|
|
assert((NULL == csa->next_fenced) || (JNL_FENCE_LIST_END == csa->next_fenced)
|
|
|| (NULL != csa->next_fenced->sgm_info_ptr->jnl_head));
|
|
assert(NULL == *si->jnl_tail);
|
|
SET_PREV_JFB(si, prev_jfb);
|
|
new_tli->tlvl_jfb_info = prev_jfb;
|
|
assert(NULL != si->jnl_list);
|
|
assert(NULL != si->format_buff_list);
|
|
new_tli->jnl_list_elems = si->jnl_list->nElems;
|
|
new_tli->jfb_list_elems = si->format_buff_list->nElems;
|
|
}
|
|
new_tli->next_tlevel_info = NULL;
|
|
new_tli->update_trans = si->update_trans;
|
|
if (prev_tli)
|
|
prev_tli->next_tlevel_info = new_tli;
|
|
else
|
|
si->tlvl_info_head = new_tli;
|
|
}
|
|
/* If starting first TP level, also start TP timer if set to non-default value */
|
|
assert(0 <= TREF(dollar_zmaxtptime));
|
|
if ((0 < TREF(dollar_zmaxtptime)) && (1 == dollar_tlevel))
|
|
(*tp_timeout_start_timer_ptr)(TREF(dollar_zmaxtptime));
|
|
DBGRFCT((stderr, "\nop_tstart: complete\n"));
|
|
}
|