268 lines
7.4 KiB
C++
268 lines
7.4 KiB
C++
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#include "epicsAtomic.h"
|
|
#include "epicsTime.h"
|
|
#include "epicsThread.h"
|
|
#include "epicsUnitTest.h"
|
|
#include "testMain.h"
|
|
|
|
using namespace epics;
|
|
using namespace atomic;
|
|
|
|
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 )
|
|
{
|
|
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 )
|
|
{
|
|
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 )
|
|
{
|
|
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 )
|
|
{
|
|
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;
|
|
};
|
|
|
|
int isModulo ( size_t N, size_t n )
|
|
{
|
|
return ( n % N ) == 0u;
|
|
}
|
|
|
|
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 )
|
|
{
|
|
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 ()
|
|
{
|
|
static const size_t N = 100;
|
|
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++ ) {
|
|
epicsThreadCreate ( "incr",
|
|
50, stackSize, incr < T >, & testData );
|
|
epicsThreadCreate ( "decr",
|
|
50, stackSize, decr < T >, & testData );
|
|
}
|
|
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 ()
|
|
{
|
|
static const size_t N = 100;
|
|
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++ ) {
|
|
epicsThreadCreate ( "add",
|
|
50, stackSize, add < T >, & testData );
|
|
epicsThreadCreate ( "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 ()
|
|
{
|
|
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++ ) {
|
|
epicsThreadCreate ( "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
|
|
|
|
|
|
MAIN ( epicsAtomicTest )
|
|
{
|
|
|
|
testPlan ( 31 );
|
|
|
|
testIncrDecr < int > ();
|
|
testIncrDecr < size_t > ();
|
|
testAddSub < int > ();
|
|
testAddSub < size_t > ();
|
|
testCAS < int > ();
|
|
testCAS < size_t > ();
|
|
testCAS < EpicsAtomicPtrT > ();
|
|
|
|
return testDone ();
|
|
}
|
|
|