blob: 7ae9d4fd105c0b7128b5a96c68797b24f0463710 [file] [log] [blame] [edit]
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Netscape Portable Runtime (NSPR).
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998-2000
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/***********************************************************************
** 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, &notifyData,
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 */