/*************************************************************************\ * 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. * SPDX-License-Identifier: EPICS * 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 #include #include #include #include #include #include #include #include "epicsStdio.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)); epicsSnprintf(name[i], 16, "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(); }