Merge with additions from Gasper Jansa

This commit is contained in:
miha_vitorovic
2011-01-04 23:48:59 +01:00
18 changed files with 1303 additions and 288 deletions

View File

@@ -1,10 +1,10 @@
QtC-pvAccess.creator.user
syntax: glob
O.Common
O.linux-x86
O.*
.DS_Store
syntax: regexp
^bin
^include
^lib

View File

@@ -19,12 +19,16 @@ INC += inetAddressUtil.h
INC += logger.h
INC += introspectionRegistry.h
INC += transportRegistry.h
INC += namedLockPattern.h
INC += referenceCountingLock.h
LIBSRCS += hexDump.cpp
LIBSRCS += wildcharMatcher.cpp
LIBSRCS += inetAddressUtil.cpp
LIBSRCS += logger.cpp
LIBSRCS += introspectionRegistry.cpp
LIBSRCS += transportRegistry.cpp
LIBSRCS += namedLockPattern.cpp
LIBSRCS += referenceCountingLock.cpp
SRC_DIRS += $(PVACCESS)/client

View File

@@ -68,7 +68,7 @@ namespace epics {
const int16 CA_DEFAULT_PRIORITY = 0;
/** Unreasonable channel name length. */
const int32 UNREASONABLE_CHANNEL_NAME_LENGTH = 500;
const uint32 UNREASONABLE_CHANNEL_NAME_LENGTH = 500;
/** Invalid data type. */
const int16 INVALID_DATA_TYPE = (int16)0xFFFF;

View File

@@ -583,6 +583,17 @@ namespace epics { namespace pvAccess {
virtual ChannelArray* createChannelArray(
ChannelArrayRequester *channelArrayRequester,
epics::pvData::PVStructure *pvRequest) = 0;
/**
* Prints detailed information about the context to the standard output stream.
*/
virtual void printInfo() = 0;
/**
* Prints detailed information about the context to the specified output stream.
* @param out the output stream.
*/
virtual void printInfo(epics::pvData::StringBuilder out) = 0;
};

View File

@@ -69,7 +69,7 @@ bool BeaconHandler::updateBeacon(int8 remoteTransportRevision, int64 timestamp,
void BeaconHandler::beaconArrivalNotify()
{
int32 size;
int32 size = 0;
//TODO TCP name must be get from somewhere not hardcoded
//TODO
Transport** transports = NULL;//_context->getTransportRegistry().get("TCP", _responseFrom, size);
@@ -83,12 +83,12 @@ void BeaconHandler::beaconArrivalNotify()
{
transports[i]->aliveNotification();
}
delete transports;
delete[] transports;
}
void BeaconHandler::changedTransport()
{
int32 size;
int32 size = 0;
//TODO TCP name must be get from somewhere not hardcoded
//TODO
Transport** transports = NULL;//_context->getTransportRegistry().get("TCP", _responseFrom, size);
@@ -102,7 +102,7 @@ void BeaconHandler::changedTransport()
{
transports[i]->changedTransport();
}
delete transports;
delete[] transports;
}
}}

View File

@@ -67,7 +67,6 @@ namespace epics { namespace pvAccess {
* Mutex
*/
Mutex _mutex;
/**
* Update beacon.
* @param remoteTransportRevision encoded (major, minor) revision.

View File

@@ -24,7 +24,7 @@ namespace epics { namespace pvAccess {
*/
BeaconServerStatusProvider(ServerContext* context);
/**
* Test Constructor (ohne context)
* Test Constructor (without context)
*/
BeaconServerStatusProvider();
/**

View File

@@ -0,0 +1,7 @@
/*
* namedLockPattern.cpp
*/
#include "namedLockPattern.h"
//NOTE NamedLockPattern is template so implementation is in header file

View File

@@ -0,0 +1,144 @@
/*
* namedLockPattern.h
*/
#ifndef NAMEDLOCKPATTERN_H
#define NAMEDLOCKPATTERN_H
#include <map>
#include <iostream>
#include <lock.h>
#include <pvType.h>
#include "referenceCountingLock.h"
using namespace std;
using namespace epics::pvData;
namespace epics { namespace pvAccess {
/**
* NamedLockPattern
*/
template <class Key, class Compare = less<Key> >
class NamedLockPattern
{
public:
/**
* Constructor.
*/
NamedLockPattern() {};
/**
* Destructor.
*/
virtual ~NamedLockPattern() {};
/**
* Acquire synchronization lock for named object.
* @param name name of the object whose lock to acquire.
* @param msec the number of milleseconds to wait.
* An argument less than or equal to zero means not to wait at all.
* @return <code>true</code> if acquired, <code>false</code> othwerwise.
*/
bool acquireSynchronizationObject(const Key name, const int64 msec);
/**
* Release synchronization lock for named object.
* @param name name of the object whose lock to release.
*/
void releaseSynchronizationObject(const Key name);
private:
Mutex _mutex;
std::map<const Key,ReferenceCountingLock*,Compare> _namedLocks;
typename std::map<const Key,ReferenceCountingLock*,Compare>::iterator _namedLocksIter;
/**
* Release synchronization lock for named object.
* @param name name of the object whose lock to release.
* @param release set to <code>false</code> if there is no need to call release
* on synchronization lock.
*/
void releaseSynchronizationObject(const Key name,const bool release);
};
template <class Key, class Compare>
bool NamedLockPattern<Key,Compare>::acquireSynchronizationObject(const Key name, const int64 msec)
{
ReferenceCountingLock* lock;
{ //due to guard
Lock guard(&_mutex);
_namedLocksIter = _namedLocks.find(name);
// get synchronization object
// none is found, create and return new one
// increment references
if(_namedLocksIter == _namedLocks.end())
{
lock = new ReferenceCountingLock();
_namedLocks[name] = lock;
}
else
{
lock = _namedLocksIter->second;
lock->increment();
}
} // end of guarded area
bool success = lock->acquire(msec);
if(!success)
{
releaseSynchronizationObject(name, false);
}
return success;
}
template <class Key, class Compare>
void NamedLockPattern<Key,Compare>::releaseSynchronizationObject(const Key name)
{
releaseSynchronizationObject(name, true);
}
template <class Key, class Compare>
void NamedLockPattern<Key,Compare>::releaseSynchronizationObject(const Key name,const bool release)
{
Lock guard(&_mutex);
ReferenceCountingLock* lock;
_namedLocksIter = _namedLocks.find(name);
// release lock
if (_namedLocksIter != _namedLocks.end())
{
lock = _namedLocksIter->second;
// release the lock
if (release)
{
lock->release();
}
// if there only one current lock exists
// remove it from the map
if (lock->decrement() <= 0)
{
_namedLocks.erase(_namedLocksIter);
delete lock;
}
}
}
template <class Key, class Compare>
class NamedLock : private NoDefaultMethods
{
public:
NamedLock(NamedLockPattern<Key,Compare>* namedLockPattern): _namedLockPattern(namedLockPattern) {}
bool acquireSynchronizationObject(const Key name, const int64 msec) {_name = name; return _namedLockPattern->acquireSynchronizationObject(name,msec);}
~NamedLock(){_namedLockPattern->releaseSynchronizationObject(_name);}
private:
Key _name;
NamedLockPattern<Key,Compare>* _namedLockPattern;
};
}}
#endif /* NAMEDLOCKPATTERN_H */

View File

@@ -0,0 +1,81 @@
/*
* namedLockPattern.cpp
*/
#include "referenceCountingLock.h"
namespace epics { namespace pvAccess {
ReferenceCountingLock::ReferenceCountingLock(): _references(1)
{
pthread_mutexattr_t mutexAttribute;
int32 retval = pthread_mutexattr_init(&mutexAttribute);
if(retval != 0)
{
//string errMsg = "Error: pthread_mutexattr_init failed: " + string(strerror(retval));
assert(true);
}
retval = pthread_mutexattr_settype(&mutexAttribute, PTHREAD_MUTEX_RECURSIVE);
if(retval == 0)
{
retval = pthread_mutex_init(&_mutex, &mutexAttribute);
if(retval != 0)
{
//string errMsg = "Error: pthread_mutex_init failed: " + string(strerror(retval));
assert(true);
}
}
else
{
//string errMsg = "Error: pthread_mutexattr_settype failed: " + string(strerror(retval));
assert(true);
}
pthread_mutexattr_destroy(&mutexAttribute);
}
ReferenceCountingLock::~ReferenceCountingLock()
{
pthread_mutex_destroy(&_mutex);
}
bool ReferenceCountingLock::acquire(int64 msecs)
{
struct timespec deltatime;
deltatime.tv_sec = msecs / 1000;
deltatime.tv_nsec = (msecs % 1000) * 1000;
int32 retval = pthread_mutex_timedlock(&_mutex, &deltatime);
if(retval == 0)
{
return true;
}
return false;
}
void ReferenceCountingLock::release()
{
int retval = pthread_mutex_unlock(&_mutex);
if(retval != 0)
{
string errMsg = "Error: pthread_mutex_unlock failed: " + string(strerror(retval));
//TODO do something?
}
}
int ReferenceCountingLock::increment()
{
//TODO does it really has to be atomic?
//return ++_references;
return __sync_add_and_fetch(&_references,1);
}
int ReferenceCountingLock::decrement()
{
//TODO does it really has to be atomic?
//return --_references;
return __sync_sub_and_fetch(&_references,1);
}
}}

View File

@@ -0,0 +1,74 @@
/*
* referenceCountingLock.h
*/
#ifndef REFERENCECOUNTINGLOCK_H
#define REFERENCECOUNTINGLOCK_H
#include <map>
#include <iostream>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <pvType.h>
#include <epicsAssert.h>
using namespace std;
using namespace epics::pvData;
namespace epics { namespace pvAccess {
/**
* Reference counting mutex implementation w/ deadlock detection.
* Synchronization helper class used (intended for use) for activation/deactivation synchronization.
* This class enforces <code>attempt</code> method of acquiring the locks to prevent deadlocks.
* Class also offers reference counting.
* (NOTE: automatic lock counting was not implemented due to imperfect usage.)
*
*/
class ReferenceCountingLock
{
public:
/**
* Constructor of <code>ReferenceCountingLock</code>.
* After construction lock is free and reference count equals <code>1</code>.
*/
ReferenceCountingLock();
/**
* Destructor of <code>ReferenceCountingLock</code>.
*/
virtual ~ReferenceCountingLock();
/**
* Attempt to acquire lock.
*
* @param msecs the number of milleseconds to wait.
* An argument less than or equal to zero means not to wait at all.
* @return <code>true</code> if acquired, <code>false</code> otherwise.
*/
bool acquire(int64 msecs);
/**
* Release previously acquired lock.
*/
void release();
/**
* Increment number of references.
*
* @return number of references.
*/
int increment();
/**
* Decrement number of references.
*
* @return number of references.
*/
int decrement();
private:
int _references;
pthread_mutex_t _mutex;
};
}}
#endif /* NAMEDLOCKPATTERN_H */

View File

@@ -520,6 +520,26 @@ class MockChannel : public Channel {
// TODO
return 0;
}
virtual void printInfo() {
String info;
printInfo(&info);
std::cout << info.c_str() << std::endl;
}
virtual void printInfo(epics::pvData::StringBuilder out) {
//std::ostringstream ostr;
//static String emptyString;
out->append( "CHANNEL : "); out->append(m_name);
out->append("\nSTATE : "); out->append(ConnectionStateNames[getConnectionState()]);
if (isConnected())
{
out->append("\nADDRESS : "); out->append(getRemoteAddress());
//out->append("\nRIGHTS : "); out->append(getAccessRights());
}
out->append("\n");
}
};
class MockChannelProvider;
@@ -905,7 +925,7 @@ int main(int argc,char *argv[])
context->getProvider()->createChannel("test", &channelRequester, ChannelProvider::PRIORITY_DEFAULT, "over the rainbow");
Channel* channel = context->getProvider()->createChannel("test", &channelRequester);
std::cout << channel->getChannelName() << std::endl;
channel->printInfo();
GetFieldRequesterImpl getFieldRequesterImpl;
channel->getField(&getFieldRequesterImpl, "timeStamp.secondsPastEpoch");

View File

@@ -18,6 +18,9 @@ PROD_HOST += testBeaconEmitter
testBeaconEmitter_SRCS += testBeaconEmitter.cpp
testBeaconEmitter_LIBS += pvData pvAccess Com
PROD_HOST += testBeaconHandler
testBeaconHandler_SRCS += testBeaconHandler.cpp
testBeaconHandler_LIBS += pvData pvAccess Com
include $(TOP)/configure/RULES
#----------------------------------------

View File

@@ -42,9 +42,9 @@ void testBeaconEmitter()
osiSockAddr* addr = new osiSockAddr;
addr->ia.sin_family = AF_INET;
addr->ia.sin_port = htons(5067);
if(inet_aton("92.50.75.255",&addr->ia.sin_addr)==0) {
cout<<"error in inet_aton()"<<endl;
return;
if(inet_aton("255.255.255.255",&addr->ia.sin_addr)==0)
{
assert(false);
}
broadcastAddresses->push_back(addr);
BlockingUDPConnector connector(true, broadcastAddresses, true);

View File

@@ -6,6 +6,7 @@
#include "blockingUDP.h"
#include "beaconHandler.h"
#include "inetAddressUtil.h"
#include "introspectionRegistry.h"
#include <osiSock.h>
@@ -14,32 +15,105 @@
using namespace epics::pvAccess;
using namespace epics::pvData;
using namespace std;
void decodeFromIPv6Address(ByteBuffer* buffer, osiSockAddr* address)
{
// IPv4 compatible IPv6 address
// first 80-bit are 0
buffer->getLong();
buffer->getShort();
// next 16-bits are 1
buffer->getShort();
// following IPv4 address in big-endian (network) byte order
in_addr_t ipv4Addr = 0;
ipv4Addr |= (uint32)buffer->getByte() << 24;
ipv4Addr |= (uint32)buffer->getByte() << 16;
ipv4Addr |= (uint32)buffer->getByte() << 8;
ipv4Addr |= (uint32)buffer->getByte() << 0;
address->ia.sin_addr.s_addr = ipv4Addr;
}
class BeaconResponseHandler : public ResponseHandler
{
public:
BeaconResponseHandler()
{
_pvDataCreate = getPVDataCreate();
}
virtual void handleResponse(osiSockAddr* responseFrom,
Transport* transport, int8 version, int8 command, int payloadSize,
ByteBuffer* payloadBuffer)
{
cout << "DummyResponseHandler::handleResponse" << endl;
cout << "BeaconResponseHandler::handleResponse" << endl;
// reception timestamp
TimeStamp timestamp;
timestamp.getCurrent();
//TODO
//super.handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer);
transport->ensureData((2*sizeof(int16)+2*sizeof(int32)+128)/sizeof(int8));
const int32 sequentalID = payloadBuffer->getShort() & 0x0000FFFF;
const TimeStamp startupTimestamp(payloadBuffer->getInt() & 0x00000000FFFFFFFFL,(int32)(payloadBuffer->getInt() & 0x00000000FFFFFFFFL));
// 128-bit IPv6 address
osiSockAddr address;
decodeFromIPv6Address(payloadBuffer, &address);
// get port
const int32 port = payloadBuffer->getShort() & 0xFFFF;
address.ia.sin_port = ntohs(port);
// accept given address if explicitly specified by sender
if (!ipv4AddressToInt(address))
{
responseFrom->ia.sin_port = port;
}
else
{
responseFrom->ia.sin_port = port;
responseFrom->ia.sin_addr.s_addr = address.ia.sin_addr.s_addr;
}
//org.epics.ca.client.impl.remote.BeaconHandler beaconHandler = context.getBeaconHandler(responseFrom);
// currently we care only for servers used by this context
//if (beaconHandler == null)
// return;
// extra data
PVFieldPtr data = NULL;
const FieldConstPtr field = IntrospectionRegistry::deserializeFull(payloadBuffer, transport);
if (field != NULL)
{
data = _pvDataCreate->createPVField(NULL, field);
data->deserialize(payloadBuffer, transport);
}
// notify beacon handler
//beaconHandler.beaconNotify(responseFrom, version, timestamp, startupTimestamp, sequentalID, data);
}
private:
PVDataCreate* _pvDataCreate;
BeaconHandler* _beaconHandler;
};
void testBeaconHandler()
{
BeacondResponseHandler brh;
BeaconResponseHandler brh;
BlockingUDPConnector connector(false, NULL, true);
DummyClientContext context;
osiSockAddr bindAddr;
bindAddr.ia.sin_family = AF_INET;
bindAddr.ia.sin_port = htons(5067);
bindAddr.ia.sin_addr.s_addr = htonl(INADDR_ANY);
Transport* transport = connector.connect(NULL, &brh, &bindAddr, 1, 50);
((BlockingUDPTransport*)transport)->start();
(static_cast<BlockingUDPTransport*>(transport))->start();
while(1) sleep(1);

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,10 @@ PROD_HOST += transportRegisterTest
transportRegisterTest_SRCS += transportRegistryTest.cpp
transportRegisterTest_LIBS += pvAccess Com pvData
PROD_HOST += namedLockPatternTest
namedLockPatternTest_SRCS += namedLockPatternTest.cpp
namedLockPatternTest_LIBS += pvAccess Com pvData
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -0,0 +1,216 @@
/*
* namedLockPatternTest.cpp
*
*/
#include "namedLockPattern.h"
#include "showConstructDestruct.h"
#include <epicsAssert.h>
#include <iostream>
#include <osiSock.h>
using namespace epics::pvAccess;
using namespace std;
void testIntLockPattern()
{
int64 timeout = 100;
NamedLockPattern<int> namedLockPattern;
int name1 = 1;
assert(namedLockPattern.acquireSynchronizationObject(name1,timeout));
assert(namedLockPattern.acquireSynchronizationObject(name1,timeout));
namedLockPattern.releaseSynchronizationObject(name1);
namedLockPattern.releaseSynchronizationObject(name1);
int name2 = 2;
assert(namedLockPattern.acquireSynchronizationObject(name2,timeout));
namedLockPattern.releaseSynchronizationObject(name2);
}
void testIntPtrLockPattern()
{
int64 timeout = 100;
NamedLockPattern<int*> namedLockPattern;
int name1 = 1;
assert(namedLockPattern.acquireSynchronizationObject(&name1,timeout));
assert(namedLockPattern.acquireSynchronizationObject(&name1,timeout));
namedLockPattern.releaseSynchronizationObject(&name1);
namedLockPattern.releaseSynchronizationObject(&name1);
int name2 = 2;
assert(namedLockPattern.acquireSynchronizationObject(&name2,timeout));
namedLockPattern.releaseSynchronizationObject(&name2);
}
struct cmp_str
{
bool operator()(char const *a, char const *b)
{
return strcmp(a, b) < 0;
}
};
void testCharPtrLockPattern()
{
int64 timeout = 100;
NamedLockPattern<const char*,cmp_str> namedLockPattern;
string name1 = "lojze";
assert(namedLockPattern.acquireSynchronizationObject(name1.c_str(),timeout));
assert(namedLockPattern.acquireSynchronizationObject(name1.c_str(),timeout));
namedLockPattern.releaseSynchronizationObject(name1.c_str());
namedLockPattern.releaseSynchronizationObject(name1.c_str());
string name2 = "francka";
assert(namedLockPattern.acquireSynchronizationObject(name2.c_str(),timeout));
namedLockPattern.releaseSynchronizationObject(name2.c_str());
}
struct comp_osiSockAddrPtr
{
bool operator()(osiSockAddr const *a, osiSockAddr const *b)
{
if (a->sa.sa_family < b->sa.sa_family) return true;
if ((a->sa.sa_family == b->sa.sa_family) && (a->ia.sin_addr.s_addr < b->ia.sin_addr.s_addr )) return true;
if ((a->sa.sa_family == b->sa.sa_family) && (a->ia.sin_addr.s_addr == b->ia.sin_addr.s_addr ) && ( a->ia.sin_port < b->ia.sin_port )) return true;
return false;
}
};
void testOsiSockAddrLockPattern()
{
int64 timeout = 10000;
NamedLockPattern<const osiSockAddr*,comp_osiSockAddrPtr> namedLockPattern;
osiSockAddr name1;
name1.ia.sin_addr.s_addr = 1;
name1.ia.sin_port = 1;
name1.ia.sin_family = AF_INET;
assert(namedLockPattern.acquireSynchronizationObject(&name1,timeout));
assert(namedLockPattern.acquireSynchronizationObject(&name1,timeout));
namedLockPattern.releaseSynchronizationObject(&name1);
namedLockPattern.releaseSynchronizationObject(&name1);
osiSockAddr name2;
name2.ia.sin_addr.s_addr = 1;
name2.ia.sin_port = 1;
name2.ia.sin_family = AF_INET;
NamedLock<const osiSockAddr*,comp_osiSockAddrPtr> namedGuard(&namedLockPattern);
assert(namedGuard.acquireSynchronizationObject(&name1,timeout));
}
struct comp_osiSockAddr
{
bool operator()(osiSockAddr const a, osiSockAddr const b)
{
if (a.sa.sa_family < b.sa.sa_family) return true;
if ((a.sa.sa_family == b.sa.sa_family) && (a.ia.sin_addr.s_addr < b.ia.sin_addr.s_addr )) return true;
if ((a.sa.sa_family == b.sa.sa_family) && (a.ia.sin_addr.s_addr == b.ia.sin_addr.s_addr ) && ( a.ia.sin_port < b.ia.sin_port )) return true;
return false;
}
};
void* testWorker1(void* p)
{
int32 timeout = 1000;
const int32 max = 1000;
NamedLockPattern<osiSockAddr,comp_osiSockAddr>* namedLockPattern = (NamedLockPattern<osiSockAddr,comp_osiSockAddr>*)p;
for(int32 i = 0 ; i < max; i = i +2)
{
osiSockAddr addr;
addr.ia.sin_addr.s_addr = i;
addr.ia.sin_port = i;
addr.ia.sin_family = AF_INET;
NamedLock<osiSockAddr,comp_osiSockAddr> namedGuard(namedLockPattern);
assert(namedGuard.acquireSynchronizationObject(addr,timeout));
usleep(1);
}
//this one takes a lock, thread 2 will be slower and will get timeout
{ //due to namedGuard
osiSockAddr addr;
addr.ia.sin_addr.s_addr = 1;
addr.ia.sin_port = 1;
addr.ia.sin_family = AF_INET;
NamedLock<osiSockAddr,comp_osiSockAddr> namedGuard(namedLockPattern);
assert(namedGuard.acquireSynchronizationObject(addr,timeout));
sleep(5);
}
return NULL;
}
void* testWorker2(void* p)
{
int32 timeout = 1000;
const int32 max = 1000;
NamedLockPattern<osiSockAddr,comp_osiSockAddr>* namedLockPattern = (NamedLockPattern<osiSockAddr,comp_osiSockAddr>*)p;
for(int32 i = 1 ; i < max; i = i + 2)
{
osiSockAddr addr;
addr.ia.sin_addr.s_addr = i;
addr.ia.sin_port = i;
addr.ia.sin_family = AF_INET;
NamedLock<osiSockAddr,comp_osiSockAddr> namedGuard(namedLockPattern);
assert(namedGuard.acquireSynchronizationObject(addr,timeout));
usleep(1);
}
//this thread sleeps a while and gets timeout on lock
{
sleep(1);
osiSockAddr addr;
addr.ia.sin_addr.s_addr = 1;
addr.ia.sin_port = 1;
addr.ia.sin_family = AF_INET;
NamedLock<osiSockAddr,comp_osiSockAddr> namedGuard(namedLockPattern);
assert(!namedGuard.acquireSynchronizationObject(addr,timeout));
}
return NULL;
}
int main(int argc, char *argv[])
{
testIntLockPattern();
testIntPtrLockPattern();
testCharPtrLockPattern();
testOsiSockAddrLockPattern();
pthread_t _worker1Id;
pthread_t _worker2Id;
NamedLockPattern<osiSockAddr,comp_osiSockAddr> namedLockPattern;
//create two threads
int32 retval = pthread_create(&_worker1Id, NULL, testWorker1, &namedLockPattern);
if(retval != 0)
{
assert(true);
}
retval = pthread_create(&_worker2Id, NULL, testWorker2, &namedLockPattern);
if(retval != 0)
{
assert(true);
}
//wait for threads
retval = pthread_join(_worker1Id, NULL);
if(retval != 0)
{
assert(true);
}
retval = pthread_join(_worker2Id, NULL);
if(retval != 0)
{
assert(true);
}
getShowConstructDestruct()->constuctDestructTotals(stdout);
return 0;
}