In some cases the license-identification header was missing, so I added that as well. Replaced the remaining headers that specifically identified "Versions 3.13.7 and higher". Makefiles and the build system were deliberately excluded.
249 lines
6.1 KiB
C
249 lines
6.1 KiB
C
/*************************************************************************\
|
|
* Copyright (c) 2012 Helmholtz-Zentrum Berlin
|
|
* fuer Materialien und Energie GmbH.
|
|
* Copyright (c) 2012 ITER Organization.
|
|
* SPDX-License-Identifier: EPICS
|
|
* EPICS BASE is distributed subject to a Software License Agreement found
|
|
* in file LICENSE that is included with this distribution.
|
|
\*************************************************************************/
|
|
|
|
/*
|
|
* Author: Ralph Lange <Ralph.Lange@gmx.de>
|
|
*
|
|
* based on epicsMutexTest by Marty Kraimer and Jeff Hill
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <time.h>
|
|
|
|
#include "epicsTime.h"
|
|
#include "epicsThread.h"
|
|
#include "epicsAtomic.h"
|
|
#include "epicsSpin.h"
|
|
#include "epicsEvent.h"
|
|
#include "errlog.h"
|
|
#include "epicsUnitTest.h"
|
|
#include "testMain.h"
|
|
|
|
typedef struct info {
|
|
int threadnum;
|
|
epicsSpinId spin;
|
|
int *counter;
|
|
int rounds;
|
|
epicsEventId done;
|
|
} info;
|
|
|
|
#define spinDelay 0.016667
|
|
|
|
void spinThread(void *arg)
|
|
{
|
|
info *pinfo = (info *) arg;
|
|
testDiag("spinThread %d starting", pinfo->threadnum);
|
|
epicsThreadSleep(0.1); /* Try to align threads */
|
|
while (pinfo->rounds--) {
|
|
epicsThreadSleep(spinDelay);
|
|
epicsSpinLock(pinfo->spin);
|
|
pinfo->counter[0]++;
|
|
epicsSpinUnlock(pinfo->spin);
|
|
}
|
|
testDiag("spinThread %d exiting", pinfo->threadnum);
|
|
epicsEventSignal(pinfo->done);
|
|
}
|
|
|
|
static void lockPair(struct epicsSpin *spin)
|
|
{
|
|
epicsSpinLock(spin);
|
|
epicsSpinUnlock(spin);
|
|
}
|
|
|
|
static void tenLockPairs(struct epicsSpin *spin)
|
|
{
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
lockPair(spin);
|
|
}
|
|
|
|
static void tenLockPairsSquared(struct epicsSpin *spin)
|
|
{
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
tenLockPairs(spin);
|
|
}
|
|
|
|
void epicsSpinPerformance ()
|
|
{
|
|
static const unsigned N = 10000;
|
|
unsigned i;
|
|
epicsSpinId spin;
|
|
epicsTimeStamp begin;
|
|
epicsTimeStamp end;
|
|
double delay;
|
|
|
|
/* Initialize spinlock */
|
|
spin = epicsSpinCreate();
|
|
if (!spin)
|
|
testAbort("epicsSpinCreate() returned NULL");
|
|
|
|
/* test a single lock pair */
|
|
epicsTimeGetCurrent(&begin);
|
|
for ( i = 0; i < N; i++ ) {
|
|
tenLockPairsSquared(spin);
|
|
}
|
|
epicsTimeGetCurrent(&end);
|
|
|
|
delay = epicsTimeDiffInSeconds(&end, &begin);
|
|
delay /= N * 100u; /* convert to delay per lock pair */
|
|
delay *= 1e6; /* convert to micro seconds */
|
|
testDiag("lock()*1/unlock()*1 takes %f microseconds", delay);
|
|
epicsSpinDestroy(spin);
|
|
}
|
|
|
|
struct verifyTryLock;
|
|
|
|
struct verifyTryLockEnt {
|
|
epicsEventId done;
|
|
struct verifyTryLock *main;
|
|
};
|
|
|
|
struct verifyTryLock {
|
|
epicsSpinId spin;
|
|
int flag;
|
|
struct verifyTryLockEnt *ents;
|
|
};
|
|
|
|
static void verifyTryLockThread(void *pArg)
|
|
{
|
|
struct verifyTryLockEnt *pVerify =
|
|
(struct verifyTryLockEnt *) pArg;
|
|
|
|
while(epicsAtomicGetIntT(&pVerify->main->flag)==0) {
|
|
int ret = epicsSpinTryLock(pVerify->main->spin);
|
|
if(ret!=0) {
|
|
epicsAtomicCmpAndSwapIntT(&pVerify->main->flag, 0, ret);
|
|
break;
|
|
} else
|
|
epicsSpinUnlock(pVerify->main->spin);
|
|
}
|
|
|
|
epicsEventSignal(pVerify->done);
|
|
}
|
|
|
|
/* Start one thread per CPU which will all try lock
|
|
* the same spinlock. They break as soon as one
|
|
* fails to take the lock.
|
|
*/
|
|
static void verifyTryLock()
|
|
{
|
|
int N, i;
|
|
struct verifyTryLock verify;
|
|
|
|
N = epicsThreadGetCPUs();
|
|
if(N==1) {
|
|
testSkip(1, "verifyTryLock() only for SMP systems");
|
|
return;
|
|
}
|
|
|
|
verify.flag = 0;
|
|
verify.spin = epicsSpinMustCreate();
|
|
|
|
testDiag("Starting %d spinners", N);
|
|
|
|
verify.ents = calloc(N, sizeof(*verify.ents));
|
|
for(i=0; i<N; i++) {
|
|
verify.ents[i].main = &verify;
|
|
verify.ents[i].done = epicsEventMustCreate(epicsEventEmpty);
|
|
epicsThreadMustCreate("verifyTryLockThread", 40,
|
|
epicsThreadGetStackSize(epicsThreadStackSmall),
|
|
verifyTryLockThread, &verify.ents[i]);
|
|
}
|
|
|
|
testDiag("All started");
|
|
|
|
for(i=0; i<N; i++) {
|
|
epicsEventMustWait(verify.ents[i].done);
|
|
epicsEventDestroy(verify.ents[i].done);
|
|
}
|
|
|
|
testDiag("All done");
|
|
|
|
testOk(verify.flag==1, "epicsTryLock returns %d (expect 1)", verify.flag);
|
|
|
|
epicsSpinDestroy(verify.spin);
|
|
free(verify.ents);
|
|
}
|
|
|
|
MAIN(epicsSpinTest)
|
|
{
|
|
const int nthreads = 3;
|
|
const int nrounds = 500;
|
|
unsigned int stackSize;
|
|
epicsThreadId *id;
|
|
int i, counter;
|
|
char **name;
|
|
info **pinfo;
|
|
epicsSpinId spin;
|
|
|
|
testPlan(2);
|
|
|
|
verifyTryLock();
|
|
|
|
spin = epicsSpinCreate();
|
|
if (!spin)
|
|
testAbort("epicsSpinCreate() returned NULL");
|
|
|
|
id = (epicsThreadId *) calloc(nthreads, sizeof(epicsThreadId));
|
|
name = (char **) calloc(nthreads, sizeof(char *));
|
|
pinfo = (info **) calloc(nthreads, sizeof(info *));
|
|
stackSize = epicsThreadGetStackSize(epicsThreadStackSmall);
|
|
counter = 0;
|
|
for (i = 0; i < nthreads; i++) {
|
|
name[i] = (char *) calloc(10, sizeof(char));
|
|
sprintf(name[i],"task%d",i);
|
|
pinfo[i] = (info *) calloc(1, sizeof(info));
|
|
pinfo[i]->threadnum = i;
|
|
pinfo[i]->spin = spin;
|
|
pinfo[i]->counter = &counter;
|
|
pinfo[i]->rounds = nrounds;
|
|
pinfo[i]->done = epicsEventMustCreate(epicsEventEmpty);
|
|
id[i] = epicsThreadCreate(name[i], 40, stackSize,
|
|
spinThread,
|
|
pinfo[i]);
|
|
}
|
|
for (i = 0; i < nthreads; i++) {
|
|
epicsEventMustWait(pinfo[i]->done);
|
|
epicsEventDestroy(pinfo[i]->done);
|
|
free(name[i]);
|
|
free(pinfo[i]);
|
|
}
|
|
testOk(counter == nthreads * nrounds, "Loops run = %d (expecting %d)",
|
|
counter, nthreads * nrounds);
|
|
|
|
free(pinfo);
|
|
free(name);
|
|
free(id);
|
|
epicsSpinDestroy(spin);
|
|
|
|
epicsSpinPerformance();
|
|
|
|
return testDone();
|
|
}
|