Merge branch 'fix-async-dns/master' into 3.14
This commit is contained in:
@@ -13,6 +13,12 @@
|
||||
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>Fix DNS related crash on exit</h3>
|
||||
|
||||
<p>The attempt to fix DNS related delays for short lived CLI programs (eg. caget)
|
||||
in lp:1527636 introduced a bug which cased these short lived clients to crash on exit.
|
||||
This bug should now be fixed.</p>
|
||||
|
||||
<h3>Server bind issue on Windows</h3>
|
||||
|
||||
<p>When a National Instruments network variables CA server is already running on
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#include <stdexcept>
|
||||
#include <stdio.h>
|
||||
|
||||
//#define EPICS_FREELIST_DEBUG
|
||||
#define EPICS_PRIVATE_API
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "ipAddrToAsciiAsynchronous.h"
|
||||
#include "epicsThread.h"
|
||||
@@ -45,7 +48,6 @@ public:
|
||||
< ipAddrToAsciiTransactionPrivate, 0x80 > & );
|
||||
epicsPlacementDeleteOperator (( void *, tsFreeList
|
||||
< ipAddrToAsciiTransactionPrivate, 0x80 > & ))
|
||||
private:
|
||||
osiSockAddr addr;
|
||||
ipAddrToAsciiEnginePrivate & engine;
|
||||
ipAddrToAsciiCallBack * pCB;
|
||||
@@ -54,7 +56,7 @@ private:
|
||||
void release ();
|
||||
void * operator new ( size_t );
|
||||
void operator delete ( void * );
|
||||
friend class ipAddrToAsciiEnginePrivate;
|
||||
private:
|
||||
ipAddrToAsciiTransactionPrivate & operator = ( const ipAddrToAsciiTransactionPrivate & );
|
||||
ipAddrToAsciiTransactionPrivate ( const ipAddrToAsciiTransactionPrivate & );
|
||||
};
|
||||
@@ -75,41 +77,54 @@ extern "C" {
|
||||
static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * );
|
||||
}
|
||||
|
||||
// - this class executes the synchronous DNS query
|
||||
// - it creates one thread
|
||||
class ipAddrToAsciiEnginePrivate :
|
||||
public ipAddrToAsciiEngine,
|
||||
public epicsThreadRunable {
|
||||
public:
|
||||
ipAddrToAsciiEnginePrivate ();
|
||||
virtual ~ipAddrToAsciiEnginePrivate ();
|
||||
void show ( unsigned level ) const;
|
||||
private:
|
||||
namespace {
|
||||
struct ipAddrToAsciiGlobal : public epicsThreadRunable {
|
||||
ipAddrToAsciiGlobal();
|
||||
virtual ~ipAddrToAsciiGlobal() {}
|
||||
|
||||
virtual void run ();
|
||||
|
||||
char nameTmp [1024];
|
||||
tsFreeList
|
||||
< ipAddrToAsciiTransactionPrivate, 0x80 >
|
||||
tsFreeList
|
||||
< ipAddrToAsciiTransactionPrivate, 0x80 >
|
||||
transactionFreeList;
|
||||
tsDLList < ipAddrToAsciiTransactionPrivate > labor;
|
||||
mutable epicsMutex mutex;
|
||||
epicsEvent laborEvent;
|
||||
epicsEvent destructorBlockEvent;
|
||||
epicsThread thread;
|
||||
// pCurrent may be changed by any thread (worker or other)
|
||||
ipAddrToAsciiTransactionPrivate * pCurrent;
|
||||
// pActive may only be changed by the worker
|
||||
ipAddrToAsciiTransactionPrivate * pActive;
|
||||
unsigned cancelPendingCount;
|
||||
bool exitFlag;
|
||||
bool callbackInProgress;
|
||||
static ipAddrToAsciiEnginePrivate * pEngine;
|
||||
};
|
||||
}
|
||||
|
||||
// - this class executes the synchronous DNS query
|
||||
// - it creates one thread
|
||||
class ipAddrToAsciiEnginePrivate :
|
||||
public ipAddrToAsciiEngine {
|
||||
public:
|
||||
ipAddrToAsciiEnginePrivate() :refcount(1u), released(false) {}
|
||||
virtual ~ipAddrToAsciiEnginePrivate () {}
|
||||
void show ( unsigned level ) const;
|
||||
|
||||
unsigned refcount;
|
||||
bool released;
|
||||
|
||||
static ipAddrToAsciiGlobal * pEngine;
|
||||
ipAddrToAsciiTransaction & createTransaction ();
|
||||
void release ();
|
||||
void run ();
|
||||
ipAddrToAsciiEnginePrivate ( const ipAddrToAsciiEngine & );
|
||||
void release ();
|
||||
|
||||
private:
|
||||
ipAddrToAsciiEnginePrivate ( const ipAddrToAsciiEngine & );
|
||||
ipAddrToAsciiEnginePrivate & operator = ( const ipAddrToAsciiEngine & );
|
||||
friend class ipAddrToAsciiEngine;
|
||||
friend class ipAddrToAsciiTransactionPrivate;
|
||||
friend void ipAddrToAsciiEngineGlobalMutexConstruct ( void * );
|
||||
};
|
||||
|
||||
ipAddrToAsciiEnginePrivate * ipAddrToAsciiEnginePrivate :: pEngine = 0;
|
||||
ipAddrToAsciiGlobal * ipAddrToAsciiEnginePrivate :: pEngine = 0;
|
||||
static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
// the users are not required to supply a show routine
|
||||
@@ -124,12 +139,24 @@ ipAddrToAsciiEngine::~ipAddrToAsciiEngine () {}
|
||||
static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * )
|
||||
{
|
||||
try{
|
||||
ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiEnginePrivate ();
|
||||
ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiGlobal ();
|
||||
}catch(std::exception& e){
|
||||
errlogPrintf("ipAddrToAsciiEnginePrivate ctor fails with: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ipAddrToAsciiEngine::cleanup()
|
||||
{
|
||||
{
|
||||
epicsGuard<epicsMutex> G(ipAddrToAsciiEnginePrivate::pEngine->mutex);
|
||||
ipAddrToAsciiEnginePrivate::pEngine->exitFlag = true;
|
||||
}
|
||||
ipAddrToAsciiEnginePrivate::pEngine->laborEvent.signal();
|
||||
ipAddrToAsciiEnginePrivate::pEngine->thread.exitWait();
|
||||
delete ipAddrToAsciiEnginePrivate::pEngine;
|
||||
ipAddrToAsciiEnginePrivate::pEngine = 0;
|
||||
}
|
||||
|
||||
// for now its probably sufficent to allocate one
|
||||
// DNS transaction thread for all codes sharing
|
||||
// the same process that need DNS services but we
|
||||
@@ -141,41 +168,78 @@ ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate ()
|
||||
ipAddrToAsciiEngineGlobalMutexConstruct, 0 );
|
||||
if(!ipAddrToAsciiEnginePrivate::pEngine)
|
||||
throw std::runtime_error("ipAddrToAsciiEngine::allocate fails");
|
||||
return * ipAddrToAsciiEnginePrivate::pEngine;
|
||||
return * new ipAddrToAsciiEnginePrivate();
|
||||
}
|
||||
|
||||
ipAddrToAsciiEnginePrivate::ipAddrToAsciiEnginePrivate () :
|
||||
ipAddrToAsciiGlobal::ipAddrToAsciiGlobal () :
|
||||
thread ( *this, "ipToAsciiProxy",
|
||||
epicsThreadGetStackSize(epicsThreadStackBig),
|
||||
epicsThreadPriorityLow ),
|
||||
pCurrent ( 0 ), cancelPendingCount ( 0u ), exitFlag ( false ),
|
||||
pCurrent ( 0 ), pActive ( 0 ), cancelPendingCount ( 0u ), exitFlag ( false ),
|
||||
callbackInProgress ( false )
|
||||
{
|
||||
this->thread.start (); // start the thread
|
||||
}
|
||||
|
||||
ipAddrToAsciiEnginePrivate::~ipAddrToAsciiEnginePrivate ()
|
||||
{
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
this->exitFlag = true;
|
||||
}
|
||||
this->laborEvent.signal ();
|
||||
this->thread.exitWait ();
|
||||
}
|
||||
|
||||
void ipAddrToAsciiEnginePrivate::release ()
|
||||
{
|
||||
bool last;
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->pEngine->mutex );
|
||||
if(released)
|
||||
throw std::logic_error("Engine release() called again!");
|
||||
|
||||
// released==true prevents new transactions
|
||||
released = true;
|
||||
|
||||
{
|
||||
// cancel any pending transactions
|
||||
tsDLIter < ipAddrToAsciiTransactionPrivate > it(pEngine->labor.firstIter());
|
||||
while(it.valid()) {
|
||||
ipAddrToAsciiTransactionPrivate *trn = it.pointer();
|
||||
++it;
|
||||
|
||||
if(this==&trn->engine) {
|
||||
trn->pending = false;
|
||||
pEngine->labor.remove(*trn);
|
||||
}
|
||||
}
|
||||
|
||||
// cancel transaction in lookup or callback
|
||||
if (pEngine->pCurrent && this==&pEngine->pCurrent->engine) {
|
||||
pEngine->pCurrent->pending = false;
|
||||
pEngine->pCurrent = 0;
|
||||
}
|
||||
|
||||
// wait for completion of in-progress callback
|
||||
pEngine->cancelPendingCount++;
|
||||
while(pEngine->pActive && this==&pEngine->pActive->engine
|
||||
&& ! pEngine->thread.isCurrentThread()) {
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
pEngine->destructorBlockEvent.wait();
|
||||
}
|
||||
pEngine->cancelPendingCount--;
|
||||
if(pEngine->cancelPendingCount)
|
||||
pEngine->destructorBlockEvent.signal();
|
||||
}
|
||||
|
||||
assert(refcount>0);
|
||||
last = 0==--refcount;
|
||||
}
|
||||
if(last) {
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void ipAddrToAsciiEnginePrivate::show ( unsigned level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
epicsGuard < epicsMutex > guard ( this->pEngine->mutex );
|
||||
printf ( "ipAddrToAsciiEngine at %p with %u requests pending\n",
|
||||
static_cast <const void *> (this), this->labor.count () );
|
||||
static_cast <const void *> (this), this->pEngine->labor.count () );
|
||||
if ( level > 0u ) {
|
||||
tsDLIterConst < ipAddrToAsciiTransactionPrivate >
|
||||
pItem = this->labor.firstIter ();
|
||||
tsDLIter < ipAddrToAsciiTransactionPrivate >
|
||||
pItem = this->pEngine->labor.firstIter ();
|
||||
while ( pItem.valid () ) {
|
||||
pItem->show ( level - 1u );
|
||||
pItem++;
|
||||
@@ -183,10 +247,10 @@ void ipAddrToAsciiEnginePrivate::show ( unsigned level ) const
|
||||
}
|
||||
if ( level > 1u ) {
|
||||
printf ( "mutex:\n" );
|
||||
this->mutex.show ( level - 2u );
|
||||
this->pEngine->mutex.show ( level - 2u );
|
||||
printf ( "laborEvent:\n" );
|
||||
this->laborEvent.show ( level - 2u );
|
||||
printf ( "exitFlag boolean = %u\n", this->exitFlag );
|
||||
this->pEngine->laborEvent.show ( level - 2u );
|
||||
printf ( "exitFlag boolean = %u\n", this->pEngine->exitFlag );
|
||||
printf ( "exit event:\n" );
|
||||
}
|
||||
}
|
||||
@@ -226,10 +290,20 @@ void ipAddrToAsciiTransactionPrivate::operator delete ( void * )
|
||||
|
||||
ipAddrToAsciiTransaction & ipAddrToAsciiEnginePrivate::createTransaction ()
|
||||
{
|
||||
return * new ( this->transactionFreeList ) ipAddrToAsciiTransactionPrivate ( *this );
|
||||
epicsGuard <epicsMutex> G(this->pEngine->mutex);
|
||||
if(this->released)
|
||||
throw std::logic_error("createTransaction() on release()'d ipAddrToAsciiEngine");
|
||||
|
||||
assert(this->refcount>0);
|
||||
|
||||
ipAddrToAsciiTransactionPrivate *ret = new ( this->pEngine->transactionFreeList ) ipAddrToAsciiTransactionPrivate ( *this );
|
||||
|
||||
this->refcount++;
|
||||
|
||||
return * ret;
|
||||
}
|
||||
|
||||
void ipAddrToAsciiEnginePrivate::run ()
|
||||
void ipAddrToAsciiGlobal::run ()
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->mutex );
|
||||
while ( ! this->exitFlag ) {
|
||||
@@ -267,7 +341,7 @@ void ipAddrToAsciiEnginePrivate::run ()
|
||||
// fix for lp:1580623
|
||||
// a destructing cac sets pCurrent to NULL, so
|
||||
// make local copy to avoid race when releasing the guard
|
||||
ipAddrToAsciiTransactionPrivate *pCur = this->pCurrent;
|
||||
ipAddrToAsciiTransactionPrivate *pCur = pActive = pCurrent;
|
||||
this->callbackInProgress = true;
|
||||
|
||||
{
|
||||
@@ -277,6 +351,7 @@ void ipAddrToAsciiEnginePrivate::run ()
|
||||
}
|
||||
|
||||
this->callbackInProgress = false;
|
||||
pActive = 0;
|
||||
|
||||
if ( this->pCurrent ) {
|
||||
this->pCurrent->pending = false;
|
||||
@@ -300,44 +375,53 @@ ipAddrToAsciiTransactionPrivate::ipAddrToAsciiTransactionPrivate
|
||||
void ipAddrToAsciiTransactionPrivate::release ()
|
||||
{
|
||||
this->~ipAddrToAsciiTransactionPrivate ();
|
||||
this->engine.transactionFreeList.release ( this );
|
||||
this->engine.pEngine->transactionFreeList.release ( this );
|
||||
}
|
||||
|
||||
ipAddrToAsciiTransactionPrivate::~ipAddrToAsciiTransactionPrivate ()
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->engine.mutex );
|
||||
while ( this->pending ) {
|
||||
if ( this->engine.pCurrent == this &&
|
||||
this->engine.callbackInProgress &&
|
||||
! this->engine.thread.isCurrentThread() ) {
|
||||
// cancel from another thread while callback in progress
|
||||
// waits for callback to complete
|
||||
assert ( this->engine.cancelPendingCount < UINT_MAX );
|
||||
this->engine.cancelPendingCount++;
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
this->engine.destructorBlockEvent.wait ();
|
||||
}
|
||||
assert ( this->engine.cancelPendingCount > 0u );
|
||||
this->engine.cancelPendingCount--;
|
||||
if ( ! this->pending ) {
|
||||
if ( this->engine.cancelPendingCount ) {
|
||||
this->engine.destructorBlockEvent.signal ();
|
||||
ipAddrToAsciiGlobal *pGlobal = this->engine.pEngine;
|
||||
bool last;
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( pGlobal->mutex );
|
||||
while ( this->pending ) {
|
||||
if ( pGlobal->pCurrent == this &&
|
||||
pGlobal->callbackInProgress &&
|
||||
! pGlobal->thread.isCurrentThread() ) {
|
||||
// cancel from another thread while callback in progress
|
||||
// waits for callback to complete
|
||||
assert ( pGlobal->cancelPendingCount < UINT_MAX );
|
||||
pGlobal->cancelPendingCount++;
|
||||
{
|
||||
epicsGuardRelease < epicsMutex > unguard ( guard );
|
||||
pGlobal->destructorBlockEvent.wait ();
|
||||
}
|
||||
assert ( pGlobal->cancelPendingCount > 0u );
|
||||
pGlobal->cancelPendingCount--;
|
||||
if ( ! this->pending ) {
|
||||
if ( pGlobal->cancelPendingCount ) {
|
||||
pGlobal->destructorBlockEvent.signal ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( this->engine.pCurrent == this ) {
|
||||
// cancel from callback, or while lookup in progress
|
||||
this->engine.pCurrent = 0;
|
||||
}
|
||||
else {
|
||||
// cancel before lookup starts
|
||||
this->engine.labor.remove ( *this );
|
||||
if ( pGlobal->pCurrent == this ) {
|
||||
// cancel from callback, or while lookup in progress
|
||||
pGlobal->pCurrent = 0;
|
||||
}
|
||||
else {
|
||||
// cancel before lookup starts
|
||||
pGlobal->labor.remove ( *this );
|
||||
}
|
||||
this->pending = false;
|
||||
}
|
||||
this->pending = false;
|
||||
}
|
||||
assert(this->engine.refcount>0);
|
||||
last = 0==--this->engine.refcount;
|
||||
}
|
||||
if(last) {
|
||||
delete &this->engine;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,15 +429,21 @@ void ipAddrToAsciiTransactionPrivate::ipAddrToAscii (
|
||||
const osiSockAddr & addrIn, ipAddrToAsciiCallBack & cbIn )
|
||||
{
|
||||
bool success;
|
||||
ipAddrToAsciiGlobal *pGlobal = this->engine.pEngine;
|
||||
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->engine.mutex );
|
||||
// put some reasonable limit on queue expansion
|
||||
if ( !this->pending && engine.labor.count () < 16u ) {
|
||||
epicsGuard < epicsMutex > guard ( pGlobal->mutex );
|
||||
|
||||
if (this->engine.released) {
|
||||
errlogPrintf("Warning: ipAddrToAscii on transaction with release()'d ipAddrToAsciiEngine");
|
||||
success = false;
|
||||
|
||||
} else if ( !this->pending && pGlobal->labor.count () < 16u ) {
|
||||
// put some reasonable limit on queue expansion
|
||||
this->addr = addrIn;
|
||||
this->pCB = & cbIn;
|
||||
this->pending = true;
|
||||
this->engine.labor.add ( *this );
|
||||
pGlobal->labor.add ( *this );
|
||||
success = true;
|
||||
}
|
||||
else {
|
||||
@@ -362,7 +452,7 @@ void ipAddrToAsciiTransactionPrivate::ipAddrToAscii (
|
||||
}
|
||||
|
||||
if ( success ) {
|
||||
this->engine.laborEvent.signal ();
|
||||
pGlobal->laborEvent.signal ();
|
||||
}
|
||||
else {
|
||||
char autoNameTmp[256];
|
||||
@@ -379,7 +469,7 @@ osiSockAddr ipAddrToAsciiTransactionPrivate::address () const
|
||||
|
||||
void ipAddrToAsciiTransactionPrivate::show ( unsigned level ) const
|
||||
{
|
||||
epicsGuard < epicsMutex > guard ( this->engine.mutex );
|
||||
epicsGuard < epicsMutex > guard ( this->engine.pEngine->mutex );
|
||||
char ipAddr [64];
|
||||
sockAddrToDottedIP ( &this->addr.sa, ipAddr, sizeof ( ipAddr ) );
|
||||
printf ( "ipAddrToAsciiTransactionPrivate for address %s\n", ipAddr );
|
||||
|
||||
@@ -44,6 +44,10 @@ public:
|
||||
static ipAddrToAsciiEngine & allocate ();
|
||||
protected:
|
||||
virtual ~ipAddrToAsciiEngine () = 0;
|
||||
public:
|
||||
#ifdef EPICS_PRIVATE_API
|
||||
static void cleanup();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // ifdef ipAddrToAsciiAsynchronous_h
|
||||
|
||||
@@ -11,6 +11,7 @@ TOP=../../..
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
PROD_LIBS += Com
|
||||
PROD_SYS_LIBS_WIN32 += ws2_32 advapi32 user32
|
||||
|
||||
TESTPROD_HOST += epicsUnitTestTest
|
||||
epicsUnitTestTest_SRCS += epicsUnitTestTest.c
|
||||
@@ -151,6 +152,10 @@ epicsMessageQueueTest_SRCS += epicsMessageQueueTest.cpp
|
||||
testHarness_SRCS += epicsMessageQueueTest.cpp
|
||||
TESTS += epicsMessageQueueTest
|
||||
|
||||
TESTPROD_HOST += ipAddrToAsciiTest
|
||||
ipAddrToAsciiTest_SRCS += ipAddrToAsciiTest.cpp
|
||||
testHarness_SRCS += ipAddrToAsciiTest.cpp
|
||||
TESTS += ipAddrToAsciiTest
|
||||
|
||||
# The testHarness runs all the test programs in a known working order.
|
||||
testHarness_SRCS += epicsRunLibComTests.c
|
||||
|
||||
@@ -41,6 +41,7 @@ int macEnvExpandTest(void);
|
||||
int ringPointerTest(void);
|
||||
int ringBytesTest(void);
|
||||
int blockingSockTest(void);
|
||||
int ipAddrToAsciiTest(void);
|
||||
int epicsSockResolveTest(void);
|
||||
int taskwdTest(void);
|
||||
int epicsExitTest(void);
|
||||
@@ -102,6 +103,8 @@ void epicsRunLibComTests(void)
|
||||
runTest(ringBytesTest);
|
||||
|
||||
runTest(blockingSockTest);
|
||||
|
||||
runTest(ipAddrToAsciiTest);
|
||||
|
||||
runTest(epicsSockResolveTest);
|
||||
|
||||
|
||||
165
src/libCom/test/ipAddrToAsciiTest.cpp
Normal file
165
src/libCom/test/ipAddrToAsciiTest.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2017 Michael Davidsaver
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define EPICS_PRIVATE_API
|
||||
|
||||
#include "epicsMutex.h"
|
||||
#include "epicsGuard.h"
|
||||
#include "epicsThread.h"
|
||||
#include "epicsEvent.h"
|
||||
#include "ipAddrToAsciiAsynchronous.h"
|
||||
|
||||
#include "epicsUnitTest.h"
|
||||
#include "testMain.h"
|
||||
|
||||
namespace {
|
||||
|
||||
typedef epicsGuard<epicsMutex> Guard;
|
||||
typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
|
||||
struct CB : public ipAddrToAsciiCallBack
|
||||
{
|
||||
const char *name;
|
||||
epicsMutex mutex;
|
||||
epicsEvent starter, blocker, complete;
|
||||
bool started, cont, done;
|
||||
CB(const char *name) : name(name), started(false), cont(false), done(false) {}
|
||||
virtual ~CB() {}
|
||||
virtual void transactionComplete ( const char * pHostName )
|
||||
{
|
||||
Guard G(mutex);
|
||||
started = true;
|
||||
starter.signal();
|
||||
testDiag("In transactionComplete(%s) for %s", pHostName, name);
|
||||
while(!cont) {
|
||||
UnGuard U(G);
|
||||
if(!blocker.wait(2.0))
|
||||
break;
|
||||
}
|
||||
done = true;
|
||||
complete.signal();
|
||||
}
|
||||
void waitStart()
|
||||
{
|
||||
Guard G(mutex);
|
||||
while(!started) {
|
||||
UnGuard U(G);
|
||||
if(!starter.wait(2.0))
|
||||
break;
|
||||
}
|
||||
}
|
||||
void poke()
|
||||
{
|
||||
testDiag("Poke");
|
||||
Guard G(mutex);
|
||||
cont = true;
|
||||
blocker.signal();
|
||||
}
|
||||
void finish()
|
||||
{
|
||||
testDiag("Finish");
|
||||
Guard G(mutex);
|
||||
while(!done) {
|
||||
UnGuard U(G);
|
||||
if(!complete.wait(2.0))
|
||||
break;
|
||||
}
|
||||
testDiag("Finished");
|
||||
}
|
||||
};
|
||||
|
||||
// ensure that lookup of 127.0.0.1 works
|
||||
void doLookup(ipAddrToAsciiEngine& engine)
|
||||
{
|
||||
testDiag("In doLookup");
|
||||
|
||||
ipAddrToAsciiTransaction& trn(engine.createTransaction());
|
||||
CB cb("cb");
|
||||
osiSockAddr addr;
|
||||
addr.ia.sin_family = AF_INET;
|
||||
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.ia.sin_port = htons(42);
|
||||
|
||||
testDiag("Start lookup");
|
||||
trn.ipAddrToAscii(addr, cb);
|
||||
cb.poke();
|
||||
cb.finish();
|
||||
testOk1(cb.cont);
|
||||
testOk1(cb.done);
|
||||
|
||||
trn.release();
|
||||
}
|
||||
|
||||
// Test cancel of pending transaction
|
||||
void doCancel()
|
||||
{
|
||||
testDiag("In doCancel");
|
||||
|
||||
ipAddrToAsciiEngine& engine1(ipAddrToAsciiEngine::allocate());
|
||||
ipAddrToAsciiEngine& engine2(ipAddrToAsciiEngine::allocate());
|
||||
|
||||
ipAddrToAsciiTransaction& trn1(engine1.createTransaction()),
|
||||
& trn2(engine2.createTransaction());
|
||||
testOk1(&trn1!=&trn2);
|
||||
CB cb1("cb1"), cb2("cb2");
|
||||
|
||||
osiSockAddr addr;
|
||||
addr.ia.sin_family = AF_INET;
|
||||
addr.ia.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
addr.ia.sin_port = htons(42);
|
||||
|
||||
// ensure that the worker thread is blocked with a transaction from engine1
|
||||
testDiag("Start lookup1");
|
||||
trn1.ipAddrToAscii(addr, cb1);
|
||||
testDiag("Wait start1");
|
||||
cb1.waitStart();
|
||||
|
||||
testDiag("Start lookup2");
|
||||
trn2.ipAddrToAscii(addr, cb2);
|
||||
|
||||
testDiag("release engine2, implicitly cancels lookup2");
|
||||
engine2.release();
|
||||
|
||||
cb2.poke();
|
||||
testDiag("Wait for lookup2 timeout");
|
||||
cb2.finish();
|
||||
testOk1(!cb2.done);
|
||||
|
||||
testDiag("Complete lookup1");
|
||||
cb1.poke();
|
||||
cb1.finish();
|
||||
testOk1(cb1.done);
|
||||
|
||||
engine1.release();
|
||||
|
||||
trn1.release();
|
||||
trn2.release();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(ipAddrToAsciiTest)
|
||||
{
|
||||
testPlan(5);
|
||||
{
|
||||
ipAddrToAsciiEngine& engine(ipAddrToAsciiEngine::allocate());
|
||||
doLookup(engine);
|
||||
engine.release();
|
||||
}
|
||||
doCancel();
|
||||
// TODO: somehow test cancel of in-progress callback
|
||||
// allow time for any un-canceled transcations to crash us...
|
||||
epicsThreadSleep(1.0);
|
||||
|
||||
#ifdef __linux__
|
||||
ipAddrToAsciiEngine::cleanup();
|
||||
#endif
|
||||
|
||||
return testDone();
|
||||
}
|
||||
Reference in New Issue
Block a user