1
0
Files
2022-09-29 17:59:04 +03:00

359 lines
6.8 KiB
C

#ident "$Revision: 1.3 $"
#include <sys/types.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/t6satmp.h>
#include "sem.h"
#include "thread.h"
#include "timer.h"
/* type of interval timer to use */
static const int TIMER_TYPE = ITIMER_REAL;
/* name of the alarm signal */
static const int TIMER_SIG = SIGALRM;
static mutex tinq_lock;
static tqueue *tinq_head, *tinq_tail;
static mutex toutq_lock;
static condvar toutq_condvar;
static tqueue *toutq_head, *toutq_tail;
/* this holds the value of current_time()
whenever an interval timer is started */
static unsigned int timer_set_timestamp;
static unsigned int current_time (void);
/*******************************************************************
* INITIALIZATION/DESTRUCTION FUNCTIONS
******************************************************************/
static void
timer_destroy (void)
{
(void) condvar_destroy (&toutq_condvar);
(void) mutex_destroy (&toutq_lock);
(void) mutex_destroy (&tinq_lock);
}
int
timer_initialize (void)
{
if (mutex_initialize (&tinq_lock) == -1 ||
mutex_initialize (&toutq_lock) == -1 ||
condvar_initialize (&toutq_condvar) == -1)
return (-1);
if (atexit (timer_destroy) == 0)
return (0);
timer_destroy ();
return (-1);
}
/*******************************************************************
* TIMEOUT HANDLER THREAD
******************************************************************/
static void
toutq_add (tqueue *q)
{
if (mutex_lock (&toutq_lock) == -1)
return;
/* insert into list */
if (toutq_tail == NULL)
toutq_head = q;
else
toutq_tail->next = q;
toutq_tail = q;
(void) condvar_signal (&toutq_condvar);
mutex_unlock (&toutq_lock);
}
/* ARGSUSED */
static thread_rtn
timeout_thread (void *arg)
{
tqueue *t, *n;
(void) mutex_lock (&toutq_lock);
for(;;)
{
if (condvar_wait (&toutq_condvar, &toutq_lock) == -1)
continue;
for (t = toutq_head; t != NULL; t = n)
{
if (t->notify)
(*t->notify) (t->client);
if (t->destroy)
(*t->destroy) (t->client);
n = t->next;
free (t);
}
toutq_head = NULL;
toutq_tail = NULL;
}
/* NOTREACHED */
thread_return (0);
}
/*******************************************************************
* TIMER THREAD
******************************************************************/
static tqueue *
timer_cancel_unlocked (satmp_esi_t esi)
{
tqueue *t, *p = NULL;
/* search for entry in chain */
for (t = tinq_head; t != NULL; p = t, t = t->next)
if (esi == t->esi)
break;
/* if there is no entry, stop here */
if (t == NULL)
return (t);
/* remove from chain */
if (p == NULL)
{
tinq_head = tinq_head->next;
if (tinq_head == NULL)
tinq_tail = NULL;
}
else
{
p->next = t->next;
if (tinq_tail == t)
tinq_tail = p;
}
/* cancel timer */
t->timeout = 0;
t->next = NULL;
/* return pointer to entry */
return (t);
}
/* subtract time from all timers, enabling any that run out along the way */
static void
update_all_timers_by (unsigned int time)
{
tqueue *t, *next;
for (t = tinq_head; t != NULL; t = next)
{
next = t->next;
if (time < t->timeout)
{
t->timeout -= time;
}
else if (t->ntries-- > 0 && t->retry)
{
/* retry */
t->timeout = t->otimeout;
(*t->retry) (t->client);
}
else
{
/* remove from tinq, add to toutq */
t = timer_cancel_unlocked (t->esi);
if (t != NULL)
toutq_add (t);
}
}
}
/* return time in tens of milliseconds */
static unsigned int
current_time (void)
{
struct timeval tp;
(void) gettimeofday (&tp, (struct timezone *) 0);
/* ignore overflow */
return (tp.tv_sec * USECS_PER_SEC + tp.tv_usec);
}
static tqueue *
shortest_timer (void)
{
tqueue *t, *s_t; /* shortest_timer */
unsigned int old_timer = MANY_USECS;
s_t = NULL;
for (t = tinq_head; t != NULL; t = t->next)
{
if (t->timeout > 0 && t->timeout < old_timer)
{
old_timer = t->timeout;
s_t = t;
}
}
return (s_t);
}
static void
start_timer (tqueue *t)
{
struct itimerval ntimer;
if (t == NULL)
{
/* cancel interval timer */
timerclear (&ntimer.it_value);
timerclear (&ntimer.it_interval);
(void) setitimer (TIMER_TYPE, &ntimer, (struct itimerval *) 0);
return;
}
timer_set_timestamp = current_time ();
/* initialize timer parameters */
timerclear (&ntimer.it_interval);
ntimer.it_value.tv_sec = SEC (t->timeout);
ntimer.it_value.tv_usec = USEC (t->timeout);
/* install timer parameters */
(void) setitimer (TIMER_TYPE, &ntimer, (struct itimerval *) NULL);
}
static thread_id timer_tid;
static void
wakeup_timer (void)
{
(void) thread_kill (timer_tid, TIMER_SIG);
}
/* ARGSUSED */
static thread_rtn
timer_thread (void *arg)
{
int sig;
sigset_t set;
(void) sigemptyset (&set);
(void) sigaddset (&set, TIMER_SIG);
while (sigwait (&set, &sig) == 0 && sig == TIMER_SIG)
{
if (mutex_lock (&tinq_lock) == -1)
continue;
/* update timeout values */
update_all_timers_by (current_time () - timer_set_timestamp);
/* start timer for next shortest guy if one exists */
start_timer (shortest_timer ());
mutex_unlock (&tinq_lock);
}
/* NOTREACHED */
thread_return (0);
}
void *
timer_cancel (satmp_esi_t esi)
{
tqueue *t;
void *client = NULL;
/* check args && lock queue */
if (mutex_lock (&tinq_lock) == -1)
return (client);
t = timer_cancel_unlocked (esi);
if (t)
client = t->client;
mutex_unlock (&tinq_lock);
free (t);
return (client);
}
int
timer_set (time_t timeout, unsigned int ntries, satmp_esi_t esi,
void *client, timer_ast notify, timer_ast retry, timer_ast destroy)
{
tqueue *t;
/* allocate queue entry */
t = (tqueue *) malloc (sizeof (*t));
if (t == NULL)
return (1);
/* initialize queue entry */
t->timeout = timeout;
t->otimeout = timeout;
t->ntries = ntries;
t->esi = esi;
t->client = client;
t->notify = notify;
t->retry = retry;
t->destroy = destroy;
t->next = NULL;
/* lock queue */
if (mutex_lock (&tinq_lock) == -1)
{
free (t);
return (1);
}
/* insert into list */
if (tinq_tail == NULL)
{
tinq_head = t;
timer_set_timestamp = current_time ();
}
else
tinq_tail->next = t;
tinq_tail = t;
/* unlock queue */
mutex_unlock (&tinq_lock);
/* signal waiter */
wakeup_timer ();
return (0);
}
static void
alarm_handler (void)
{
}
int
timer_threads_start (void)
{
thread_id tid;
sigset_t nset;
struct sigaction act;
/*
* Block TIMER_SIG except when explicitly waiting for it.
* Setup TIMER_SIG handler.
*/
act.sa_flags = 0;
act.sa_handler = alarm_handler;
if (sigemptyset (&nset) == -1 ||
sigaddset (&nset, TIMER_SIG) == -1 ||
thread_sigprocmask (SIG_BLOCK, &nset, (sigset_t *) NULL) == -1 ||
sigemptyset (&act.sa_mask) == -1 ||
sigaction (TIMER_SIG, &act, (struct sigaction *) NULL) == -1 ||
thread_create (timeout_thread, &tid, (void *) 0) == -1 ||
thread_create (timer_thread, &timer_tid, (void *) 0) == -1)
return (-1);
return (0);
}