Subject: alarm.c

From: Neal Murphy <[email-masked]>
Date: Fri, 14 Jun 1996 14:00:36 -0500 (CDT)

/*
   Copyright 1996, Harsof Engineering.

   Please let me know of any errors in this code, so I can fix them.

   If you wish to use this module, send email to [email-masked].
 */

/*
   Date:               17 June 1996
   Author:             Neal P. Murphy <[email-masked]>

   Module Name:        Alarm() for BeOS DR7 <alarm.c>
   Required Headers:   none
   Required Externs:   extern unsigned int alarm(unsigned int)
                       exterm void alarm_expired(void)

   Global Variables:   none
   Global Functions:   unsigned int alarm(unsigned int), void alarm_expired(void)
   Static Data:        double time_started, thread_id alarm_thread, struct ALARM_STRUCT
   Local functions:    long do_alarm(void *)

   Usage:              time_remaining = alarm(seconds)
                       alarm() works just as it is supposed to under Posix
                       and Unix environments, with *one* exception. alarm()
                       returns zero if the alarm was off, or it returns
                       the time remaining 'till the alarm was to go off;
                       this is true whether you are setting, stopping or
                       re-setting the alarm clock.

                       The exception is that your SIGALRM must include a call
                       to alarm_expired(). Actually, DR7 doesn't require that
                       one must call wait_for_thread(), but it is good form to
                       do so anyway.

   Caveats:            This function is *not* thread-safe. At some point I will
                       produce a thread-safe version, but for now, this is all
                       there is.
 */

#include <errno.h>
#include <signal.h>
#include <kernel/OS.h>
#include <sys/time.h>

struct ALARM_STRUCT
{
    thread_id               thread;
    unsigned int            time;
};

static double           time_started = 0.0;
static thread_id        alarm_thread = -1;
static struct ALARM_STRUCT alarm_struct;
static long             do_alarm (void *);

void                    alarm_expired (void);

/*
   This should mimic the Unix/Posix alarm() function well enough, with the caveat
   that one's SIGALRM handler must call alarm_expired() to wait for the alarm
   thread to finish.
 */

unsigned int
alarm (unsigned int seconds)
{
    unsigned int            time_left = 0;

/* If an alarm is active, turn it off, saving the remaining time */
    if (alarm_thread != -1)
    {
        /* We'll be generous and count partial seconds as whole seconds. */
        time_left = alarm_struct.time - (unsigned int) ((system_time () - time_started) / 1000000.0);

        /* Kill the alarm thread, since we think it's still there */
        kill_thread (alarm_thread);

        /* We need to clean up as though the alarm occured. */
        alarm_expired ();
        time_started = 0, 0;
        alarm_struct.thread = -1;
        alarm_struct.time = 0;
    }

/*
   This was confusing. Linux appears to used signed int, while SunOS ans SysV/88
   use unsigned. I will use unsigned.
 */

/* Set a new alarm clock, if requested. */
    if (seconds > 0)
    {
        alarm_struct.thread = find_thread (NULL);
        alarm_struct.time = seconds;
        time_started = system_time ();
        alarm_thread = spawn_thread (do_alarm,
                                     "alarm_thread",
                                     B_NORMAL_PRIORITY,
                                     (void *) &alarm_struct);
        resume_thread (alarm_thread);
    }

/* Now return [unused time | 0] */
    return ((unsigned int) time_left);
}

/*
   This function is the departure from Unix/Posix alarm handling. In the case
   of Be's missing alarm() function, this stuff needs to be done in the SIGALRM
   handler. When Be implements alarm(), this function call can be eliminated from
   user's SIGALRM signal handlers. Technically, under DR7, one really doesn't need
   wait_for_thread(), but it is good form to use it anyway.
 */

void
alarm_expired (void)
{
    long                    ret_val;

    if (alarm_thread != -1)
    {
        wait_for_thread (alarm_thread, &ret_val);
        alarm_thread = -1;
    }
}

/*
   This is the function that snoozes the requisite number of seconds and then
   SIGALRMs the calling thread. Note that kill() wants a pid_t arg, whilst Be
   uses thread_id; currently they are both typdef'ed as long, but I'll do the
   cast anyway.
 */

static long
do_alarm (void *alarm_struct)
{
    snooze ((double) ((struct ALARM_STRUCT *) alarm_struct)->time * 1000000.0);
    kill ((pid_t) ((struct ALARM_STRUCT *) alarm_struct)->thread, SIGALRM);
    time_started = 0;
    ((struct ALARM_STRUCT *)alarm_struct)->thread = -1;
    ((struct ALARM_STRUCT *)alarm_struct)->time = 0;
}