/**************************************************************** * * * 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. * * * ****************************************************************/ #ifndef HAVE_CRIT_H_INCLUDED #define HAVE_CRIT_H_INCLUDED #include /* needed for VSIG_ATOMIC_T */ #ifdef UNIX #include #endif /* states of CRIT passed as argument to have_crit() */ #define CRIT_IN_COMMIT 0x00000001 #define CRIT_NOT_TRANS_REG 0x00000002 #define CRIT_RELEASE 0x00000004 #define CRIT_ALL_REGIONS 0x00000008 #define CRIT_IN_WTSTART 0x00000010 /* check if csa->in_wtstart is true */ /* Note absence of any flags is default value which finds if any region * or the replication pool have crit or are getting crit. It returns * when one is found without checking further. */ #define CRIT_HAVE_ANY_REG 0x00000000 #ifdef DEBUG #include "wbox_test_init.h" #endif #ifdef UNIX #include "gt_timer.h" #endif typedef enum { INTRPT_OK_TO_INTERRUPT = 0, INTRPT_IN_GTCMTR_TERMINATE, INTRPT_IN_TP_UNWIND, INTRPT_IN_TP_CLEAN_UP, INTRPT_IN_CRYPT_SECTION, INTRPT_IN_DB_CSH_GETN, INTRPT_IN_GVCST_INIT, INTRPT_IN_GDS_RUNDOWN, INTRPT_IN_SS_INITIATE, INTRPT_IN_ZLIB_CMP_UNCMP, INTRPT_IN_TRIGGER_NOMANS_LAND, /* State where have trigger base frame but no trigger (exec) frame */ INTRPT_IN_MUR_OPEN_FILES, INTRPT_IN_TRUNC, INTRPT_IN_SET_NUM_ADD_PROCS, INTRPT_IN_SYSCONF, INTRPT_NO_TIMER_EVENTS, /* State where primary reason for deferral is to avoid timer pops */ INTRPT_IN_FFLUSH, /* Deferring interrupts during fflush */ INTRPT_IN_SHMDT, /* Deferring interrupts during SHMDT */ INTRPT_IN_WAIT_FOR_DISK_SPACE, /* Deferring interrupts during wait_for_disk_space.c */ INTRPT_NUM_STATES /* Should be the *last* one in the enum */ } intrpt_state_t; GBLREF intrpt_state_t intrpt_ok_state; GBLREF boolean_t deferred_timers_check_needed; /* Macro to check if we are in a state that is ok to interrupt (or to do deferred signal handling). * We do not want to interrupt if the global variable intrpt_ok_state indicates it is not ok to interrupt, * if we are in the midst of a malloc, if we are holding crit, if we are in the midst of commit, or in * wcs_wtstart. In the last case, we could be causing another process HOLDING CRIT on the region to wait * in bg_update_phase1 if we hold the write interlock. Hence it is important for us to finish that as soon * as possible and not interrupt it. */ #define OK_TO_INTERRUPT ((INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) && (0 == gtmMallocDepth) \ && (0 == have_crit(CRIT_HAVE_ANY_REG | CRIT_IN_COMMIT | CRIT_IN_WTSTART))) /* Macro to be used whenever we want to handle any signals that we deferred handling and exit in the process. * In VMS, we dont do any signal handling, only exit handling. */ #define DEFERRED_EXIT_HANDLING_CHECK \ { \ VMS_ONLY(GBLREF int4 exi_condition;) \ GBLREF int process_exiting; \ GBLREF VSIG_ATOMIC_T forced_exit; \ GBLREF volatile int4 gtmMallocDepth; \ \ if (forced_exit) \ { \ if (!process_exiting && OK_TO_INTERRUPT) \ { \ UNIX_ONLY(deferred_signal_handler();) \ VMS_ONLY(sys$exit(exi_condition);) \ } \ } \ UNIX_ONLY( \ else if (deferred_timers_check_needed) \ { \ if (!process_exiting && OK_TO_INTERRUPT) \ check_for_deferred_timers(); \ } \ ) \ } /* Macro to cause deferrable interrupts to be deferred recording the cause. * If interrupt is already deferred, state is not changed. * * The normal usage of the below macros is * DEFER_INTERRUPTS * non-interruptible code * ENABLE_INTERRUPTS * We want the non-interruptible code to be executed AFTER the SAVE_INTRPT_OK_STATE macro. * To enforce this ordering, one would think a read memory barrier is needed in between. * But it is not needed. This is because we expect the non-interruptible code to have * a) pointer dereferences OR * b) function calls * Either of these will prevent the compiler from reordering the non-interruptible code. * Any non-interruptible code that does not have either of the above usages (for e.g. uses C global * variables) might be affected by compiler reordering. As of now, there is no known case of such * usage and no such usage is anticipated in the future. * * We dont need to worry about machine reordering as well since there is no shared memory variable * involved here (intrpt_ok_state is a process private variable) and even if any reordering occurs * they will all be in-flight instructions when the interrupt occurs so the hardware will guarantee * all such instructions are completely done or completely discarded before servicing the interrupt * which means the interrupt service routine will never see a reordered state of the above code. */ #define DEFER_INTERRUPTS(NEWSTATE) \ { \ if (INTRPT_OK_TO_INTERRUPT == intrpt_ok_state) \ /* Only reset state if we are in "OK" state */ \ intrpt_ok_state = NEWSTATE; \ else \ assert((NEWSTATE) != intrpt_ok_state); /* Make sure not nesting same code */ \ } /* Re-enable deferrable interrupts if the expected state is found. If expected state is not found, then * we must have nested interrupt types. Avoid state changes in that case. When the nested state pops, * interrupts will be restored. */ #define ENABLE_INTERRUPTS(OLDSTATE) \ { \ assert(((OLDSTATE) == intrpt_ok_state) || (INTRPT_OK_TO_INTERRUPT != intrpt_ok_state)); \ if ((OLDSTATE) == intrpt_ok_state) \ { /* Only reset state if in expected state - othwise state must be non-zero which is \ * asserted above. \ */ \ intrpt_ok_state = INTRPT_OK_TO_INTERRUPT; \ DEFERRED_EXIT_HANDLING_CHECK; /* check if signals were deferred while held lock */ \ } \ } uint4 have_crit(uint4 crit_state); #endif /* HAVE_CRIT_H_INCLUDED */