Files
epics-base/src/libCom/test/epicsAtomicTest.cpp
Michael Davidsaver 19680d7869 libCom/test: fixup epicsAtomicTest
Add testBasic to check basic op symantics.
Add anon namespace to hide symbols other than MAIN(epicsAtomicTest).
Replace using namespace with using for individual functions.
For RTEMS skip the tests using threads as the use of
RTEMS_NO_TIMESLICE prevents them from being meaningful
2016-03-01 10:57:26 -05:00

426 lines
12 KiB
C++

#include <stdlib.h>
#include <assert.h>
#include "epicsAtomic.h"
#include "epicsTime.h"
#include "epicsThread.h"
#include "epicsUnitTest.h"
#include "testMain.h"
namespace {
template < class T >
struct TestDataIncrDecr {
T m_testValue;
size_t m_testIterations;
};
template < class T >
struct TestDataAddSub {
T m_testValue;
size_t m_testIterations;
static const T delta = 17;
};
template < class T >
static void incr ( void *arg )
{
using epics::atomic::increment;
TestDataIncrDecr < T > * const pTestData =
reinterpret_cast < TestDataIncrDecr < T > * > ( arg );
increment ( pTestData->m_testValue );
increment ( pTestData->m_testIterations );
}
template < class T >
static void decr ( void *arg )
{
using epics::atomic::decrement;
using epics::atomic::increment;
TestDataIncrDecr < T > * const pTestData =
reinterpret_cast < TestDataIncrDecr < T > * > ( arg );
decrement ( pTestData->m_testValue );
increment ( pTestData->m_testIterations );
}
template < class T >
static void add ( void *arg )
{
using epics::atomic::add;
using epics::atomic::increment;
TestDataAddSub < T > * const pTestData =
reinterpret_cast < TestDataAddSub < T > * > ( arg );
add ( pTestData->m_testValue, TestDataAddSub < T > :: delta );
increment ( pTestData->m_testIterations );
}
template < class T >
static void sub ( void *arg )
{
using epics::atomic::subtract;
using epics::atomic::increment;
TestDataAddSub < T > * const pTestData =
reinterpret_cast < TestDataAddSub < T > * > ( arg );
subtract ( pTestData->m_testValue, TestDataAddSub < T > :: delta );
increment ( pTestData->m_testIterations );
}
template < class T >
struct TestDataCAS {
T m_testValue;
size_t m_testIterationsSet;
size_t m_testIterationsNotSet;
};
template < class T >
static T trueValue ();
template < class T >
static T falseValue ();
// int
template <>
inline int trueValue < int > () { return 1; }
template <>
inline int falseValue < int > () { return 0; }
// size_t
template <>
inline size_t trueValue < size_t > () { return 1u; }
template <>
inline size_t falseValue < size_t > () { return 0u; }
// EpicsAtomicPtrT
template <>
inline EpicsAtomicPtrT trueValue < EpicsAtomicPtrT > ()
{ static char c; return & c; }
template <>
inline EpicsAtomicPtrT falseValue < EpicsAtomicPtrT > ()
{ return 0u; }
template < class T >
static void cas ( void *arg )
{
using epics::atomic::set;
using epics::atomic::increment;
using epics::atomic::decrement;
using epics::atomic::compareAndSwap;
TestDataCAS < T > * const pTestData =
reinterpret_cast < TestDataCAS < T > * > ( arg );
/*
* intentionally waste cpu and maximize
* contention for the shared data
*/
increment ( pTestData->m_testIterationsNotSet );
while ( ! compareAndSwap ( pTestData->m_testValue,
falseValue < T > (),
trueValue < T > () ) ) {
}
decrement ( pTestData->m_testIterationsNotSet );
set ( pTestData->m_testValue, falseValue < T > () );
increment ( pTestData->m_testIterationsSet );
}
template < class T >
void testIncrDecr ()
{
using epics::atomic::set;
using epics::atomic::get;
static const size_t N = 90;
static const T NT = static_cast < T > ( N );
const unsigned int stackSize =
epicsThreadGetStackSize ( epicsThreadStackSmall );
TestDataIncrDecr < T > testData = { 0, N };
set ( testData.m_testValue, NT );
testOk ( get ( testData.m_testValue ) == NT,
"get returns initial incr/decr test data value that was set" );
set ( testData.m_testIterations, 0u );
testOk ( get ( testData.m_testIterations ) == 0u,
"get returns initial incr/decr test thread iterations value that was set" );
for ( size_t i = 0u; i < N; i++ ) {
epicsThreadMustCreate ( "incr",
50, stackSize, incr < T >, & testData );
epicsThreadMustCreate ( "decr",
50, stackSize, decr < T >, & testData );
if(i%10==0)
testDiag("iteration %u", (unsigned)i);
}
while ( testData.m_testIterations < 2 * N ) {
epicsThreadSleep ( 0.01 );
}
testOk ( get ( testData.m_testIterations ) == 2 * N,
"proper number of incr/decr test thread iterations" );
testOk ( get ( testData.m_testValue ) == NT,
"proper final incr/decr test value" );
}
template < class T >
void testAddSub ()
{
using epics::atomic::set;
using epics::atomic::get;
static const size_t N = 90;
static const T NDT = TestDataAddSub < T > :: delta *
static_cast < T > ( N );
const unsigned int stackSize =
epicsThreadGetStackSize ( epicsThreadStackSmall );
TestDataIncrDecr < T > testData = { 0, N };
set ( testData.m_testValue, NDT );
testOk ( get ( testData.m_testValue ) == NDT,
"get returns initial add/sub test data value that was set" );
set ( testData.m_testIterations, 0u );
testOk ( get ( testData.m_testIterations ) == 0u,
"get returns initial incr/decr test thread iterations value that was set" );
for ( size_t i = 0u; i < N; i++ ) {
epicsThreadMustCreate ( "add",
50, stackSize, add < T >, & testData );
epicsThreadMustCreate ( "sub",
50, stackSize, sub < T >, & testData );
}
while ( testData.m_testIterations < 2 * N ) {
epicsThreadSleep ( 0.01 );
}
testOk ( get ( testData.m_testIterations ) == 2 * N,
"proper number of add/sub test thread iterations" );
testOk ( get ( testData.m_testValue ) == NDT,
"proper final add/sub test value" );
}
template < class T >
void testCAS ()
{
using epics::atomic::set;
using epics::atomic::get;
static const size_t N = 10;
const unsigned int stackSize =
epicsThreadGetStackSize ( epicsThreadStackSmall );
TestDataCAS < T > testData = { 0, N, N };
set ( testData.m_testIterationsSet, 0 );
testOk ( get ( testData.m_testIterationsSet ) == 0u,
"get returns initial CAS test thread "
"iterations set value" );
set ( testData.m_testIterationsNotSet, 0 );
testOk ( get ( testData.m_testIterationsNotSet ) == 0u,
"get returns initial CAS test thread "
"iterations not set value" );
set ( testData.m_testValue, trueValue < T > () );
testOk ( get ( testData.m_testValue ) == trueValue < T > (),
"set/get a true value" );
for ( size_t i = 0u; i < N; i++ ) {
epicsThreadMustCreate ( "tns",
50, stackSize, cas < T >, & testData );
}
set ( testData.m_testValue, falseValue < T > () );
while ( testData.m_testIterationsSet < N ) {
epicsThreadSleep ( 0.01 );
}
testOk ( get ( testData.m_testIterationsSet ) == N,
"proper number of CAS test thread set iterations" );
testOk ( get ( testData.m_testIterationsNotSet ) == 0u,
"proper number of CAS test thread not set iterations" );
}
// template instances, needed for vxWorks 5.5.2
#ifdef _MSC_VER
# pragma warning ( push )
# pragma warning ( disable:4660 )
#endif
template void incr < int > (void *);
template void decr < int > (void *);
template void incr < size_t > (void *);
template void decr < size_t > (void *);
template void add < int > (void *);
template void sub < int > (void *);
template void add < size_t > (void *);
template void sub < size_t > (void *);
template void cas < int > (void *);
template void cas < size_t > (void *);
template void cas < EpicsAtomicPtrT > (void *);
template void testIncrDecr < int > (void);
template void testIncrDecr < size_t > (void);
template void testAddSub < int > (void);
template void testAddSub < size_t > (void);
template void testCAS < int > (void);
template void testCAS < size_t > (void);
template void testCAS < EpicsAtomicPtrT > (void);
#ifdef _MSC_VER
# pragma warning ( pop )
#endif
static void testClassify()
{
testDiag("Classify Build conditions");
#ifdef EPICS_ATOMIC_CMPLR_NAME
testDiag("Compiler dependent impl name %s", EPICS_ATOMIC_CMPLR_NAME);
#else
testDiag("Compiler dependent impl name undefined");
#endif
#ifdef EPICS_ATOMIC_OS_NAME
testDiag("OS dependent impl name %s", EPICS_ATOMIC_OS_NAME);
#else
testDiag("OS dependent impl name undefined");
#endif
#ifdef __GNUC__
#if GCC_ATOMIC_INTRINSICS_GCC4_OR_BETTER
testDiag("GCC using atomic builtin memory barrier");
#else
testDiag("GCC using asm memory barrier");
#endif
#if GCC_ATOMIC_INTRINSICS_AVAIL_INT_T || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER
testDiag("GCC use builtin for int");
#endif
#if GCC_ATOMIC_INTRINSICS_AVAIL_SIZE_T || GCC_ATOMIC_INTRINSICS_AVAIL_EARLIER
testDiag("GCC use builtin for size_t");
#endif
#ifndef EPICS_ATOMIC_INCR_INTT
testDiag("Use default epicsAtomicIncrIntT()");
#endif
#ifndef EPICS_ATOMIC_INCR_SIZET
testDiag("Use default epicsAtomicIncrSizeT()");
#endif
#ifndef EPICS_ATOMIC_DECR_INTT
testDiag("Use default epicsAtomicDecrIntT()");
#endif
#ifndef EPICS_ATOMIC_DECR_SIZET
testDiag("Use default epicsAtomicDecrSizeT()");
#endif
#ifndef EPICS_ATOMIC_ADD_INTT
testDiag("Use default epicsAtomicAddIntT()");
#endif
#ifndef EPICS_ATOMIC_ADD_SIZET
testDiag("Use default epicsAtomicAddSizeT()");
#endif
#ifndef EPICS_ATOMIC_SUB_SIZET
testDiag("Use default epicsAtomicSubSizeT()");
#endif
#ifndef EPICS_ATOMIC_SET_INTT
testDiag("Use default epicsAtomicSetIntT()");
#endif
#ifndef EPICS_ATOMIC_SET_SIZET
testDiag("Use default epicsAtomicSetSizeT()");
#endif
#ifndef EPICS_ATOMIC_SET_PTRT
testDiag("Use default epicsAtomicSetPtrT()");
#endif
#ifndef EPICS_ATOMIC_GET_INTT
testDiag("Use default epicsAtomicGetIntT()");
#endif
#ifndef EPICS_ATOMIC_GET_SIZET
testDiag("Use default epicsAtomicGetSizeT()");
#endif
#ifndef EPICS_ATOMIC_GET_PTRT
testDiag("Use default epicsAtomicGetPtrT()");
#endif
#ifndef EPICS_ATOMIC_CAS_INTT
testDiag("Use default epicsAtomicCmpAndSwapIntT()");
#endif
#ifndef EPICS_ATOMIC_CAS_SIZET
testDiag("Use default epicsAtomicCmpAndSwapSizeT()");
#endif
#ifndef EPICS_ATOMIC_CAS_PTRT
testDiag("Use default epicsAtomicCmpAndSwapPtrT()");
#endif
#endif /* __GNUC__ */
}
static
void testBasic()
{
using epics::atomic::set;
using epics::atomic::get;
using epics::atomic::decrement;
using epics::atomic::increment;
using epics::atomic::add;
using epics::atomic::subtract;
using epics::atomic::compareAndSwap;
testDiag("Test basic operation symantics");
int Int = 0;
size_t Sizet = 0;
void *voidp = NULL;
set(Int, -42);
set(Sizet, 42);
set(voidp, (void*)&voidp);
increment(Int);
increment(Sizet);
testOk1(get(Int)==-41);
testOk1(get(Sizet)==43);
testOk1(get(voidp)==(void*)&voidp);
decrement(Int);
decrement(Sizet);
testOk1(get(Int)==-42);
testOk1(get(Sizet)==42);
add(Int, -2);
subtract(Sizet, 2);
testOk1(get(Int)==-44);
testOk1(get(Sizet)==40);
testOk1(compareAndSwap(Int, -34, -10)==-44);
testOk1(compareAndSwap(Sizet, 34, 10)==40);
testOk1(compareAndSwap(voidp, NULL, (void*)&Sizet)==(void*)&voidp);
testOk1(get(Int)==-44);
testOk1(get(Sizet)==40);
testOk1(get(voidp)==(void*)&voidp);
testOk1(compareAndSwap(Int, -44, -10)==-44);
testOk1(compareAndSwap(Sizet, 40, 10)==40);
testOk1(compareAndSwap(voidp, (void*)&voidp, (void*)&Sizet)==(void*)&voidp);
testOk1(get(Int)==-10);
testOk1(get(Sizet)==10);
testOk1(get(voidp)==(void*)&Sizet);
}
} // namespace
MAIN ( epicsAtomicTest )
{
testPlan ( 50 );
testClassify ();
testBasic();
#if defined(__rtems__)
testSkip(31, "Tests assume time sliced thread scheduling");
#else
testIncrDecr < int > ();
testIncrDecr < size_t > ();
testAddSub < int > ();
testAddSub < size_t > ();
testCAS < int > ();
testCAS < size_t > ();
testCAS < EpicsAtomicPtrT > ();
#endif
return testDone ();
}