fis-gtm/sr_port/have_crit.h

156 lines
6.2 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. *
* *
****************************************************************/
#ifndef HAVE_CRIT_H_INCLUDED
#define HAVE_CRIT_H_INCLUDED
#include <signal.h> /* needed for VSIG_ATOMIC_T */
#ifdef UNIX
#include <deferred_signal_handler.h>
#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 */