fis-gtm/sr_port/have_crit.h

198 lines
8.8 KiB
C

/****************************************************************
* *
* Copyright 2001, 2013 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_HAVE_ANY_REG 0x00000001
#define CRIT_IN_COMMIT 0x00000002
#define CRIT_NOT_TRANS_REG 0x00000004
#define CRIT_RELEASE 0x00000008
#define CRIT_ALL_REGIONS 0x00000010
#define CRIT_IN_WTSTART 0x00000020 /* check if csa->in_wtstart is true */
#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_IN_WCS_WTSTART, /* Deferring interrupts until cnl->intent_wtstart is decremented and dbsync timer is
* started */
INTRPT_IN_REFORMAT_BUFFER_USE, /* Deferring interrupts until buffer is reformatted */
INTRPT_IN_X_TIME_FUNCTION, /* Deferring interrupts in non-nesting functions, such as localtime, ctime, and mktime. */
INTRPT_IN_FUNC_WITH_MALLOC, /* Deferring interrupts while in libc- or system functions that do a malloc internally. */
INTRPT_IN_FDOPEN, /* Deferring interrupts in fdopen. */
INTRPT_IN_LOG_FUNCTION, /* Deferring interrupts in openlog, syslog, or closelog. */
INTRPT_IN_FORK_OR_SYSTEM, /* Deferring interrupts in fork or system. */
INTRPT_IN_FSTAT, /* Deferring interrupts in fstat. */
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)))
/* Set the value of forced_exit to 1. This should indicate that we want a deferred signal handler to be invoked first upon leaving
* the current deferred window. Since we do not want forced_exit state to ever regress, and there might be several signals delivered
* within the same deferred window, assert that forced_exit is either 0 or 1 before setting it to 1.
*/
#define SET_FORCED_EXIT_STATE \
{ \
GBLREF VSIG_ATOMIC_T forced_exit; \
\
assert((0 == forced_exit) || (1 == forced_exit)); \
forced_exit = 1; \
}
/* Set the value of forced_exit to 2. This should indicate that we are already in the exit processing, and do not want to handle any
* deferred events, from timers or other interrupts, anymore. Ensure that forced_exit state does not regress by asserting that the
* current value is 0 or 1 before setting it to 2. Note that on UNIX forced_exit can progress to 2 only from 1, while on VMS it is
* possible to generic_exit_handler or dbcertify_exit_handler to change forced_exit from 0 to 2 directly. This design ensures that
* on VMS we will not invoke sys$exit from DEFERRED_EXIT_HANDLING_CHECK after we started exit processing; on UNIX process_exiting
* flag servs the same purpose (and is also checked by DEFERRED_EXIT_HANDLING_CHECK), so it is not necessary for either
* generic_signal_handler or dbcertify_signal_handler to set forced_exit to 2.
*/
#define SET_FORCED_EXIT_STATE_ALREADY_EXITING \
{ \
GBLREF VSIG_ATOMIC_T forced_exit; \
\
assert(VMS_ONLY((0 == forced_exit) || ) (1 == forced_exit)); \
forced_exit = 2; \
}
/* 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; \
\
/* The forced_exit state of 2 indicates that the exit is already in progress, so we do not \
* need to process any deferred events. \
*/ \
if (2 > forced_exit) \
{ /* If forced_exit was set while in a deferred state, disregard any deferred timers and \
* invoke deferred_signal_handler directly. \
*/ \
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 */ \
} \
}
#define OK_TO_SEND_MSG ((INTRPT_IN_X_TIME_FUNCTION != intrpt_ok_state) \
&& (INTRPT_IN_LOG_FUNCTION != intrpt_ok_state) \
&& (INTRPT_IN_FORK_OR_SYSTEM != intrpt_ok_state))
uint4 have_crit(uint4 crit_state);
#endif /* HAVE_CRIT_H_INCLUDED */