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