fis-gtm/sr_unix/gt_timers.c

941 lines
26 KiB
C

/****************************************************************
* *
* Copyright 2001, 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. *
* *
****************************************************************/
/*
* --------------------------------------------------------------
* This file contains a general purpose timer package.
* Simultaneous multiple timers are supported.
* All outstanding timers are contained in a queue of
* pending requests. New timer is added to the queue in an
* expiration time order. The first timer in a queue expires
* first, and the last one expires last.
* When timer expires, the signal is generated and the process
* is awakened. This timer is then removed from the queue,
* and the first timer in a queue is started again, and so on.
* Starting a timer with the timer id equal to one of the existing
* timers in a chain will remove the existing timer from the chain
* and add a new one instead.
*
* It is a responsibility of the user to go to hibernation mode
* by executing appropriate system call if the user needs to
* wait for the timer expiration.
*
* Following routines are top level, user callable
* routines of this package:
*
* uninit_timers()
* De-Initialize timers - restore signals, etc.
*
* void sys_get_cur_time(ABS_TIME *atp)
* fetch absolute time into stucture
*
* void hiber_start(uint4 hiber)
* used to sleep for hiber milliseconds
*
* void start_timer(TID tid, int4 time_to_expir, void (*handler)(), int4 dlen, char *data)
* Used to start a new timer.
*
* void cancel_timer(TID tid)
* Cancel an existing timer.
* Cancelling timer with tid = 0, cancells all timers.
* --------------------------------------------------------------
*/
#include "mdef.h"
#include <errno.h>
#include "gtm_time.h"
#include "gtm_string.h"
#include <stddef.h>
#if (defined(__ia64) && defined(__linux__)) || defined(__MVS__)
#include "gtm_unistd.h"
#endif /* __ia64 && __linux__ or __MVS__ */
#include "gt_timer.h"
#include "wake_alarm.h"
#if defined(mips) && !defined(_SYSTYPE_SVR4)
#include <bsd/sys/time.h>
#else
#include <sys/time.h>
#endif
#ifndef __MVS__
#include <sys/param.h>
#endif
#include "send_msg.h"
#if defined(__osf__)
#define HZ CLK_TCK
#elif defined(__MVS__)
#define HZ gtm_zos_HZ
STATICDEF int gtm_zos_HZ = 100; /* see prealloc_gt_timers below */
#endif
#ifdef ITIMER_REAL
#define BSD_TIMER
#else
/* check def of time() including arg - see below should be time_t
(from sys/types.h) and traditionally unsigned long */
#ifndef __osf__
int4 time();
#endif
#endif
/* Set each timer request to go for 10ms more than requested, since the
* interval timer alarm will sometimes go off early on many UNIX systems
* 10ms is more than enough for all systems tested so far (SunOS, Solaris,
* HP/UX, NonStop/UX)
*/
#ifndef SLACKTIME
# define SLACKTIME 10
#endif
#define TIMER_BLOCK_SIZE 64 /* # of timer entries allocated initially as well as at every expansion */
#define GT_TIMER_EXPAND_TRIGGER 8 /* if the # of timer entries in the free queue goes below this, allocate more */
#define GT_TIMER_INIT_DATA_LEN 8
#ifdef BSD_TIMER
static struct itimerval sys_timer, old_sys_timer;
#endif
/* following can be used to see why timer_handler was called */
#define DUMMY_SIG_NUM 0
volatile STATICDEF GT_TIMER *timeroot = NULL; /* Chain of pending timer requests in time order */
static boolean_t first_timeset = TRUE;
/*
* Chain of unused timer request blocks
*/
volatile static GT_TIMER *timefree = NULL;
volatile static int4 num_timers_free; /* # of timers in the unused queue */
static int4 timeblk_hdrlen;
GBLREF boolean_t blocksig_initialized; /* set to TRUE when blockalrm and block_sigsent are initialized */
GBLREF sigset_t blockalrm;
GBLREF sigset_t block_sigsent;
volatile static st_timer_alloc *timer_allocs = NULL;
/*
* Save previous SIGALRM handler if any.
*/
static struct sigaction prev_alrm_handler;
/*
* Flag signifying timer is active. Especially useful
* when the timer handlers get nested..
*/
volatile static int4 timer_active = FALSE;
GBLDEF volatile boolean_t timer_in_handler = FALSE; /* set to TRUE when timer pops */
GBLREF int4 outofband;
GBLREF int process_exiting;
static void (*safe_handlers[])() = {hiber_wake, wake_alarm , NULL};
error_def(ERR_TIMERHANDLER);
/*
* --------------------------------------
* Uninitialize timers and signals
* --------------------------------------
*/
void uninit_timers(void)
{
/* restore previous handler */
sigaction(SIGALRM, &prev_alrm_handler, NULL);
}
/*
* --------------------------------------
* Called when a hiber_start timer pops.
* Set flag so a given timer will wake up
* (not go back to sleep).
* --------------------------------------
*/
STATICFNDEF void hiber_wake(TID tid, int4 hd_len, int4 **waitover_flag)
{
**waitover_flag = TRUE;
}
STATICFNDEF void gt_timers_alloc(void)
{
int4 gt_timer_cnt;
GT_TIMER *timeblk, *timeblks;
st_timer_alloc *new_alloc;
assert(!timer_in_handler);
timeblk_hdrlen = OFFSETOF(GT_TIMER, hd_data[0]);
timeblk = timeblks = (GT_TIMER *)malloc((timeblk_hdrlen + GT_TIMER_INIT_DATA_LEN) * TIMER_BLOCK_SIZE);
new_alloc = (st_timer_alloc *)malloc(SIZEOF(st_timer_alloc));
new_alloc->addr = timeblk;
new_alloc->next = (st_timer_alloc *)timer_allocs;
timer_allocs = new_alloc;
for (gt_timer_cnt = TIMER_BLOCK_SIZE; 0 < gt_timer_cnt; --gt_timer_cnt)
{
timeblk->hd_len_max = GT_TIMER_INIT_DATA_LEN; /* Set amount it can store */
timeblk->next = (GT_TIMER *)timefree; /* Put on free queue */
timefree = timeblk;
timeblk = (GT_TIMER *)((char *)timeblk + timeblk_hdrlen + GT_TIMER_INIT_DATA_LEN); /* Next! */
}
assert(((char *)timeblk - (char *)timeblks) == (timeblk_hdrlen + GT_TIMER_INIT_DATA_LEN) * TIMER_BLOCK_SIZE);
num_timers_free += TIMER_BLOCK_SIZE;
}
/*
* Do the initialization of block_sigsent and blockalrm, and set blocksig_initialized to TRUE, so
* that we can later block signals when there is a need. This function should be called very early
* in the main() routines of modules that wish to do their own interrupt handling.
*/
void set_blocksig(void)
{
sigemptyset(&blockalrm);
sigaddset(&blockalrm, SIGALRM);
sigemptyset(&block_sigsent);
sigaddset(&block_sigsent, SIGINT);
sigaddset(&block_sigsent, SIGQUIT);
sigaddset(&block_sigsent, SIGTERM);
sigaddset(&block_sigsent, SIGTSTP);
sigaddset(&block_sigsent, SIGCONT);
sigaddset(&block_sigsent, SIGALRM);
blocksig_initialized = TRUE; /* note the fact that blockalrm and block_sigsent are initialized */
}
/*
* --------------------------------------
* Initialize group of timer blocks
* --------------------------------------
*/
void prealloc_gt_timers(void)
{
/* Preallocate some timer blocks. This will be all the timer blocks we hope to need.
* Allocate them with 8 bytes of possible data each.
* If more timer blocks are needed, we will allocate them as needed.
*/
#ifdef __MVS__
gtm_zos_HZ == sysconf(_SC_CLK_TCK); /* get the real value */
#endif
gt_timers_alloc(); /* Allocate timers */
}
/*
* ----------------------------------------------------
* Get current clock time
* Fill-in the structure with the absolute time
* of system clock.
*
* Arguments:
* atp - pointer to structure of absolute time
* ----------------------------------------------------
*/
void sys_get_curr_time (ABS_TIME *atp)
{
#ifdef BSD_TIMER
struct timeval tv;
struct timezone tz;
struct tm *dtp;
/* getclock or clock_gettime perhaps to avoid tz just to ignore */
gettimeofday(&tv, &tz);
atp->at_sec = (int4)tv.tv_sec;
atp->at_usec = (int4)tv.tv_usec;
#else
atp->at_sec = time((int4 *) 0);
atp->at_usec = 0;
#endif
}
/*
* ---------------------------------------------------------
* Start hibernating by starting a timer and waiting for it.
* ---------------------------------------------------------
*/
void hiber_start (uint4 hiber)
{
/* start_timer_int has char * as hdata type */
int4 waitover;
int4 *waitover_addr;
TID tid;
sigset_t savemask;
/* Timer services are unavailable from within a timer handler */
if (timer_in_handler)
GTMASSERT;
/* block SIGALRM signal */
sigprocmask(SIG_BLOCK, &blockalrm, &savemask);
waitover = FALSE; /* When OUR timer pops, it will set this flag */
waitover_addr = &waitover;
tid = (TID)waitover_addr; /* Unique id of this timer */
start_timer_int((TID)tid, hiber, hiber_wake, SIZEOF(waitover_addr), &waitover_addr);
/* We will loop here until OUR timer pops and sets OUR flag. Otherwise
we will keep waiting for it. */
do
{ /* unblock SIGALRM and wait for timer interrupt */
sigsuspend(&savemask);
if (outofband)
{
cancel_timer(tid);
break;
}
} while(FALSE == waitover);
/* reset signal handlers */
sigprocmask(SIG_SETMASK, &savemask, NULL);
}
/*
* ---------------------------------------------------------
* Start hibernating by starting a timer and waiting for it
* or any other timer interrupt that happens to come along.
* ---------------------------------------------------------
*/
void hiber_start_wait_any(uint4 hiber)
{
sigset_t savemask;
if (1000 > hiber)
{
SHORT_SLEEP(hiber); /* note: some platforms call hiber_start */
return;
}
/* Timer services are unavailable from within a timer handler */
if (timer_in_handler)
GTMASSERT;
/* block SIGALRM signal and set new timer */
sigprocmask(SIG_BLOCK, &blockalrm, &savemask);
start_timer_int((TID)hiber_start_wait_any, hiber, NULL, 0, NULL);
/* unblock SIGALRM and wait for timer interrupt */
sigsuspend(&savemask);
/* Cancel timer block before reenabling */
cancel_timer((TID)hiber_start_wait_any);
/* reset signal handlers */
sigprocmask(SIG_SETMASK, &savemask, NULL);
}
/*
* ------------------------------------------------------
* Start the timer
* If timer chain is empty or this is the first timer
* to expire, actually start the timer.
*
* Arguments:
* tid - timer id
* time_to_expir - time to expiration in msecs;
* handler - pointer to handler routine
* hdata_len - length of handler data next arg
* hdata - data to pass to handler (if any)
* ------------------------------------------------------
*/
void start_timer(TID tid,
int4 time_to_expir,
void (*handler)(),
int4 hdata_len,
void *hdata)
{
sigset_t savemask;
if (0 >= time_to_expir)
GTMASSERT;
/* block SIGALRM signal */
sigprocmask(SIG_BLOCK, &blockalrm, &savemask);
start_timer_int(tid, time_to_expir, handler, hdata_len, hdata);
/* reset signal handlers */
sigprocmask(SIG_SETMASK, &savemask, NULL);
}
/*
* ------------------------------------------------------
* Internal version of start_timer that does not protect
* itself assuming this has already been done. Otherwise
* does as explained above in start_timer.
* ------------------------------------------------------
*/
STATICFNDEF void start_timer_int(TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata)
{
ABS_TIME at;
assert(0 != time_to_expir);
sys_get_curr_time(&at);
if (first_timeset)
{
init_timers();
first_timeset = FALSE;
}
/* We expect no timer with id=<tid> to exist in the timer queue currently. This is asserted in "add_timer" call below.
* In pro though, we'll be safe and remove any tids that exist before adding a new entry with the same tid - 2009/10.
* If a few years pass without the assert failing, it might be safe then to remove the PRO_ONLY code below.
*/
PRO_ONLY(
if (timeroot && (timeroot->tid == tid))
sys_canc_timer(tid);
remove_timer(tid); /* Remove timer from chain */
)
/* Check if # of free timer slots is less than minimum threshold. If so allocate more of those while it is safe to do so */
if ((GT_TIMER_EXPAND_TRIGGER > num_timers_free) && !timer_in_handler)
gt_timers_alloc();
/* Link new timer into timer chain */
add_timer(&at, tid, time_to_expir, handler, hdata_len, hdata);
if ((timeroot->tid == tid) || !timer_active)
start_first_timer(&at);
}
STATICFNDEF void uninit_all_timers(void)
{
st_timer_alloc *next_timeblk;
st_timer_alloc *timer_iter;
#ifdef BSD_TIMER
/* clear timer */
sys_timer.it_interval.tv_sec = sys_timer.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &sys_timer, &old_sys_timer);
old_sys_timer.it_interval.tv_sec = old_sys_timer.it_interval.tv_usec = 0;
#else
alarm((unsigned)1);
#endif
first_timeset = TRUE;
/* Loop over timer_allocs entries and deallocate them */
for (; timer_allocs; timer_allocs = next_timeblk)
{
next_timeblk = timer_allocs->next;
free(timer_allocs->addr); /* Free the timeblk */
free((st_timer_alloc *)timer_allocs); /* Free the container */
}
/* After all timers are removed, we need to set the below pointers to NULL */
timeroot = NULL;
timefree = NULL;
num_timers_free = 0;
/* Empty the blockalrm and sigsent entries */
sigemptyset(&blockalrm);
sigemptyset(&block_sigsent);
uninit_timers();
timer_active = FALSE;
}
/*
* ---------------------------------------------
* Cancel timer
*
* Arguments:
* tid - timer id
* ---------------------------------------------
*/
void cancel_timer(TID tid)
{
ABS_TIME at;
sigset_t savemask;
/* block SIGALRM signal */
sigprocmask(SIG_BLOCK, &blockalrm, &savemask);
sys_get_curr_time(&at);
if (tid == 0)
{
assert(process_exiting); /* wcs_phase2_commit_wait relies on this flag being set BEFORE cancelling all timers */
cancel_all_timers();
uninit_all_timers();
timer_in_handler = FALSE;
sigprocmask(SIG_SETMASK, &savemask, NULL);
return;
}
/* If this is the first timer in the chain, stop it */
if (timeroot && timeroot->tid == tid)
sys_canc_timer(tid);
/* remove it from the chain */
remove_timer(tid);
/* Start the first timer in the chain */
start_first_timer(&at);
sigprocmask(SIG_SETMASK, &savemask, NULL);
}
/*
* ---------------------------------------------
* Clear the timers' state for the forked-off process.
* ---------------------------------------------
*/
void clear_timers(void)
{
sigset_t savemask;
/* block SIGALRM signal */
sigprocmask(SIG_BLOCK, &blockalrm, &savemask);
while (timeroot)
remove_timer(timeroot->tid);
timer_in_handler = FALSE;
timer_active = FALSE;
sigprocmask(SIG_SETMASK, &savemask, NULL);
return;
}
/*
* ----------------------------------------------------
* System call to set timer.
* Time is given im msecs.
*
* Arguments:
* tid - timer id
* time_to_expir - time to expiration.
* handler - address of handler routine
* ----------------------------------------------------
*/
STATICFNDEF void sys_settimer (TID tid, ABS_TIME *time_to_expir, void (*handler)())
{
#ifdef BSD_TIMER
if (time_to_expir->at_sec == 0 && time_to_expir->at_usec < (1000000 / HZ))
{
sys_timer.it_value.tv_sec = 0;
sys_timer.it_value.tv_usec = 1000000 / HZ;
}
else
{
sys_timer.it_value.tv_sec = time_to_expir->at_sec;
sys_timer.it_value.tv_usec = (gtm_tv_usec_t)time_to_expir->at_usec;
}
sys_timer.it_interval.tv_sec = sys_timer.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &sys_timer, &old_sys_timer);
#else
if (time_to_expir->at_sec == 0)
alarm((unsigned)1);
else
alarm(time_to_expir->at_sec);
#endif
timer_active = TRUE;
}
/*
* ----------------------------------------
* Start the first timer in the timer chain
* ----------------------------------------
*/
STATICFNDEF void start_first_timer(ABS_TIME *curr_time)
{
ABS_TIME eltime, interval;
if (FALSE == timer_in_handler)
{
/* smw why loop timeroot here and timer_handler */
/* se: while I don't recall the specific purpose of this loop, it had to do with other
than timer_handler cases where something came due while we were out doing other
things. It may have been a "performance" enhancement trying to drive stuff that had
come due without waiting for the timer pop to drive it -- Anyway, we don't want this
particular code to be run from code inside a handler since the handler return will do
this. Without the timer_in_handler check, busy interrupt handlers can nest so deeply
that we run out of timer blocks and have a very long stack.
*/
while (timeroot)
{
eltime = sub_abs_time((ABS_TIME *)&timeroot->expir_time, curr_time);
if (0 > eltime.at_sec) /* First entry on queue expired? */
timer_handler(DUMMY_SIG_NUM); /* Invoke handlers -- we are blocked on sigalarms here */
else
break; /* eltime ok -- */
}
if (timeroot) /* We still have a timer to set ? */
{
add_int_to_abs_time(&eltime, SLACKTIME, &interval);
/* Call system to set timer */
sys_settimer(timeroot->tid, &interval, timeroot->handler);
}
}
}
/*
* ------------------------------------------------------
* 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.
*
* ------------------------------------------------------
*/
STATICFNDEF void timer_handler(int why)
{
int4 cmp;
GT_TIMER *tpop;
ABS_TIME at;
sigset_t savemask;
int4 eltime;
int save_errno;
boolean_t save_timer_in_handler;
save_errno = errno;
timer_active = FALSE; /* Timer has popped - not active anymore */
save_timer_in_handler = timer_in_handler; /* since recurses */
timer_in_handler = TRUE; /* but we are (usually) at interrupt level */
sys_get_curr_time(&at);
/* Fire all handlers that expired */
while (timeroot)
{
cmp = abs_time_comp(&at, (ABS_TIME *)&timeroot->expir_time);
if (cmp < 0)
break;
else
{ /* Delete first entry in a timer chain */
tpop = (GT_TIMER *)timeroot;
timeroot = tpop->next;
if (NULL != tpop->handler) /* If want a handler, do it */
{
(*tpop->handler)(tpop->tid, tpop->hd_len, tpop->hd_data);
if (!tpop->safe) /* If safe, avoid get environment call */
sys_get_curr_time(&at); /* Refresh current time if called a handler */
}
/* put timer block on the free chain */
tpop->next = (GT_TIMER *)timefree;
timefree = tpop;
num_timers_free++;
assert(0 < num_timers_free);
}
}
/* Start the first timer in the chain */
timer_in_handler = save_timer_in_handler;
start_first_timer(&at);
errno = save_errno; /* restore mainline errno */
}
/*
* ------------------------------------------------------
* Find a timer given by tid in the timer chain
*
* Arguments:
* tid - timer id
* tprev - address of pointer to previous node
*
* Return:
* pointer to timer in the chain, or
* 0 - if timer does not exist on the chain
*
* Side effects:
* tprev is set to the link previous to the tid link
* ------------------------------------------------------
*/
STATICFNDEF GT_TIMER *find_timer(TID tid, GT_TIMER **tprev)
{
GT_TIMER *tp, *tc;
tc = (GT_TIMER*)timeroot;
*tprev = 0;
while (tc)
{
if (tc->tid == tid)
return(tc);
*tprev = tc;
tc = tc->next;
}
return (0);
}
/*
* --------------------------------------------------
* Add timer to timer chain
* Allocate a new link for a timer.
* Convert time to expiration into absolute time.
* Insert new link into chain in timer order.
*
* Arguments:
* tid - timer id
* time_to_expir - elapsed time to expiration
* handler - pointer to handler routine
* hdata_len - length of data to follow
* hdata - data to pass to timer rtn if any
*
* Return:
* TRUE - timer added ok
* FALSE - timer could not be added
* --------------------------------------------------
*/
STATICFNDEF void add_timer(ABS_TIME *atp, TID tid, int4 time_to_expir, void (*handler)(), int4 hdata_len, void *hdata)
{
GT_TIMER *tp, *tpp, *ntp, *lastntp;
int4 cmp, i;
st_timer_alloc *new_alloc;
/* Assert that no timer entry with the same "tid" exists in the timer chain */
assert((NULL == find_timer(tid, &tpp)));
/* Obtain a new timer block */
ntp = (GT_TIMER *)timefree; /* Start at first free block */
lastntp = NULL;
for ( ; NULL != ntp; )
{ /* We expect all callers of timer functions to not require more than 8 bytes of data. Any violations
* of this assumption need to be caught hence the assert below.
*/
assert(GT_TIMER_INIT_DATA_LEN == ntp->hd_len_max);
assert(ntp->hd_len_max >= hdata_len);
if (ntp->hd_len_max >= hdata_len) /* Found one that can hold our data */
{ /* Dequeue block */
if (NULL == lastntp) /* First one on queue */
timefree = ntp->next; /* Dequeue 1st element */
else /* is not 1st on queue -- use simple dequeue */
lastntp->next = ntp->next;
assert(0 < num_timers_free);
num_timers_free--;
break;
}
/* Still looking, try next block */
lastntp = ntp;
ntp = ntp->next;
}
/* If didn't find one, fail if dbg; else malloc a new one */
if (NULL == ntp)
{
assert(FALSE); /* If dbg, we should have enough already */
ntp = (GT_TIMER *)malloc(timeblk_hdrlen + hdata_len); /* if we are in a timer, this malloc may error out */
/* Insert in front of the list */
new_alloc = (st_timer_alloc *)malloc(SIZEOF(st_timer_alloc));
new_alloc->addr = ntp;
new_alloc->next = (st_timer_alloc *)timer_allocs;
timer_allocs = new_alloc;
ntp->hd_len_max = hdata_len;
}
ntp->tid = tid;
ntp->handler = handler;
ntp->safe = FALSE;
if (NULL == handler)
ntp->safe = TRUE;
else
{
for (i = 0; NULL != safe_handlers[i]; i++)
{
if (safe_handlers[i] == handler)
{
ntp->safe = TRUE; /* known to just set flags, etc. */
break;
}
}
}
ntp->hd_len = hdata_len;
if (0 < hdata_len)
memcpy(ntp->hd_data, hdata, hdata_len);
add_int_to_abs_time(atp, time_to_expir, &ntp->expir_time);
tp = (GT_TIMER *)timeroot;
tpp = NULL;
while (tp)
{
cmp = abs_time_comp(&tp->expir_time, &ntp->expir_time);
if (cmp >= 0)
break;
tpp = tp;
tp = tp->next;
}
ntp->next = tp;
if (NULL == tpp)
timeroot = ntp;
else
tpp->next = ntp;
return;
}
/*
* ---------------------------------------------
* Remove timer from the timer chain
* ---------------------------------------------
*/
STATICFNDEF void remove_timer(TID tid)
{
GT_TIMER *tprev, *tp, *tpp;
if ((tp = find_timer(tid, &tprev)))
{
if (tprev)
tprev->next = tp->next;
else
timeroot = tp->next;
/* Place element on free queue */
tp->next = (GT_TIMER *)timefree;
timefree = tp;
num_timers_free++;
assert(0 < num_timers_free);
/* Assert that no duplicate timer entry with the same "tid" exists in the timer chain */
assert((NULL == find_timer(tid, &tpp)));
}
}
/*
* ---------------------------------------------
* System call to cancel timer.
* ---------------------------------------------
*/
STATICFNDEF void sys_canc_timer(TID tid)
{
#ifdef BSD_TIMER
struct itimerval zero;
memset(&zero, 0, SIZEOF(struct itimerval));
setitimer(ITIMER_REAL, &zero, &old_sys_timer);
#else
alarm(0);
#endif
timer_active = FALSE; /* No timer is active now */
}
/*
* ---------------------------------------------
* Cancel all timers
*
* Arguments:
* none
*
* Dependencies:
* The timer signal must be blocked prior to entry
* ---------------------------------------------
*
*/
STATICFNDEF void cancel_all_timers(void)
{
if (timeroot)
sys_canc_timer(timeroot->tid);
while (timeroot)
/* Remove timer from the chain */
remove_timer(timeroot->tid);
}
/*
* --------------------------------------
* Initialize timers
* --------------------------------------
*/
STATICFNDEF void init_timers()
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = timer_handler;
sigaction(SIGALRM, &act, &prev_alrm_handler);
if (first_timeset && /* not from timer_handler to prevent dup message */
(SIG_IGN != prev_alrm_handler.sa_handler) && /* as set by sig_init */
(SIG_DFL != prev_alrm_handler.sa_handler)) /* utils, compile */
{
send_msg(VARLSTCNT(5) ERR_TIMERHANDLER, 3, prev_alrm_handler.sa_handler,
LEN_AND_LIT("init_timers"));
rts_error(VARLSTCNT(5) ERR_TIMERHANDLER, 3, prev_alrm_handler.sa_handler,
LEN_AND_LIT("init_timers"));
assert(FALSE);
}
}
/*
* ---------------------------------------
* Check for timer pops. If any timers are
* on the queue, pretend a sigalrm occur'd
* and we have to check everything. This
* is mainly for use after external calls
* until such time as external calls can
* use this timing facility. Current problem
* is that external calls are doing their
* own catching of sigalarms that should
* be ours and we end up hung.
* ---------------------------------------
*/
void check_for_timer_pops()
{
static boolean_t stolen_timer = FALSE; /* only complain once */
int stolenwhen = 0; /* 0 = no, 1 = not first, 2 = first time */
sigset_t savemask;
struct sigaction current_sa;
static char *whenstolen[] = {"check_for_timer_pops",
"check_for_timer_pops first time"};
sigaction(SIGALRM, NULL, &current_sa); /* get current info */
if (!first_timeset)
{ /* check if what we expected */
if (timer_handler != current_sa.sa_handler)
{
init_timers();
if (!stolen_timer)
{
stolen_timer = TRUE;
stolenwhen = 1;
}
}
} else /* we haven't set so should be ... */
{
if ((SIG_IGN != current_sa.sa_handler) && /* as set by sig_init */
(SIG_DFL != current_sa.sa_handler)) /* utils, compile */
{
if (!stolen_timer)
{
stolen_timer = TRUE;
stolenwhen = 2;
}
}
}
if (timeroot)
{
/* block SIGALRM signal */
sigprocmask(SIG_BLOCK, &blockalrm, &savemask);
timer_handler(DUMMY_SIG_NUM);
/* reset signal handlers */
sigprocmask(SIG_SETMASK, &savemask, NULL);
}
if (stolenwhen)
{
send_msg(VARLSTCNT(5) ERR_TIMERHANDLER, 3, current_sa.sa_handler,
LEN_AND_STR(whenstolen[stolenwhen - 1]));
rts_error(VARLSTCNT(5) ERR_TIMERHANDLER, 3, current_sa.sa_handler,
LEN_AND_STR(whenstolen[stolenwhen - 1]));
assert(FALSE); /* does not return here */
}
}
/* Externally exposed routine that does a find_timer and is SIGALRM interrupt safe. */
GT_TIMER *find_timer_intr_safe(TID tid, GT_TIMER **tprev)
{
sigset_t savemask;
GT_TIMER *tcur;
/* Before scanning timer queues, block SIGALRM signal as otherwise that signal could cause an interrupt
* timer routine to be driven which could in turn modify the timer queues while this mainline code is
* examining the very same queue. This could cause all sorts of invalid returns (of tcur and tprev)
* from the find_timer call below.
*/
sigprocmask(SIG_BLOCK, &blockalrm, &savemask);
tcur = find_timer(tid, tprev);
sigprocmask(SIG_SETMASK, &savemask, NULL);
return tcur;
}