void tp_unwind(uint4 newlevel, enum tp_unwind_invocation invocation_type, int *tprestart_rc) { mlk_pvtblk **prior, *mlkp; mlk_tp *oldlock, *nextlock; int tl; lv_val *save_lv, *curr_lv, *lv; tp_var *restore_ent; mv_stent *mvc; boolean_t restore_lv, rollback_locks; lvscan_blk *lvscan, *lvscan_next, first_lvscan; int elemindx, rc; lvTree *lvt_child; /* We are about to clean up structures. Defer MUPIP STOP/signal handling until function end. */ DEFER_INTERRUPTS(INTRPT_IN_TP_UNWIND); /* Unwind the requested TP levels */ # if defined(DEBUG_REFCNT) || defined(DEBUG_ERRHND) DBGFPF((stderr, "\ntp_unwind: Beginning TP unwind process\n")); # endif restore_lv = (RESTART_INVOCATION == invocation_type); lvscan = &first_lvscan; lvscan->next = NULL; lvscan->elemcnt = 0; assert((tp_sp <= tpstackbase) && (tp_sp > tpstacktop)); assert((tp_pointer <= (tp_frame *)tpstackbase) && (tp_pointer > (tp_frame *)tpstacktop)); for (tl = dollar_tlevel; tl > newlevel; --tl) { DBGRFCT((stderr, "\ntp_unwind: Unwinding level %d -- tp_pointer: 0x"lvaddr"\n", tl, tp_pointer)); assertpro(NULL != tp_pointer); for (restore_ent = tp_pointer->vars; NULL != restore_ent; restore_ent = tp_pointer->vars) { /*********************************************************************************/ /* TP_VAR_CLONE sets the var_cloned flag, showing that the tree has been cloned */ /* If var_cloned is not set, it shows that curr_lv and save_lv are still sharing */ /* the tree, so it should not be killed. */ /*********************************************************************************/ curr_lv = restore_ent->current_value; save_lv = restore_ent->save_value; assert(curr_lv); assert(save_lv); assert(LV_IS_BASE_VAR(curr_lv)); assert(LV_IS_BASE_VAR(save_lv)); assert(0 < curr_lv->stats.trefcnt); assert(curr_lv->tp_var); assert(curr_lv->tp_var == restore_ent); /* In order to restart sub-transactions, this would have to maintain * the chain that currently is not built by op_tstart() */ if (restore_lv) { rc = tp_unwind_restlv(curr_lv, save_lv, restore_ent, NULL, tprestart_rc); # ifdef GTM_TRIGGER if (0 != rc) { dollar_tlevel = tl; /* Record fact if we unwound some tp_frames */ ENABLE_INTERRUPTS(INTRPT_IN_TP_UNWIND); /* drive any MUPIP STOP/signals deferred * while in this function */ TPUNWND_WBOX_TEST; /* Debug-only wbox-test to simulate SIGTERM */ INVOKE_RESTART; } # endif } else if (restore_ent->var_cloned) { /* curr_lv has been cloned. * Note: LV_CHILD(save_lv) can be non-NULL only if restore_ent->var_cloned is TRUE */ DBGRFCT((stderr, "\ntp_unwind: Not restoring curr_lv and is cloned\n")); lvt_child = LV_GET_CHILD(save_lv); if (NULL != lvt_child) { /* If subtree exists, we have to blow away the cloned tree */ DBGRFCT((stderr, "\ntp_unwind: save_lv has children\n")); assert(save_lv->tp_var); DBGRFCT((stderr,"\ntp_unwind: For lv_val 0x"lvaddr": Deleting saved lv_val 0x"lvaddr"\n", curr_lv, save_lv)); assert(LVT_PARENT(lvt_child) == (lvTreeNode *)save_lv); lv_kill(save_lv, DOTPSAVE_FALSE, DO_SUBTREE_TRUE); } restore_ent->var_cloned = FALSE; } else { /* If not cloned, we still have to reduce the reference counts of any * container vars in the untouched tree that were added to keep anything * they referenced from disappearing. */ DBGRFCT((stderr, "\ntp_unwind: Not restoring curr_lv and is NOT cloned\n")); lvt_child = LV_GET_CHILD(curr_lv); if (NULL != lvt_child) { DBGRFCT((stderr, "\ntp_unwind: curr_lv has children and so reducing ref counts\n")); TPUNWND_CNTNRS_IN_TREE(curr_lv); } } LV_FREESLOT(save_lv); /* Not easy to predict what the trefcnt will be except that it should be greater than zero. In * most cases, it will have its own hash table ref plus the extras we added but it is also * possible that the entry has been kill *'d in which case the ONLY ref that will be left is * our own increment but there is no [quick] way to distinguish this case so we just * test for > 0. */ assert(0 < curr_lv->stats.trefcnt); assert(0 < curr_lv->stats.crefcnt); DECR_CREFCNT(curr_lv); /* Remove the copy refcnt we added in in op_tstart() or lv_newname() */ DECR_BASE_REF_NOSYM(curr_lv, FALSE); curr_lv->tp_var = NULL; tp_pointer->vars = restore_ent->next; free(restore_ent); } if ((tp_pointer->fp == frame_pointer) && (MVST_TPHOLD == mv_chain->mv_st_type) && (msp == (unsigned char *)mv_chain)) POP_MV_STENT(); if (NULL == tp_pointer->old_tp_frame) tp_sp = tpstackbase; else tp_sp = (unsigned char *)tp_pointer->old_tp_frame; if (tp_sp > tpstackbase) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKUNDERFLO); if (tp_pointer->tp_save_all_flg) --tp_pointer->sym->tp_save_all; if ((NULL != (tp_pointer = tp_pointer->old_tp_frame)) /* Note assignment */ && ((tp_pointer < (tp_frame *)tp_sp) || (tp_pointer > (tp_frame *)tpstackbase) || (tp_pointer < (tp_frame *)tpstacktop))) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) ERR_STACKUNDERFLO); } if ((0 != newlevel) && restore_lv) { /* Restore current context (without releasing) */ assertpro(NULL != tp_pointer); DBGRFCT((stderr, "\n\n** tp_unwind: Newlevel (%d) != 0 loop processing\n", newlevel)); for (restore_ent = tp_pointer->vars; NULL != restore_ent; restore_ent = restore_ent->next) { curr_lv = restore_ent->current_value; save_lv = restore_ent->save_value; assert(curr_lv); assert(save_lv); assert(LV_IS_BASE_VAR(curr_lv)); assert(LV_IS_BASE_VAR(save_lv)); assert(curr_lv->tp_var); assert(curr_lv->tp_var == restore_ent); assert(0 < curr_lv->stats.trefcnt); rc = tp_unwind_restlv(curr_lv, save_lv, restore_ent, &lvscan, tprestart_rc); # ifdef GTM_TRIGGER if (0 != rc) { dollar_tlevel = tl; /* Record fact if we unwound some levels */ ENABLE_INTERRUPTS(INTRPT_IN_TP_UNWIND); /* drive any MUPIP STOP/signals deferred while * in this function */ TPUNWND_WBOX_TEST; /* Debug-only wbox-test to simulate SIGTERM */ INVOKE_RESTART; } # endif assert(0 < curr_lv->stats.trefcnt); /* Should have its own hash table ref plus the extras we added */ assert(0 < curr_lv->stats.crefcnt); } /* If we have any lv_vals queued up to be scanned for container vars, do that now */ DBGRFCT((stderr, "\ntp_unwind: Starting deferred rescan of lv trees needing refcnt processing\n")); while (0 < lvscan->elemcnt) { assert(ARY_SCNCNTNR_DIM >= lvscan->elemcnt); for (elemindx = 0; lvscan->elemcnt > elemindx; ++elemindx) { lv = lvscan->ary_scncntnr[elemindx]; DBGRFCT((stderr, "\n**tp_unwind_process_lvscan_array: Deferred processing lv 0x"lvaddr"\n", lv)); assert(LV_IS_BASE_VAR(lv)); /* This is the final level being restored so redo the counters on these vars */ TPREST_CNTNRS_IN_TREE(lv); } /* If we allocated any secondary blocks, we are done with them now so release them. Only the * very last block on the chain is the original block that was automatically allocated which * should not be freed in this fashion. */ lvscan_next = lvscan->next; if (NULL != lvscan_next) { /* There is another block on the chain so this one can be freed */ free(lvscan); DBGRFCT((stderr, "\ntp_unwind_process_lvscan_array: Freeing lvscan array\n")); lvscan = lvscan_next; } else { /* Since this is the original block allocated on the C stack which we may reuse, * zero the element count. */ lvscan->elemcnt = 0; DBGRFCT((stderr, "\ntp_unwind_process_lvscan_array: Setting elemcnt to 0 in original " "lvscan block\n")); assert(lvscan == &first_lvscan); } } } assert(0 == lvscan->elemcnt); /* verify no elements queued that were not scanned */ rollback_locks = (COMMIT_INVOCATION != invocation_type); for (prior = &mlk_pvt_root, mlkp = *prior; NULL != mlkp; mlkp = *prior) { if (mlkp->granted) { /* This was a pre-existing lock */ for (oldlock = mlkp->tp; (NULL != oldlock) && ((int)oldlock->tplevel > newlevel); oldlock = nextlock) { /* Remove references to the lock from levels being unwound */ nextlock = oldlock->next; free(oldlock); } if (rollback_locks) { if (NULL == oldlock) { /* Lock did not exist at the tp level being unwound to */ mlk_unlock(mlkp); mlk_pvtblk_delete(prior); continue; } else { /* Lock still exists but restore lock state as it was when the transaction started. */ mlkp->level = oldlock->level; mlkp->zalloc = oldlock->zalloc; } } if ((NULL != oldlock) && (oldlock->tplevel == newlevel)) { /* Remove lock reference from level being unwound to, * now that any {level,zalloc} state information has been restored. */ assert((NULL == oldlock->next) || (oldlock->next->tplevel < newlevel)); mlkp->tp = oldlock->next; /* update root reference pointer */ free(oldlock); } else mlkp->tp = oldlock; /* update root reference pointer */ prior = &mlkp->next; } else mlk_pvtblk_delete(prior); } DBGRFCT((stderr, "tp_unwind: Processing complete\n")); dollar_tlevel = newlevel; ENABLE_INTERRUPTS(INTRPT_IN_TP_UNWIND); /* check if any MUPIP STOP/signals were deferred while in this function */ }
void deferred_signal_handler(void) { void (*signal_routine)(); DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; /* To avoid nested calls to this routine, we set forced_exit to FALSE at the very beginning */ forced_exit = FALSE; if (exit_handler_active) { assert(FALSE); /* at this point in time (June 2003) there is no way we know of to get here, hence the assert */ return; /* since anyway we are exiting currently, resume exit handling instead of reissuing another one */ } /* For signals that get a delayed response so we can get out of crit, we also delay the messages. * This routine will output those delayed messages from the appropriate structures to both the * user and the system console. */ /* note can't use switch here because ERR_xxx are not defined as constants */ if (ERR_KILLBYSIG == forced_exit_err) { send_msg(VARLSTCNT(6) ERR_KILLBYSIG, 4, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal); gtm_putmsg(VARLSTCNT(6) ERR_KILLBYSIG, 4, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal); } else if (ERR_KILLBYSIGUINFO == forced_exit_err) { send_msg(VARLSTCNT(8) ERR_KILLBYSIGUINFO, 6, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.send_pid, signal_info.send_uid); gtm_putmsg(VARLSTCNT(8) ERR_KILLBYSIGUINFO, 6, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.send_pid, signal_info.send_uid); } else if (ERR_KILLBYSIGSINFO1 == forced_exit_err) { send_msg(VARLSTCNT(8) ERR_KILLBYSIGSINFO1, 6, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.int_iadr, signal_info.bad_vadr); gtm_putmsg(VARLSTCNT(8) ERR_KILLBYSIGSINFO1, 6, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.int_iadr, signal_info.bad_vadr); } else if (ERR_KILLBYSIGSINFO2 == forced_exit_err) { send_msg(VARLSTCNT(7) ERR_KILLBYSIGSINFO2, 5, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.int_iadr); gtm_putmsg(VARLSTCNT(7) ERR_KILLBYSIGSINFO2, 5, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.int_iadr); } else if (ERR_KILLBYSIGSINFO3 == forced_exit_err) { send_msg(VARLSTCNT(7) ERR_KILLBYSIGSINFO3, 5, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.bad_vadr); gtm_putmsg(VARLSTCNT(7) ERR_KILLBYSIGSINFO3, 5, GTMIMAGENAMETXT(image_type), process_id, signal_info.signal, signal_info.bad_vadr); } else if (ERR_FORCEDHALT != forced_exit_err || !gtm_quiet_halt) { /* No HALT messages if quiet halt is requested */ send_msg(VARLSTCNT(1) forced_exit_err); gtm_putmsg(VARLSTCNT(1) forced_exit_err); } assert(OK_TO_INTERRUPT); /* Signal intent to exit BEFORE driving condition handlers. This avoids checks that will otherwise fail (for example * if mdb_condition_handler/preemptive_ch gets called below, that could invoke the RESET_GV_TARGET macro which in turn * would assert that gv_target->gd_csa is equal to cs_addrs. This could not be true in case we were in mainline code * that was interrupted by the flush timer for a different region which in turn was interrupted by an external signal * that would drive us to exit. Setting the "process_exiting" variable causes those csa checks to pass. */ SET_PROCESS_EXITING_TRUE; # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_DEFERRED_TIMERS == gtm_white_box_test_case_number) && (2 == gtm_white_box_test_case_count)) { DEFER_INTERRUPTS(INTRPT_NO_TIMER_EVENTS); DBGFPF((stderr, "DEFERRED_SIGNAL_HANDLER: will sleep for 20 seconds\n")); LONG_SLEEP(20); DBGFPF((stderr, "DEFERRED_SIGNAL_HANDLER: done sleeping\n")); ENABLE_INTERRUPTS(INTRPT_NO_TIMER_EVENTS); } # endif /* If any special routines are registered to be driven on a signal, drive them now */ if ((0 != exi_condition) && (NULL != call_on_signal)) { signal_routine = call_on_signal; call_on_signal = NULL; /* So we don't recursively call ourselves */ (*signal_routine)(); } /* Note, we do not drive create_fatal_error zshow_dmp() in this routine since any deferrable signals are * by definition not fatal. */ exit(-exi_condition); }
/* * ------------------------------------------ * Hang the process for a specified time. * * Goes to sleep for a positive value. * Any caught signal will terminate the sleep * following the execution of that signal's catching routine. * * The actual hang duration should be NO LESS than the specified * duration for specified durations greater than .001 seconds. * Certain applications depend on this assumption. * * Arguments: * num - time to sleep * * Return: * none * ------------------------------------------ */ void op_hang(mval* num) { int ms; double tmp; mv_stent *mv_zintcmd; ABS_TIME cur_time, end_time; # ifdef VMS uint4 time[2]; int4 efn_mask, status; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ms = 0; MV_FORCE_NUM(num); if (num->mvtype & MV_INT) { if (0 < num->m[1]) { assert(MV_BIAS >= 1000); /* if formats change overflow may need attention */ ms = num->m[1] * (1000 / MV_BIAS); } } else if (0 == num->sgn) /* if sign is not 0 it means num is negative */ { tmp = mval2double(num) * (double)1000; ms = ((double)MAXPOSINT4 >= tmp) ? (int)tmp : (int)MAXPOSINT4; } if (ms) { if (TREF(tpnotacidtime) * 1000 < ms) TPNOTACID_CHECK(HANGSTR); # if defined(DEBUG) && defined(UNIX) if (WBTEST_ENABLED(WBTEST_DEFERRED_TIMERS) && (3 > gtm_white_box_test_case_count) && (123000 == ms)) { DEFER_INTERRUPTS(INTRPT_NO_TIMER_EVENTS); DBGFPF((stderr, "OP_HANG: will sleep for 20 seconds\n")); LONG_SLEEP(20); DBGFPF((stderr, "OP_HANG: done sleeping\n")); ENABLE_INTERRUPTS(INTRPT_NO_TIMER_EVENTS); return; } if (WBTEST_ENABLED(WBTEST_BREAKMPC)&& (0 == gtm_white_box_test_case_count) && (999 == ms)) { frame_pointer->old_frame_pointer->mpc = (unsigned char *)GTM64_ONLY(0xdeadbeef12345678) NON_GTM64_ONLY(0xdead1234); return; } if (WBTEST_ENABLED(WBTEST_UTIL_OUT_BUFFER_PROTECTION) && (0 == gtm_white_box_test_case_count) && (999 == ms)) { /* Upon seeing a .999s hang this white-box test launches a timer that pops with a period of * UTIL_OUT_SYSLOG_INTERVAL and prints a long message via util_out_ptr. */ start_timer((TID)&util_out_syslog_dump, UTIL_OUT_SYSLOG_INTERVAL, util_out_syslog_dump, 0, NULL); return; } # endif sys_get_curr_time(&cur_time); mv_zintcmd = find_mvstent_cmd(ZINTCMD_HANG, restart_pc, restart_ctxt, FALSE); if (!mv_zintcmd) add_int_to_abs_time(&cur_time, ms, &end_time); else { end_time = mv_zintcmd->mv_st_cont.mvs_zintcmd.end_or_remain; cur_time = sub_abs_time(&end_time, &cur_time); /* get remaing time to sleep */ if (0 <= cur_time.at_sec) ms = (int4)(cur_time.at_sec * 1000 + cur_time.at_usec / 1000); else ms = 0; /* all done */ /* restore/pop previous zintcmd_active[ZINTCMD_HANG] hints */ TAREF1(zintcmd_active, ZINTCMD_HANG).restart_pc_last = mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_pc_prior; TAREF1(zintcmd_active, ZINTCMD_HANG).restart_ctxt_last = mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_ctxt_prior; TAREF1(zintcmd_active, ZINTCMD_HANG).count--; assert(0 <= TAREF1(zintcmd_active, ZINTCMD_HANG).count); if (mv_chain == mv_zintcmd) POP_MV_STENT(); /* just pop if top of stack */ else { /* flag as not active */ mv_zintcmd->mv_st_cont.mvs_zintcmd.command = ZINTCMD_NOOP; mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_pc_check = NULL; } if (0 == ms) return; /* done HANGing */ } # ifdef UNIX if (ms < 10) SLEEP_USEC(ms * 1000, TRUE); /* Finish the sleep if it is less than 10ms. */ else hiber_start(ms); # elif defined(VMS) time[0] = -time_low_ms(ms); time[1] = -time_high_ms(ms) - 1; efn_mask = (1 << efn_outofband | 1 << efn_timer); if (SS$_NORMAL != (status = sys$setimr(efn_timer, &time, NULL, &time, 0))) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("$setimr"), CALLFROM, status); if (SS$_NORMAL != (status = sys$wflor(efn_outofband, efn_mask))) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("$wflor"), CALLFROM, status); if (outofband) { if (SS$_WASCLR == (status = sys$readef(efn_timer, &efn_mask))) { if (SS$_NORMAL != (status = sys$cantim(&time, 0))) rts_error_csa(CSA_ARG(NULL) VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("$cantim"), CALLFROM, status); } else assertpro(SS$_WASSET == status); } # endif } else rel_quant(); if (outofband) { PUSH_MV_STENT(MVST_ZINTCMD); mv_chain->mv_st_cont.mvs_zintcmd.end_or_remain = end_time; mv_chain->mv_st_cont.mvs_zintcmd.restart_ctxt_check = restart_ctxt; mv_chain->mv_st_cont.mvs_zintcmd.restart_pc_check = restart_pc; /* save current information from zintcmd_active */ mv_chain->mv_st_cont.mvs_zintcmd.restart_ctxt_prior = TAREF1(zintcmd_active, ZINTCMD_HANG).restart_ctxt_last; mv_chain->mv_st_cont.mvs_zintcmd.restart_pc_prior = TAREF1(zintcmd_active, ZINTCMD_HANG).restart_pc_last; TAREF1(zintcmd_active, ZINTCMD_HANG).restart_pc_last = restart_pc; TAREF1(zintcmd_active, ZINTCMD_HANG).restart_ctxt_last = restart_ctxt; TAREF1(zintcmd_active, ZINTCMD_HANG).count++; mv_chain->mv_st_cont.mvs_zintcmd.command = ZINTCMD_HANG; outofband_action(FALSE); } return; }
/* * ------------------------------------------ * Hang the process for a specified time. * * Goes to sleep for a positive value. * Any caught signal will terminate the sleep * following the execution of that signal's catching routine. * * Arguments: * num - time to sleep * * Return: * none * ------------------------------------------ */ void op_hang(mval* num) { int ms; mv_stent *mv_zintcmd; ABS_TIME cur_time, end_time; # ifdef VMS uint4 time[2]; int4 efn_mask, status; # endif DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; ms = 0; MV_FORCE_NUM(num); if (num->mvtype & MV_INT) { if (0 < num->m[1]) { assert(MV_BIAS >= 1000); /* if formats change overflow may need attention */ ms = num->m[1] * (1000 / MV_BIAS); } } else if (0 == num->sgn) /* if sign is not 0 it means num is negative */ ms = mval2i(num) * 1000; /* too big to care about fractional amounts */ if (ms) { if (TREF(tpnotacidtime) * 1000 < ms) TPNOTACID_CHECK(HANGSTR); # if defined(DEBUG) && defined(UNIX) if (gtm_white_box_test_case_enabled && (WBTEST_DEFERRED_TIMERS == gtm_white_box_test_case_number) && (3 > gtm_white_box_test_case_count) && (123000 == ms)) { DEFER_INTERRUPTS(INTRPT_NO_TIMER_EVENTS); DBGFPF((stderr, "OP_HANG: will sleep for 20 seconds\n")); LONG_SLEEP(20); DBGFPF((stderr, "OP_HANG: done sleeping\n")); ENABLE_INTERRUPTS(INTRPT_NO_TIMER_EVENTS); return; } if (gtm_white_box_test_case_enabled && (WBTEST_BREAKMPC == gtm_white_box_test_case_number) && (0 == gtm_white_box_test_case_count) && (999 == ms)) { frame_pointer->old_frame_pointer->mpc = (unsigned char *)GTM64_ONLY(0xdeadbeef12345678) NON_GTM64_ONLY(0xdead1234); return; } /* Upon seeing a .999s hang this white-box test launches a timer that pops with a period of UTIL_OUT_SYSLOG_INTERVAL * and prints a long message via util_out_ptr. */ if (gtm_white_box_test_case_enabled && (WBTEST_UTIL_OUT_BUFFER_PROTECTION == gtm_white_box_test_case_number) && (0 == gtm_white_box_test_case_count) && (999 == ms)) { start_timer((TID)&util_out_syslog_dump, UTIL_OUT_SYSLOG_INTERVAL, util_out_syslog_dump, 0, NULL); return; } # endif sys_get_curr_time(&cur_time); mv_zintcmd = find_mvstent_cmd(ZINTCMD_HANG, restart_pc, restart_ctxt, FALSE); if (!mv_zintcmd) add_int_to_abs_time(&cur_time, ms, &end_time); else { end_time = mv_zintcmd->mv_st_cont.mvs_zintcmd.end_or_remain; cur_time = sub_abs_time(&end_time, &cur_time); /* get remaing time to sleep */ if (0 <= cur_time.at_sec) ms = (int4)(cur_time.at_sec * 1000 + cur_time.at_usec / 1000); else ms = 0; /* all done */ /* restore/pop previous zintcmd_active[ZINTCMD_HANG] hints */ TAREF1(zintcmd_active, ZINTCMD_HANG).restart_pc_last = mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_pc_prior; TAREF1(zintcmd_active, ZINTCMD_HANG).restart_ctxt_last = mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_ctxt_prior; TAREF1(zintcmd_active, ZINTCMD_HANG).count--; assert(0 <= TAREF1(zintcmd_active, ZINTCMD_HANG).count); if (mv_chain == mv_zintcmd) POP_MV_STENT(); /* just pop if top of stack */ else { /* flag as not active */ mv_zintcmd->mv_st_cont.mvs_zintcmd.command = ZINTCMD_NOOP; mv_zintcmd->mv_st_cont.mvs_zintcmd.restart_pc_check = NULL; } if (0 == ms) return; /* done HANGing */ } UNIX_ONLY(hiber_start(ms);) VMS_ONLY( time[0] = -time_low_ms(ms); time[1] = -time_high_ms(ms) - 1; efn_mask = (1 << efn_outofband | 1 << efn_timer); if (SS$_NORMAL != (status = sys$setimr(efn_timer, &time, NULL, &time, 0))) rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("$setimr"), CALLFROM, status); if (SS$_NORMAL != (status = sys$wflor(efn_outofband, efn_mask))) rts_error(VARLSTCNT(8) ERR_SYSCALL, 5, RTS_ERROR_LITERAL("$wflor"), CALLFROM, status); ) if (outofband)
/* Timer handler. This is the main handler routine that is being called by the kernel upon receipt * of timer signal. It dispatches to the user handler routine, and removes first timer in a timer * queue. If the queue is not empty, it starts the first timer in the queue. The why parameter is a * no-op in our case, but is required to maintain compatibility with the system type of __sighandler_t, * which is (void*)(int). */ STATICFNDEF void timer_handler(int why) { int4 cmp; GT_TIMER *tpop, *tpop_prev = NULL; ABS_TIME at; sigset_t savemask; int save_errno, timer_defer_cnt, offset; TID *deferred_tid; boolean_t tid_found; char *save_util_outptr; va_list save_last_va_list_ptr; boolean_t util_copy_saved = FALSE; DCL_THREADGBL_ACCESS; SETUP_THREADGBL_ACCESS; # ifdef DEBUG if (IS_GTM_IMAGE) { tpop = find_timer((TID)heartbeat_timer_ptr, &tpop); assert(process_exiting || (((NULL != tpop) && heartbeat_started) || ((NULL == tpop) && !heartbeat_started))); } # endif if (0 < timer_stack_count) return; timer_stack_count++; deferred_timers_check_needed = FALSE; save_errno = errno; timer_active = FALSE; /* timer has popped; system timer not active anymore */ sys_get_curr_time(&at); tpop = (GT_TIMER *)timeroot; timer_defer_cnt = 0; /* reset the deferred timer count, since we are in timer_handler */ SAVE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); while (tpop) /* fire all handlers that expired */ { cmp = abs_time_comp(&at, (ABS_TIME *)&tpop->expir_time); if (cmp < 0) break; /* A timer might pop while we are in the non-zero intrpt_ok_state zone, which could cause collisions. Instead, * we will defer timer events and drive them once the deferral is removed, unless the timer is safe. */ if ((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (FALSE == process_exiting) || tpop->safe) { if (NULL != tpop_prev) tpop_prev->next = tpop->next; else timeroot = tpop->next; if (tpop->safe) { safe_timer_cnt--; assert(0 <= safe_timer_cnt); } if (NULL != tpop->handler) /* if there is a handler, call it */ { # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_DEFERRED_TIMERS == gtm_white_box_test_case_number) && ((void *)tpop->handler != (void*)heartbeat_timer_ptr)) { DBGFPF((stderr, "TIMER_HANDLER: handled a timer\n")); timer_pop_cnt++; } # endif timer_in_handler = TRUE; (*tpop->handler)(tpop->tid, tpop->hd_len, tpop->hd_data); timer_in_handler = FALSE; if (!tpop->safe) /* if safe, avoid a system call */ sys_get_curr_time(&at); /* refresh current time if called a handler */ } tpop->next = (GT_TIMER *)timefree; /* put timer block on the free chain */ timefree = tpop; if (NULL != tpop_prev) tpop = tpop_prev->next; else tpop = (GT_TIMER *)timeroot; num_timers_free++; assert(0 < num_timers_free); } else { timer_defer_cnt++; # ifdef DEBUG if (gtm_white_box_test_case_enabled && (WBTEST_DEFERRED_TIMERS == gtm_white_box_test_case_number)) { if (!deferred_tids) { deferred_tids = (TID *)malloc(SIZEOF(TID) * 2); *deferred_tids = tpop->tid; *(deferred_tids + 1) = -1; DBGFPF((stderr, "TIMER_HANDLER: deferred a timer\n")); } else { tid_found = FALSE; deferred_tid = deferred_tids; while (-1 != *deferred_tid) { if (*deferred_tid == tpop->tid) { tid_found = TRUE; break; } deferred_tid++; } if (!tid_found) { offset = deferred_tid - deferred_tids; deferred_tid = (TID *)malloc((offset + 2) * SIZEOF(TID)); memcpy(deferred_tid, deferred_tids, offset * SIZEOF(TID)); free(deferred_tids); deferred_tids = deferred_tid; *(deferred_tids + offset++) = tpop->tid; *(deferred_tids + offset) = -1; DBGFPF((stderr, "TIMER_HANDLER: deferred a timer\n")); } } } # endif tpop_prev = tpop; tpop = tpop->next; if (0 == safe_timer_cnt) /* no more safe timers left, so quit */ break; } } RESTORE_UTIL_OUT_BUFFER(save_util_outptr, save_last_va_list_ptr, util_copy_saved); if (((FALSE == process_exiting) && (INTRPT_OK_TO_INTERRUPT == intrpt_ok_state)) || (0 < safe_timer_cnt)) start_first_timer(&at); else if ((NULL != timeroot) || (0 < timer_defer_cnt)) deferred_timers_check_needed = TRUE; errno = save_errno; /* restore mainline errno */ timer_stack_count--; }