199 lines
8.6 KiB
C
199 lines
8.6 KiB
C
/****************************************************************
|
|
* *
|
|
* Copyright 2010, 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"
|
|
|
|
#ifdef GTM_TRIGGER
|
|
|
|
#include "gdsroot.h" /* for gdsfhead.h */
|
|
#include "gdsbt.h" /* for gdsfhead.h */
|
|
#include "gdsfhead.h"
|
|
#include "gvcst_protos.h"
|
|
#include <rtnhdr.h>
|
|
#include "gv_trigger.h"
|
|
#include "trigger.h"
|
|
#include "mv_stent.h" /* for COPY_SUBS_TO_GVCURRKEY macro */
|
|
#include "gvsub2str.h" /* for COPY_SUBS_TO_GVCURRKEY */
|
|
#include "format_targ_key.h" /* for COPY_SUBS_TO_GVCURRKEY */
|
|
#include "targ_alloc.h" /* for SETUP_TRIGGER_GLOBAL & SWITCH_TO_DEFAULT_REGION */
|
|
#include "filestruct.h" /* for INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED (FILE_INFO) */
|
|
#include "mvalconv.h"
|
|
#include "gdscc.h" /* needed for tp.h */
|
|
#include "gdskill.h" /* needed for tp.h */
|
|
#include "buddy_list.h" /* needed for tp.h */
|
|
#include "hashtab_int4.h" /* needed for tp.h */
|
|
#include "jnl.h" /* needed for tp.h */
|
|
#include "tp.h" /* for sgm_info */
|
|
#include "tp_frame.h"
|
|
#include "tp_restart.h"
|
|
#include "tp_set_sgm.h"
|
|
#include "t_retry.h"
|
|
#include "op.h"
|
|
#include "op_tcommit.h"
|
|
#include "memcoherency.h"
|
|
#include "gtmimagename.h"
|
|
#include "trigger_fill_xecute_buffer.h"
|
|
#include "trigger_gbl_fill_xecute_buffer.h"
|
|
|
|
GBLREF sgmnt_data_ptr_t cs_data;
|
|
GBLREF sgmnt_addrs *cs_addrs;
|
|
GBLREF gd_addr *gd_header;
|
|
GBLREF gv_key *gv_currkey;
|
|
GBLREF gd_region *gv_cur_region;
|
|
GBLREF sgm_info *sgm_info_ptr;
|
|
GBLREF gv_namehead *gv_target;
|
|
GBLREF boolean_t skip_INVOKE_RESTART;
|
|
GBLREF int tprestart_state;
|
|
GBLREF int4 tstart_trigger_depth;
|
|
GBLREF int4 gtm_trigger_depth;
|
|
GBLREF tp_frame *tp_pointer;
|
|
|
|
LITREF mval literal_hasht;
|
|
|
|
error_def(ERR_TRIGNAMBAD);
|
|
error_def(ERR_TPRETRY);
|
|
error_def(ERR_GVGETFAIL);
|
|
|
|
STATICFNDCL CONDITION_HANDLER(trigger_fill_xecute_buffer_ch);
|
|
STATICFNDCL void trigger_fill_xecute_buffer_read_trigger_source(gv_trigger_t *trigdsc);
|
|
|
|
/* Similar condition handler to above without the tp-restart - just unwind and let caller do the restart */
|
|
CONDITION_HANDLER(trigger_fill_xecute_buffer_ch)
|
|
{
|
|
START_CH;
|
|
if ((int)ERR_TPRETRY == SIGNAL)
|
|
{
|
|
UNWIND(NULL, NULL);
|
|
}
|
|
NEXTCH;
|
|
}
|
|
|
|
int trigger_fill_xecute_buffer(gv_trigger_t *trigdsc)
|
|
{
|
|
int src_fetch_status;
|
|
|
|
assert(!dollar_tlevel || (tstart_trigger_depth <= gtm_trigger_depth));
|
|
/* We have 3 cases to consider - all of which REQUIRE a TP fence to already be in effect. The reason for this is, if we
|
|
* detect a restartable condition, we are going to cause this region's triggers to be unloaded which destroys the block
|
|
* our parameter is pointing to so the restart logic MUST take place outside of this routine.
|
|
*
|
|
* 1. We have an active transaction due to an IMPLICIT TSTART done by trigger handling but the trigger level has not yet
|
|
* been created. We don't need another TP wrapper in this case but we do need a condition handler to trap the thrown
|
|
* retry to again prevent C stack unwind and return to the caller in the same shape that gtm_trigger would return.
|
|
* 2. We have an active transaction due to an EXPLICIT M-code TSTART command. For this case, the trigger loads proceed
|
|
* as normal with restarts handled in the regular automatic fashion. Note this case also covers the tp restarts done
|
|
* by both the update process and mupip recover forward since those functions have their own way of intercepting and
|
|
* dealing with restarts. To cover those cases, tp->implicit_tstart can be TRUE but tp_implicit_trigger MUST be
|
|
* FALSE.
|
|
* 3. We have an active transaction due to an IMPLICIT TSTART done by trigger handling and one or more triggers are
|
|
* running. This becomes like case 2 since the restart will be handled by gtm_trigger and the proper thing will
|
|
* be done.
|
|
*
|
|
* An extra note about case 3. Case 3 can be the identified case if in a nested trigger we are in trigger-no-mans-land
|
|
* with a base frame for the nested trigger (having driven one of a set of parallel nested triggers) but no actual trigger
|
|
* execution frame yet exists. This is really a case 1 situation with a nested trigger but it turns out that dealing with
|
|
* like case 3 does the right thing because if/when mdb_condition_handler catches a thrown TPRETRY error, mdb_condition
|
|
* handler will peal the nested trigger frame off before doing the restart which works for us and avoids issues of
|
|
* multi-level implicit restarts we would otherwise have to handle.
|
|
*
|
|
* Note, this routine is for loading trigger source when the trigger is to be driven. The trigger_source_read_andor_verify()
|
|
* routine should be used when fetching trigger source for reasons other than driving the triggers. This routine is lighter
|
|
* weight but has a dependence on the restartability of the trigger-drive logic for getting the triggers reloaded as
|
|
* necessary.
|
|
*/
|
|
if (0 < dollar_tlevel)
|
|
{
|
|
if (!tp_pointer->implicit_trigger /* Case 2 */
|
|
|| (tp_pointer->implicit_tstart && tp_pointer->implicit_trigger
|
|
&& (tstart_trigger_depth != gtm_trigger_depth))) /* Case 3 */
|
|
{ /* Test for Case 3/4 where we get to do very little: */
|
|
assert((!tp_pointer->implicit_trigger) || (0 < gtm_trigger_depth));
|
|
trigger_fill_xecute_buffer_read_trigger_source(trigdsc);
|
|
} else
|
|
{ /* Test for Case 1 where we only need a condition handler */
|
|
assert(tp_pointer->implicit_tstart && tp_pointer->implicit_trigger);
|
|
assert(tstart_trigger_depth == gtm_trigger_depth);
|
|
ESTABLISH_RET(trigger_fill_xecute_buffer_ch, SIGNAL);
|
|
trigger_fill_xecute_buffer_read_trigger_source(trigdsc);
|
|
REVERT;
|
|
}
|
|
} else
|
|
GTMASSERT;
|
|
/* return our bounty to caller */
|
|
trigdsc->xecute_str.mvtype = MV_STR;
|
|
return 0; /* Could return ERR_TPRETRY if return is via our condition handler */
|
|
}
|
|
|
|
/* Workhorse of fetching source for given trigger.
|
|
*/
|
|
STATICFNDEF void trigger_fill_xecute_buffer_read_trigger_source(gv_trigger_t *trigdsc)
|
|
{
|
|
mname_entry gvent;
|
|
enum cdb_sc cdb_status;
|
|
int4 index;
|
|
mstr gbl, xecute_buff;
|
|
mval trig_index;
|
|
sgmnt_addrs *csa;
|
|
sgmnt_data_ptr_t csd;
|
|
gvt_trigger_t *gvt_trigger;
|
|
gv_namehead *gvt;
|
|
gv_namehead *hasht_tree, *save_gv_target;
|
|
gv_key *save_gv_currkey;
|
|
gd_region *save_gv_cur_region;
|
|
sgm_info *save_sgm_info_ptr;
|
|
char save_currkey[SIZEOF(gv_key) + DBKEYSIZE(MAX_KEY_SZ)];
|
|
|
|
assert(0 < dollar_tlevel);
|
|
assert(NULL != trigdsc);
|
|
SAVE_TRIGGER_REGION_INFO;
|
|
|
|
gvt_trigger = trigdsc->gvt_trigger; /* We now know our base block now */
|
|
index = trigdsc - gvt_trigger->gv_trig_array + 1; /* We now know our trigger index value */
|
|
i2mval(&trig_index, index);
|
|
gvt = gv_target = gvt_trigger->gv_target; /* gv_target contains global name */
|
|
gbl.addr = gvt->gvname.var_name.addr;
|
|
gbl.len = gvt->gvname.var_name.len;
|
|
/* Our situation is that while our desired gv_target has csa information, we don't know specifically
|
|
* which global directory was in use so we can't run gv_bind_name() lest we find the given global
|
|
* name in the wrong global directory thus running the wrong triggers. But we know this target is
|
|
* properly formed since it had to be when it was recorded when the triggers were loaded. Because of
|
|
* that, we can get the correct csa and gv_target and csa-region will point us to a region that will
|
|
* work even if it isn't exactly the one we used to get to this trigger.
|
|
*/
|
|
TP_CHANGE_REG_IF_NEEDED(gvt->gd_csa->region);
|
|
csa = cs_addrs;
|
|
csd = csa->hdr;
|
|
assert(csd == cs_data);
|
|
tp_set_sgm();
|
|
/* See if we need to reload our triggers */
|
|
if ((csa->db_trigger_cycle != gvt->db_trigger_cycle)
|
|
|| (csa->db_dztrigger_cycle && (gvt->db_dztrigger_cycle != csa->db_dztrigger_cycle)))
|
|
{ /* The process' view of the triggers could be potentially stale. Restart to be safe.
|
|
* Triggers can be invoked only by GT.M and Update process. Out of these, we expect only
|
|
* GT.M to see restarts due to concurrent trigger changes. Update process is the only
|
|
* updater on the secondary so we dont expect it to see any concurrent trigger changes
|
|
* Assert accordingly.
|
|
*/
|
|
assert(CDB_STAGNATE > t_tries);
|
|
assert(IS_GTM_IMAGE);
|
|
t_retry(cdb_sc_triggermod);
|
|
}
|
|
SETUP_TRIGGER_GLOBAL;
|
|
INITIAL_HASHT_ROOT_SEARCH_IF_NEEDED;
|
|
assert(0 == trigdsc->xecute_str.str.len); /* Make sure not replacing/losing a buffer */
|
|
xecute_buff.addr = trigger_gbl_fill_xecute_buffer(gbl.addr, gbl.len, &trig_index, NULL, (int4 *)&xecute_buff.len);
|
|
trigdsc->xecute_str.str = xecute_buff;
|
|
/* Restore gv_target/gv_currkey which need to be kept in sync */
|
|
RESTORE_TRIGGER_REGION_INFO;
|
|
return;
|
|
}
|
|
#endif /* GTM_TRIGGER */
|