/* Copyright 1990, Daniel J. Bernstein. All rights reserved. */

#include "config.h"
#include <sys/time.h>
#include "sig.h"

/* This is a stripped-down signal library, with automatic critical */
/* sections around every signal handler. As long as no signal handler */
/* has to pause waiting for another signal, this works beautifully */
/* and prevents all races. */

static int queued[SIGNUM];
static int quo[SIGNUM];
static sig_syshandler orig[SIGNUM];
static sig_handler handler[SIGNUM];

static sig_syshandler signalv(s,h)
register sig_num s;
register sig_syshandler h;
{
 return signal(s,h);
}

static int crit = 0;

static void handle(i)
register sig_num i;
{
 if (crit)
   queued[i] = 1;
 else
  {
   register int q;
   register sig_num j;

   crit = 1; (void) handler[i](i); queued[i] = 0; crit = 0;
   do for (j = q = 0;j < SIGNUM;j++) if (queued[j])
	{
	 crit = q = 1;
	 if (j != i) (void) handler[j](j);
	 queued[j] = 0; crit = 0;
	}
   while (q);
  }
}

/* To see why handle() works: First, crit can be considered a local
variable, because handle() is the only routine that modifies it, and
handle() always leaves crit the same. Second, crit is 1 while any
handler is called, and then any simultaneous handle()s will simply
queue. Hence handlers are mutually exclusive. Third, when handle() is
called with crit == 0, it can only exit after going through an entire j
loop with no queued[]s true. Fourth, if all queued[]s are false through
that j loop, then crit is not set by handle() during that loop. Finally,
if crit is 0, handle() will exit with no queued signals: this is true by
induction from the previous observations. */


/* There is unfortunately no guarantee that a signal handler as */
/* passed to signal() will receive its signal number as its first */
/* argument. We do make that guarantee. */

#define HAN(s,h) SIGRET_TYPE h() { handle(s); }

HAN(0,han0);   HAN(1,han1);   HAN(2,han2);   HAN(3,han3);
HAN(4,han4);   HAN(5,han5);   HAN(6,han6);   HAN(7,han7);
HAN(8,han8);   HAN(9,han9);   HAN(10,han10); HAN(11,han11);
HAN(12,han12); HAN(13,han13); HAN(14,han14); HAN(15,han15);
HAN(16,han16); HAN(17,han17); HAN(18,han18); HAN(19,han19);
HAN(20,han20); HAN(21,han21); HAN(22,han22); HAN(23,han23);
HAN(24,han24); HAN(25,han25); HAN(26,han26); HAN(27,han27);
HAN(28,han28); HAN(29,han29); HAN(30,han30); HAN(31,han31);

static sig_syshandler han[32] =
  { han0 ,han1 ,han2 ,han3 ,han4 ,han5 ,han6 ,han7 ,
    han8 ,han9 ,han10,han11,han12,han13,han14,han15,
    han16,han17,han18,han19,han20,han21,han22,han23,
    han24,han25,han26,han27,han28,han29,han30,han31
  } ;

#define QUE(s,h) SIGRET_TYPE h() { quo[s] = 1; }

QUE(0,que0);   QUE(1,que1);   QUE(2,que2);   QUE(3,que3);
QUE(4,que4);   QUE(5,que5);   QUE(6,que6);   QUE(7,que7);
QUE(8,que8);   QUE(9,que9);   QUE(10,que10); QUE(11,que11);
QUE(12,que12); QUE(13,que13); QUE(14,que14); QUE(15,que15);
QUE(16,que16); QUE(17,que17); QUE(18,que18); QUE(19,que19);
QUE(20,que20); QUE(21,que21); QUE(22,que22); QUE(23,que23);
QUE(24,que24); QUE(25,que25); QUE(26,que26); QUE(27,que27);
QUE(28,que28); QUE(29,que29); QUE(30,que30); QUE(31,que31);

static sig_syshandler que[32] =
  { que0 ,que1 ,que2 ,que3 ,que4 ,que5 ,que6 ,que7 ,
    que8 ,que9 ,que10,que11,que12,que13,que14,que15,
    que16,que17,que18,que19,que20,que21,que22,que23,
    que24,que25,que26,que27,que28,que29,que30,que31
  } ;


void sig_init()
{
 sig_num i;

 for (i = 0;i < SIGNUM;i++)
   quo[i] = 0;
 for (i = 0;i < SIGNUM;i++)
   orig[i] = signalv(i,que[i]);
}

void sig_restore()
{
 sig_num i;

 for (i = 0;i < SIGNUM;i++)
   (void) signalv(i,orig[i]);
}

void sig_handle(s)
sig_num s;
{
 if (quo[s])
   han[s]();
 (void) signalv(s,han[s]);
 quo[s] = 0;
}

void sig_ignore(s)
sig_num s;
{
 (void) signalv(s,SIG_IGN);
}

void sig_default(s)
sig_num s;
{
 (void) signalv(s,SIG_DFL);
}

void sig_sethandler(s,h)
sig_num s;
sig_handler h;
{
 handler[s] = h;
}

#ifdef SIGINTERRUPT
void sig_interrupt()
{
 register sig_num s;

 for (s = 0;s < SIGNUM;s++)
   (void) siginterrupt(s,1);
}
#endif

void sig_startring()
{
 struct itimerval it;

 it.it_value.tv_sec = it.it_interval.tv_sec = 0;
 it.it_value.tv_usec = it.it_interval.tv_usec = 10000;
 (void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
}

void sig_stopring()
{
 struct itimerval it;

 it.it_value.tv_sec = it.it_interval.tv_sec = 0;
 it.it_value.tv_usec = it.it_interval.tv_usec = 0;
 (void) setitimer(ITIMER_REAL,&it,(struct itimerval *) 0);
}

/*ARGSUSED*/
void nothing(i)
sig_num i;
{
 ; /* that's right, absolutely nothing. */
}
