271 lines
8.0 KiB
C++
271 lines
8.0 KiB
C++
/*************************************************************************\
|
|
* Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne
|
|
* National Laboratory.
|
|
* Copyright (c) 2002 The Regents of the University of California, as
|
|
* Operator of Los Alamos National Laboratory.
|
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
/* epicsEventTest.cpp */
|
|
|
|
/* Author: Marty Kraimer Date: 26JAN2000 */
|
|
/* timeout accuracy tests by Jeff Hill */
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
|
|
#include "epicsThread.h"
|
|
#include "epicsEvent.h"
|
|
#include "epicsMutex.h"
|
|
#include "epicsRingPointer.h"
|
|
#include "epicsTime.h"
|
|
#include "errlog.h"
|
|
#include "epicsUnitTest.h"
|
|
#include "testMain.h"
|
|
|
|
|
|
typedef struct info {
|
|
epicsEventId event;
|
|
epicsMutexId lockRing;
|
|
int quit;
|
|
epicsRingPointerId ring;
|
|
} info;
|
|
|
|
extern "C" {
|
|
|
|
static void consumer(void *arg)
|
|
{
|
|
info *pinfo = (info *)arg;
|
|
int errors = 0;
|
|
|
|
testDiag("consumer: starting");
|
|
while (!pinfo->quit) {
|
|
epicsEventStatus status = epicsEventWait(pinfo->event);
|
|
if (status != epicsEventOK) {
|
|
testDiag("consumer: epicsEventWait returned %d", status);
|
|
errors++;
|
|
}
|
|
while (epicsRingPointerGetUsed(pinfo->ring) >= 2) {
|
|
epicsRingPointerId message[2];
|
|
for (int i = 0; i < 2; i++) {
|
|
message[i] = epicsRingPointerPop(pinfo->ring);
|
|
if (message[i] == 0) {
|
|
testDiag("consumer: epicsRingPointerPop returned 0");
|
|
errors++;
|
|
}
|
|
}
|
|
if (message[0] != message[1]) {
|
|
testDiag("consumer: message %p %p\n", message[0], message[1]);
|
|
errors++;
|
|
}
|
|
}
|
|
}
|
|
testOk(errors == 0, "consumer: errors = %d", errors);
|
|
}
|
|
|
|
static void producer(void *arg)
|
|
{
|
|
info *pinfo = (info *)arg;
|
|
const char *name = epicsThreadGetNameSelf();
|
|
epicsThreadId myId = epicsThreadGetIdSelf();
|
|
int errors = 0;
|
|
int ntimes = 0;
|
|
|
|
testDiag("%s: starting", name);
|
|
while(!pinfo->quit) {
|
|
++ntimes;
|
|
epicsMutexLockStatus status = epicsMutexLock(pinfo->lockRing);
|
|
if (status != epicsMutexLockOK) {
|
|
testDiag("%s: epicsMutexLock returned %d", name, status);
|
|
errors++;
|
|
}
|
|
if (epicsRingPointerGetFree(pinfo->ring) >= 2) {
|
|
for (int i = 0; i < 2; i++) {
|
|
if (!epicsRingPointerPush(pinfo->ring, myId)) {
|
|
testDiag("%s: epicsRingPointerPush fail", name);
|
|
errors++;
|
|
}
|
|
if (i == 0 && (ntimes % 4 == 0))
|
|
epicsThreadSleep(0.1);
|
|
}
|
|
} else {
|
|
testFail("%s: ring buffer full", name);
|
|
errors++;
|
|
}
|
|
epicsMutexUnlock(pinfo->lockRing);
|
|
epicsThreadSleep(1.0);
|
|
epicsEventMustTrigger(pinfo->event);
|
|
}
|
|
testOk(errors == 0, "%s: errors = %d", name, errors);
|
|
}
|
|
|
|
#define SLEEPERCOUNT 3
|
|
struct wakeInfo {
|
|
epicsEventId event;
|
|
epicsMutexId countMutex;
|
|
int count;
|
|
};
|
|
static void sleeper(void *arg)
|
|
{
|
|
struct wakeInfo *wp = (struct wakeInfo *)arg;
|
|
epicsEventMustWait(wp->event);
|
|
epicsMutexLock(wp->countMutex);
|
|
wp->count++;
|
|
epicsMutexUnlock(wp->countMutex);
|
|
}
|
|
static void eventWakeupTest(void)
|
|
{
|
|
struct wakeInfo wakeInfo, *wp = &wakeInfo;
|
|
int i, c;
|
|
|
|
wp->event = epicsEventMustCreate(epicsEventEmpty);
|
|
wp->countMutex = epicsMutexMustCreate();
|
|
wp->count = 0;
|
|
for (i = 0 ; i < SLEEPERCOUNT ; i++)
|
|
epicsThreadCreate("Sleeper",
|
|
epicsThreadPriorityScanHigh,
|
|
epicsThreadGetStackSize(epicsThreadStackSmall),
|
|
sleeper,
|
|
wp);
|
|
epicsThreadSleep(0.5);
|
|
epicsMutexLock(wp->countMutex);
|
|
c = wp->count;
|
|
epicsMutexUnlock(wp->countMutex);
|
|
testOk(c == 0, "all threads still sleeping");
|
|
for (i = 1 ; i <= SLEEPERCOUNT ; i++) {
|
|
epicsEventMustTrigger(wp->event);
|
|
epicsThreadSleep(0.5);
|
|
epicsMutexLock(wp->countMutex);
|
|
c = wp->count;
|
|
epicsMutexUnlock(wp->countMutex);
|
|
testOk(c == i, "%d thread%s awakened, expected %d", c, c == 1 ? "" : "s", i);
|
|
}
|
|
epicsEventDestroy(wp->event);
|
|
epicsMutexDestroy(wp->countMutex);
|
|
}
|
|
|
|
|
|
} // extern "C"
|
|
|
|
static double eventWaitCheckDelayError( const epicsEventId &id, const double & delay )
|
|
{
|
|
epicsEventWaitWithTimeout ( id, 0.000001 );
|
|
|
|
epicsTime beg = epicsTime::getMonotonic();
|
|
epicsEventWaitWithTimeout ( id, delay );
|
|
epicsTime end = epicsTime::getMonotonic();
|
|
double meas = end - beg;
|
|
double error = meas - delay;
|
|
testOk(error >= 0, "epicsEventWaitWithTimeout(%.6f) delay error %.6f sec",
|
|
delay, error);
|
|
return fabs(error);
|
|
}
|
|
|
|
#define WAITCOUNT 21
|
|
static void eventWaitTest()
|
|
{
|
|
#if defined(_WIN32) || defined(__rtems__) || defined(vxWorks)
|
|
testTodoBegin("Known issue with delay calculation");
|
|
#endif
|
|
|
|
epicsEventId event = epicsEventMustCreate(epicsEventEmpty);
|
|
double errorSum = eventWaitCheckDelayError(event, 0.0);
|
|
|
|
for (int i = 0; i < WAITCOUNT - 1; i++) {
|
|
double delay = ldexp ( 1.0 , -i );
|
|
errorSum += eventWaitCheckDelayError ( event, delay );
|
|
}
|
|
double meanError = errorSum / WAITCOUNT;
|
|
testOk(meanError < 0.05, "Mean delay error was %.6f sec", meanError);
|
|
|
|
testTodoEnd();
|
|
|
|
epicsEventDestroy(event);
|
|
}
|
|
|
|
|
|
MAIN(epicsEventTest)
|
|
{
|
|
const int nthreads = 3;
|
|
epicsThreadId *id;
|
|
char **name;
|
|
epicsEventId event;
|
|
int status;
|
|
|
|
testPlan(13 + SLEEPERCOUNT + WAITCOUNT);
|
|
|
|
event = epicsEventMustCreate(epicsEventEmpty);
|
|
|
|
status = epicsEventWaitWithTimeout(event, 0.0);
|
|
testOk(status == epicsEventWaitTimeout,
|
|
"epicsEventWaitWithTimeout(event, 0.0) = %d", status);
|
|
|
|
status = epicsEventWaitWithTimeout(event, 1.0);
|
|
testOk(status == epicsEventWaitTimeout,
|
|
"epicsEventWaitWithTimeout(event, 1.0) = %d", status);
|
|
|
|
status = epicsEventTryWait(event);
|
|
testOk(status == epicsEventWaitTimeout,
|
|
"epicsEventTryWait(event) = %d", status);
|
|
|
|
status = epicsEventTrigger(event);
|
|
testOk(status == epicsEventOK,
|
|
"epicsEventTrigger(event) = %d", status);
|
|
status = epicsEventWaitWithTimeout(event, 1.0);
|
|
testOk(status == epicsEventOK,
|
|
"epicsEventWaitWithTimeout(event, 1.0) = %d", status);
|
|
|
|
epicsEventMustTrigger(event);
|
|
status = epicsEventWaitWithTimeout(event,DBL_MAX);
|
|
testOk(status == epicsEventOK,
|
|
"epicsEventWaitWithTimeout(event, DBL_MAX) = %d", status);
|
|
|
|
epicsEventMustTrigger(event);
|
|
status = epicsEventTryWait(event);
|
|
testOk(status == epicsEventOK,
|
|
"epicsEventTryWait(event) = %d", status);
|
|
|
|
info *pinfo = (info *)calloc(1,sizeof(info));
|
|
pinfo->event = event;
|
|
pinfo->lockRing = epicsMutexCreate();
|
|
pinfo->ring = epicsRingPointerCreate(1024*2);
|
|
unsigned int stackSize = epicsThreadGetStackSize(epicsThreadStackSmall);
|
|
|
|
epicsThreadCreate("consumer", 50, stackSize, consumer, pinfo);
|
|
id = (epicsThreadId *)calloc(nthreads, sizeof(epicsThreadId));
|
|
name = (char **)calloc(nthreads, sizeof(char *));
|
|
for(int i = 0; i < nthreads; i++) {
|
|
name[i] = (char *)calloc(16, sizeof(char));
|
|
sprintf(name[i],"producer %d",i);
|
|
id[i] = epicsThreadCreate(name[i], 40, stackSize, producer, pinfo);
|
|
epicsThreadSleep(0.1);
|
|
}
|
|
epicsThreadSleep(5.0);
|
|
|
|
testDiag("setting quit");
|
|
pinfo->quit = 1;
|
|
epicsThreadSleep(2.0);
|
|
|
|
epicsEventMustTrigger(pinfo->event);
|
|
epicsThreadSleep(1.0);
|
|
|
|
eventWaitTest();
|
|
eventWakeupTest();
|
|
|
|
free(name);
|
|
free(id);
|
|
epicsRingPointerDelete(pinfo->ring);
|
|
epicsMutexDestroy(pinfo->lockRing);
|
|
epicsEventDestroy(event);
|
|
free(pinfo);
|
|
|
|
return testDone();
|
|
}
|