2008-06-06 12:36:51 +00:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-10 19:01:43 +00:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2008-06-06 12:36:51 +00:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
** 1996 - Netscape Communications Corporation
|
|
|
|
**
|
|
|
|
** Name: alarmtst.c
|
|
|
|
**
|
|
|
|
** Description: Test alarms
|
|
|
|
**
|
|
|
|
** Modification History:
|
|
|
|
** 13-May-97 AGarcia- Converted the test to accomodate the debug_mode flag.
|
|
|
|
** The debug mode will print all of the printfs associated with this test.
|
|
|
|
** The regress mode will be the default mode. Since the regress tool limits
|
|
|
|
** the output to a one line status:PASS or FAIL,all of the printf statements
|
|
|
|
** have been handled with an if (debug_mode) statement.
|
|
|
|
** 04-June-97 AGarcia removed the Test_Result function. Regress tool has been updated to
|
|
|
|
** recognize the return code from tha main program.
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
** Includes
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
#include "prlog.h"
|
|
|
|
#include "prinit.h"
|
|
|
|
#include "obsolete/pralarm.h"
|
|
|
|
#include "prlock.h"
|
|
|
|
#include "prlong.h"
|
|
|
|
#include "prcvar.h"
|
|
|
|
#include "prinrval.h"
|
|
|
|
#include "prtime.h"
|
|
|
|
|
|
|
|
/* Used to get the command line option */
|
|
|
|
#include "plgetopt.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#if defined(XP_UNIX)
|
|
|
|
#include <sys/time.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static PRIntn debug_mode;
|
|
|
|
static PRIntn failed_already=0;
|
|
|
|
static PRThreadScope thread_scope = PR_LOCAL_THREAD;
|
|
|
|
|
|
|
|
typedef struct notifyData {
|
|
|
|
PRLock *ml;
|
|
|
|
PRCondVar *child;
|
|
|
|
PRCondVar *parent;
|
|
|
|
PRBool pending;
|
|
|
|
PRUint32 counter;
|
|
|
|
} NotifyData;
|
|
|
|
|
|
|
|
static void Notifier(void *arg)
|
|
|
|
{
|
|
|
|
NotifyData *notifyData = (NotifyData*)arg;
|
|
|
|
PR_Lock(notifyData->ml);
|
|
|
|
while (notifyData->counter > 0)
|
|
|
|
{
|
|
|
|
while (!notifyData->pending)
|
|
|
|
PR_WaitCondVar(notifyData->child, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
notifyData->counter -= 1;
|
|
|
|
notifyData->pending = PR_FALSE;
|
|
|
|
PR_NotifyCondVar(notifyData->parent);
|
|
|
|
}
|
|
|
|
PR_Unlock(notifyData->ml);
|
|
|
|
} /* Notifier */
|
|
|
|
/***********************************************************************
|
|
|
|
** PRIVATE FUNCTION: ConditionNotify
|
|
|
|
** DESCRIPTION:
|
|
|
|
**
|
|
|
|
** INPUTS: loops
|
|
|
|
** OUTPUTS: None
|
|
|
|
** RETURN: overhead
|
|
|
|
** SIDE EFFECTS:
|
|
|
|
**
|
|
|
|
** RESTRICTIONS:
|
|
|
|
** None
|
|
|
|
** MEMORY: NA
|
|
|
|
** ALGORITHM:
|
|
|
|
**
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
static PRIntervalTime ConditionNotify(PRUint32 loops)
|
|
|
|
{
|
|
|
|
PRThread *thread;
|
|
|
|
NotifyData notifyData;
|
|
|
|
PRIntervalTime timein, overhead;
|
|
|
|
|
|
|
|
timein = PR_IntervalNow();
|
|
|
|
|
|
|
|
notifyData.counter = loops;
|
|
|
|
notifyData.ml = PR_NewLock();
|
|
|
|
notifyData.child = PR_NewCondVar(notifyData.ml);
|
|
|
|
notifyData.parent = PR_NewCondVar(notifyData.ml);
|
|
|
|
thread = PR_CreateThread(
|
|
|
|
PR_USER_THREAD, Notifier, ¬ifyData,
|
|
|
|
PR_GetThreadPriority(PR_GetCurrentThread()),
|
|
|
|
thread_scope, PR_JOINABLE_THREAD, 0);
|
|
|
|
|
|
|
|
overhead = PR_IntervalNow() - timein; /* elapsed so far */
|
|
|
|
|
|
|
|
PR_Lock(notifyData.ml);
|
|
|
|
while (notifyData.counter > 0)
|
|
|
|
{
|
|
|
|
notifyData.pending = PR_TRUE;
|
|
|
|
PR_NotifyCondVar(notifyData.child);
|
|
|
|
while (notifyData.pending)
|
|
|
|
PR_WaitCondVar(notifyData.parent, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
}
|
|
|
|
PR_Unlock(notifyData.ml);
|
|
|
|
|
|
|
|
timein = PR_IntervalNow();
|
|
|
|
|
|
|
|
(void)PR_JoinThread(thread);
|
|
|
|
PR_DestroyCondVar(notifyData.child);
|
|
|
|
PR_DestroyCondVar(notifyData.parent);
|
|
|
|
PR_DestroyLock(notifyData.ml);
|
|
|
|
|
|
|
|
overhead += (PR_IntervalNow() - timein); /* more overhead */
|
|
|
|
|
|
|
|
return overhead;
|
|
|
|
} /* ConditionNotify */
|
|
|
|
|
|
|
|
static PRIntervalTime ConditionTimeout(PRUint32 loops)
|
|
|
|
{
|
|
|
|
PRUintn count;
|
|
|
|
PRIntervalTime overhead, timein = PR_IntervalNow();
|
|
|
|
|
|
|
|
PRLock *ml = PR_NewLock();
|
|
|
|
PRCondVar *cv = PR_NewCondVar(ml);
|
|
|
|
PRIntervalTime interval = PR_MillisecondsToInterval(50);
|
|
|
|
|
|
|
|
overhead = PR_IntervalNow() - timein;
|
|
|
|
|
|
|
|
PR_Lock(ml);
|
|
|
|
for (count = 0; count < loops; ++count)
|
|
|
|
{
|
|
|
|
overhead += interval;
|
|
|
|
PR_ASSERT(PR_WaitCondVar(cv, interval) == PR_SUCCESS);
|
|
|
|
}
|
|
|
|
PR_Unlock(ml);
|
|
|
|
|
|
|
|
timein = PR_IntervalNow();
|
|
|
|
PR_DestroyCondVar(cv);
|
|
|
|
PR_DestroyLock(ml);
|
|
|
|
overhead += (PR_IntervalNow() - timein);
|
|
|
|
|
|
|
|
return overhead;
|
|
|
|
} /* ConditionTimeout */
|
|
|
|
|
|
|
|
typedef struct AlarmData {
|
|
|
|
PRLock *ml;
|
|
|
|
PRCondVar *cv;
|
|
|
|
PRUint32 rate, late, times;
|
|
|
|
PRIntervalTime duration, timein, period;
|
|
|
|
} AlarmData;
|
|
|
|
|
|
|
|
static PRBool AlarmFn1(PRAlarmID *id, void *clientData, PRUint32 late)
|
|
|
|
{
|
|
|
|
PRStatus rv = PR_SUCCESS;
|
|
|
|
PRBool keepGoing, resetAlarm;
|
|
|
|
PRIntervalTime interval, now = PR_IntervalNow();
|
|
|
|
AlarmData *ad = (AlarmData*)clientData;
|
|
|
|
|
|
|
|
PR_Lock(ad->ml);
|
|
|
|
ad->late += late;
|
|
|
|
ad->times += 1;
|
|
|
|
keepGoing = ((PRIntervalTime)(now - ad->timein) < ad->duration) ?
|
|
|
|
PR_TRUE : PR_FALSE;
|
|
|
|
if (!keepGoing)
|
|
|
|
rv = PR_NotifyCondVar(ad->cv);
|
|
|
|
resetAlarm = ((ad->times % 31) == 0) ? PR_TRUE : PR_FALSE;
|
|
|
|
|
|
|
|
interval = (ad->period + ad->rate - 1) / ad->rate;
|
|
|
|
if (!late && (interval > 10))
|
|
|
|
{
|
|
|
|
interval &= (now & 0x03) + 1;
|
|
|
|
PR_WaitCondVar(ad->cv, interval);
|
|
|
|
}
|
|
|
|
|
|
|
|
PR_Unlock(ad->ml);
|
|
|
|
|
|
|
|
if (rv != PR_SUCCESS)
|
|
|
|
{
|
|
|
|
if (!debug_mode) failed_already=1;
|
|
|
|
else
|
|
|
|
printf("AlarmFn: notify status: FAIL\n");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resetAlarm)
|
|
|
|
{
|
|
|
|
ad->rate += 3;
|
|
|
|
ad->late = ad->times = 0;
|
|
|
|
if (PR_ResetAlarm(id, ad->period, ad->rate) != PR_SUCCESS)
|
|
|
|
{
|
|
|
|
if (!debug_mode)
|
|
|
|
failed_already=1;
|
|
|
|
else
|
|
|
|
printf("AlarmFn: Resetting alarm status: FAIL\n");
|
|
|
|
|
|
|
|
keepGoing = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return keepGoing;
|
|
|
|
} /* AlarmFn1 */
|
|
|
|
|
|
|
|
static PRIntervalTime Alarms1(PRUint32 loops)
|
|
|
|
{
|
|
|
|
PRAlarm *alarm;
|
|
|
|
AlarmData ad;
|
|
|
|
PRIntervalTime overhead, timein = PR_IntervalNow();
|
|
|
|
PRIntervalTime duration = PR_SecondsToInterval(3);
|
|
|
|
|
|
|
|
PRLock *ml = PR_NewLock();
|
|
|
|
PRCondVar *cv = PR_NewCondVar(ml);
|
|
|
|
|
|
|
|
ad.ml = ml;
|
|
|
|
ad.cv = cv;
|
|
|
|
ad.rate = 1;
|
|
|
|
ad.times = loops;
|
|
|
|
ad.late = ad.times = 0;
|
|
|
|
ad.duration = duration;
|
|
|
|
ad.timein = PR_IntervalNow();
|
|
|
|
ad.period = PR_SecondsToInterval(1);
|
|
|
|
|
|
|
|
alarm = PR_CreateAlarm();
|
|
|
|
|
|
|
|
(void)PR_SetAlarm(
|
|
|
|
alarm, ad.period, ad.rate, AlarmFn1, &ad);
|
|
|
|
|
|
|
|
overhead = PR_IntervalNow() - timein;
|
|
|
|
|
|
|
|
PR_Lock(ml);
|
|
|
|
while ((PRIntervalTime)(PR_IntervalNow() - ad.timein) < duration)
|
|
|
|
PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
PR_Unlock(ml);
|
|
|
|
|
|
|
|
timein = PR_IntervalNow();
|
|
|
|
(void)PR_DestroyAlarm(alarm);
|
|
|
|
PR_DestroyCondVar(cv);
|
|
|
|
PR_DestroyLock(ml);
|
|
|
|
overhead += (PR_IntervalNow() - timein);
|
|
|
|
|
|
|
|
return duration + overhead;
|
|
|
|
} /* Alarms1 */
|
|
|
|
|
|
|
|
static PRBool AlarmFn2(PRAlarmID *id, void *clientData, PRUint32 late)
|
|
|
|
{
|
|
|
|
PRBool keepGoing;
|
|
|
|
PRStatus rv = PR_SUCCESS;
|
|
|
|
AlarmData *ad = (AlarmData*)clientData;
|
|
|
|
PRIntervalTime interval, now = PR_IntervalNow();
|
|
|
|
|
|
|
|
PR_Lock(ad->ml);
|
|
|
|
ad->times += 1;
|
|
|
|
keepGoing = ((PRIntervalTime)(now - ad->timein) < ad->duration) ?
|
|
|
|
PR_TRUE : PR_FALSE;
|
|
|
|
interval = (ad->period + ad->rate - 1) / ad->rate;
|
|
|
|
|
|
|
|
if (!late && (interval > 10))
|
|
|
|
{
|
|
|
|
interval &= (now & 0x03) + 1;
|
|
|
|
PR_WaitCondVar(ad->cv, interval);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!keepGoing) rv = PR_NotifyCondVar(ad->cv);
|
|
|
|
|
|
|
|
PR_Unlock(ad->ml);
|
|
|
|
|
|
|
|
|
|
|
|
if (rv != PR_SUCCESS)
|
|
|
|
failed_already=1;;
|
|
|
|
|
|
|
|
return keepGoing;
|
|
|
|
} /* AlarmFn2 */
|
|
|
|
|
|
|
|
static PRIntervalTime Alarms2(PRUint32 loops)
|
|
|
|
{
|
|
|
|
PRStatus rv;
|
|
|
|
PRAlarm *alarm;
|
|
|
|
PRIntervalTime overhead, timein = PR_IntervalNow();
|
|
|
|
AlarmData ad;
|
|
|
|
PRIntervalTime duration = PR_SecondsToInterval(30);
|
|
|
|
|
|
|
|
PRLock *ml = PR_NewLock();
|
|
|
|
PRCondVar *cv = PR_NewCondVar(ml);
|
|
|
|
|
|
|
|
ad.ml = ml;
|
|
|
|
ad.cv = cv;
|
|
|
|
ad.rate = 1;
|
|
|
|
ad.times = loops;
|
|
|
|
ad.late = ad.times = 0;
|
|
|
|
ad.duration = duration;
|
|
|
|
ad.timein = PR_IntervalNow();
|
|
|
|
ad.period = PR_SecondsToInterval(1);
|
|
|
|
|
|
|
|
alarm = PR_CreateAlarm();
|
|
|
|
|
|
|
|
(void)PR_SetAlarm(
|
|
|
|
alarm, ad.period, ad.rate, AlarmFn2, &ad);
|
|
|
|
|
|
|
|
overhead = PR_IntervalNow() - timein;
|
|
|
|
|
|
|
|
PR_Lock(ml);
|
|
|
|
while ((PRIntervalTime)(PR_IntervalNow() - ad.timein) < duration)
|
|
|
|
PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
PR_Unlock(ml);
|
|
|
|
|
|
|
|
timein = PR_IntervalNow();
|
|
|
|
|
|
|
|
rv = PR_DestroyAlarm(alarm);
|
|
|
|
if (rv != PR_SUCCESS)
|
|
|
|
{
|
|
|
|
if (!debug_mode)
|
|
|
|
failed_already=1;
|
|
|
|
else
|
|
|
|
printf("***Destroying alarm status: FAIL\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PR_DestroyCondVar(cv);
|
|
|
|
PR_DestroyLock(ml);
|
|
|
|
|
|
|
|
overhead += (PR_IntervalNow() - timein);
|
|
|
|
|
|
|
|
return duration + overhead;
|
|
|
|
} /* Alarms2 */
|
|
|
|
|
|
|
|
static PRIntervalTime Alarms3(PRUint32 loops)
|
|
|
|
{
|
|
|
|
PRIntn i;
|
|
|
|
PRStatus rv;
|
|
|
|
PRAlarm *alarm;
|
|
|
|
AlarmData ad[3];
|
|
|
|
PRIntervalTime duration = PR_SecondsToInterval(30);
|
|
|
|
PRIntervalTime overhead, timein = PR_IntervalNow();
|
|
|
|
|
|
|
|
PRLock *ml = PR_NewLock();
|
|
|
|
PRCondVar *cv = PR_NewCondVar(ml);
|
|
|
|
|
|
|
|
for (i = 0; i < 3; ++i)
|
|
|
|
{
|
|
|
|
ad[i].ml = ml;
|
|
|
|
ad[i].cv = cv;
|
|
|
|
ad[i].rate = 1;
|
|
|
|
ad[i].times = loops;
|
|
|
|
ad[i].duration = duration;
|
|
|
|
ad[i].late = ad[i].times = 0;
|
|
|
|
ad[i].timein = PR_IntervalNow();
|
|
|
|
ad[i].period = PR_SecondsToInterval(1);
|
|
|
|
|
|
|
|
/* more loops, faster rate => same elapsed time */
|
|
|
|
ad[i].times = (i + 1) * loops;
|
|
|
|
ad[i].rate = (i + 1) * 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
alarm = PR_CreateAlarm();
|
|
|
|
|
|
|
|
for (i = 0; i < 3; ++i)
|
|
|
|
{
|
|
|
|
(void)PR_SetAlarm(
|
|
|
|
alarm, ad[i].period, ad[i].rate,
|
|
|
|
AlarmFn2, &ad[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
overhead = PR_IntervalNow() - timein;
|
|
|
|
|
|
|
|
PR_Lock(ml);
|
|
|
|
for (i = 0; i < 3; ++i)
|
|
|
|
{
|
|
|
|
while ((PRIntervalTime)(PR_IntervalNow() - ad[i].timein) < duration)
|
|
|
|
PR_WaitCondVar(cv, PR_INTERVAL_NO_TIMEOUT);
|
|
|
|
}
|
|
|
|
PR_Unlock(ml);
|
|
|
|
|
|
|
|
timein = PR_IntervalNow();
|
|
|
|
|
|
|
|
if (debug_mode)
|
|
|
|
printf
|
|
|
|
("Alarms3 finished at %u, %u, %u\n",
|
|
|
|
ad[0].timein, ad[1].timein, ad[2].timein);
|
|
|
|
|
|
|
|
rv = PR_DestroyAlarm(alarm);
|
|
|
|
if (rv != PR_SUCCESS)
|
|
|
|
{
|
|
|
|
if (!debug_mode)
|
|
|
|
failed_already=1;
|
|
|
|
else
|
|
|
|
printf("***Destroying alarm status: FAIL\n");
|
|
|
|
}
|
|
|
|
PR_DestroyCondVar(cv);
|
|
|
|
PR_DestroyLock(ml);
|
|
|
|
|
|
|
|
overhead += (duration / 3);
|
|
|
|
overhead += (PR_IntervalNow() - timein);
|
|
|
|
|
|
|
|
return overhead;
|
|
|
|
} /* Alarms3 */
|
|
|
|
|
|
|
|
static PRUint32 TimeThis(
|
|
|
|
const char *msg, PRUint32 (*func)(PRUint32 loops), PRUint32 loops)
|
|
|
|
{
|
|
|
|
PRUint32 overhead, usecs;
|
|
|
|
PRIntervalTime predicted, timein, timeout, ticks;
|
|
|
|
|
|
|
|
if (debug_mode)
|
|
|
|
printf("Testing %s ...", msg);
|
|
|
|
|
|
|
|
timein = PR_IntervalNow();
|
|
|
|
predicted = func(loops);
|
|
|
|
timeout = PR_IntervalNow();
|
|
|
|
|
|
|
|
if (debug_mode)
|
|
|
|
printf(" done\n");
|
|
|
|
|
|
|
|
ticks = timeout - timein;
|
|
|
|
usecs = PR_IntervalToMicroseconds(ticks);
|
|
|
|
overhead = PR_IntervalToMicroseconds(predicted);
|
|
|
|
|
|
|
|
if(ticks < predicted)
|
|
|
|
{
|
|
|
|
if (debug_mode) {
|
|
|
|
printf("\tFinished in negative time\n");
|
|
|
|
printf("\tpredicted overhead was %d usecs\n", overhead);
|
|
|
|
printf("\ttest completed in %d usecs\n\n", usecs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (debug_mode)
|
|
|
|
printf(
|
|
|
|
"\ttotal: %d usecs\n\toverhead: %d usecs\n\tcost: %6.3f usecs\n\n",
|
|
|
|
usecs, overhead, ((double)(usecs - overhead) / (double)loops));
|
|
|
|
}
|
|
|
|
|
|
|
|
return overhead;
|
|
|
|
} /* TimeThis */
|
|
|
|
|
|
|
|
int prmain(int argc, char** argv)
|
|
|
|
{
|
|
|
|
PRUint32 cpu, cpus = 0, loops = 0;
|
|
|
|
|
|
|
|
/* The command line argument: -d is used to determine if the test is being run
|
|
|
|
in debug mode. The regress tool requires only one line output:PASS or FAIL.
|
|
|
|
All of the printfs associated with this test has been handled with a if (debug_mode)
|
|
|
|
test.
|
|
|
|
Usage: test_name [-d]
|
|
|
|
*/
|
|
|
|
PLOptStatus os;
|
|
|
|
PLOptState *opt = PL_CreateOptState(argc, argv, "Gdl:c:");
|
|
|
|
while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
|
|
|
|
{
|
|
|
|
if (PL_OPT_BAD == os) continue;
|
|
|
|
switch (opt->option)
|
|
|
|
{
|
|
|
|
case 'G': /* GLOBAL threads */
|
|
|
|
thread_scope = PR_GLOBAL_THREAD;
|
|
|
|
break;
|
|
|
|
case 'd': /* debug mode */
|
|
|
|
debug_mode = 1;
|
|
|
|
break;
|
|
|
|
case 'l': /* loop count */
|
|
|
|
loops = atoi(opt->value);
|
|
|
|
break;
|
|
|
|
case 'c': /* concurrency limit */
|
|
|
|
cpus = atoi(opt->value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
PL_DestroyOptState(opt);
|
|
|
|
|
|
|
|
|
|
|
|
if (cpus == 0) cpus = 1;
|
|
|
|
if (loops == 0) loops = 4;
|
|
|
|
|
|
|
|
if (debug_mode)
|
|
|
|
printf("Alarm: Using %d loops\n", loops);
|
|
|
|
|
|
|
|
if (debug_mode)
|
|
|
|
printf("Alarm: Using %d cpu(s)\n", cpus);
|
|
|
|
|
|
|
|
for (cpu = 1; cpu <= cpus; ++cpu)
|
|
|
|
{
|
|
|
|
if (debug_mode)
|
|
|
|
printf("\nAlarm: Using %d CPU(s)\n", cpu);
|
|
|
|
|
|
|
|
PR_SetConcurrency(cpu);
|
|
|
|
|
|
|
|
/* some basic time test */
|
|
|
|
(void)TimeThis("ConditionNotify", ConditionNotify, loops);
|
|
|
|
(void)TimeThis("ConditionTimeout", ConditionTimeout, loops);
|
|
|
|
(void)TimeThis("Alarms1", Alarms1, loops);
|
|
|
|
(void)TimeThis("Alarms2", Alarms2, loops);
|
|
|
|
(void)TimeThis("Alarms3", Alarms3, loops);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
|
|
|
PR_Initialize(prmain, argc, argv, 0);
|
|
|
|
PR_STDIO_INIT();
|
|
|
|
if (failed_already) return 1;
|
|
|
|
else return 0;
|
|
|
|
|
|
|
|
} /* main */
|
|
|
|
|
|
|
|
|
|
|
|
/* alarmtst.c */
|