manual merge

This commit is contained in:
Matej Sekoranja
2016-03-02 13:34:05 +01:00
39 changed files with 2180 additions and 1413 deletions

5
.gitignore vendored
View File

@@ -12,3 +12,8 @@ configure/*.local
!configure/ExampleRELEASE.local
**/O.*
QtC-*
*.config
*.creator
*.files
*.includes
*.user

View File

@@ -32,6 +32,7 @@
namespace epics {
namespace pvAccess {
class Configuration;
// TODO add write-only?
// change names
@@ -613,6 +614,8 @@ namespace pvAccess {
public:
POINTER_DEFINITIONS(Channel);
virtual ~Channel() {}
/**
* Channel connection status.
*/
@@ -630,8 +633,11 @@ namespace pvAccess {
// virtual ChannelProvider::shared_pointer getProvider() = 0;
/**
* Returns the channel's remote address, e.g. "/192.168.1.101:5064" or "#C0 S1".
* @return the channel's remote address.
* Returns the channel's remote address, signal name, etc...
* For example:
* - client side channel would return server's address, e.g. "/192.168.1.101:5064"
* - server side channel would return underlying bus address, e.g. "#C0 S1".
* @return the channel's address.
**/
virtual std::string getRemoteAddress() = 0;
@@ -870,7 +876,7 @@ namespace pvAccess {
virtual Channel::shared_pointer createChannel(std::string const & channelName,ChannelRequester::shared_pointer const & channelRequester,
short priority, std::string const & address) = 0;
virtual void configure(epics::pvData::PVStructure::shared_pointer /*configuration*/) {};
virtual void configure(epics::pvData::PVStructure::shared_pointer /*configuration*/) EPICS_DEPRECATED {};
virtual void flush() {};
virtual void poll() {};
@@ -892,16 +898,26 @@ namespace pvAccess {
virtual std::string getFactoryName() = 0;
/**
* Get a shared instance.
* Get a shared instance using the default Configuration.
* @return a shared instance.
*/
virtual ChannelProvider::shared_pointer sharedInstance() = 0;
/**
* Create a new instance.
* Create a new instance using the default Configuration.
* @return a new instance.
*/
virtual ChannelProvider::shared_pointer newInstance() = 0;
virtual ChannelProvider::shared_pointer newInstance() {
return newInstance(std::tr1::shared_ptr<Configuration>());
}
/**
* Create a new instance using a specific Configuration.
* @return a new instance.
*/
virtual ChannelProvider::shared_pointer newInstance(const std::tr1::shared_ptr<Configuration>&) {
throw std::logic_error("This ChannelProviderFactory does not support non-default configurations");
}
};
/**

View File

@@ -16,9 +16,8 @@
using namespace epics::pvData;
using namespace epics::pvAccess;
// TODO global static variable (de/initialization order not guaranteed)
static Mutex mutex;
static ClientContextImpl::shared_pointer context;
static Mutex cfact_mutex;
static ChannelProvider::shared_pointer cfact_shared_provider;
class ChannelProviderFactoryImpl : public ChannelProviderFactory
{
@@ -32,70 +31,47 @@ public:
virtual ChannelProvider::shared_pointer sharedInstance()
{
Lock guard(mutex);
if (!context.get())
Lock guard(cfact_mutex);
if (!cfact_shared_provider)
{
try {
ClientContextImpl::shared_pointer lcontext = createClientContextImpl();
lcontext->initialize();
context = lcontext;
} catch (std::exception &e) {
LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what());
} catch (...) {
LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__);
}
Configuration::shared_pointer def;
cfact_shared_provider = createClientProvider(def);
}
return context->getProvider();
return cfact_shared_provider;
}
virtual ChannelProvider::shared_pointer newInstance()
virtual ChannelProvider::shared_pointer newInstance(const std::tr1::shared_ptr<Configuration>& conf)
{
Lock guard(mutex);
try {
ClientContextImpl::shared_pointer lcontext = createClientContextImpl();
lcontext->initialize();
return lcontext->getProvider();
} catch (std::exception &e) {
LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what());
return ChannelProvider::shared_pointer();
} catch (...) {
LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__);
return ChannelProvider::shared_pointer();
}
}
void destroySharedInstance()
{
Lock guard(mutex);
if (context.get())
{
context->dispose();
context.reset();
}
Lock guard(cfact_mutex);
return createClientProvider(conf);
}
};
static ChannelProviderFactoryImpl::shared_pointer factory;
static ChannelProviderFactoryImpl::shared_pointer pva_factory;
void ClientFactory::start()
{
epicsSignalInstallSigAlarmIgnore();
epicsSignalInstallSigPipeIgnore();
Lock guard(mutex);
if (!factory.get())
factory.reset(new ChannelProviderFactoryImpl());
Lock guard(cfact_mutex);
if (!pva_factory)
pva_factory.reset(new ChannelProviderFactoryImpl());
registerChannelProviderFactory(factory);
registerChannelProviderFactory(pva_factory);
}
void ClientFactory::stop()
{
Lock guard(mutex);
Lock guard(cfact_mutex);
if (factory.get())
if (pva_factory)
{
unregisterChannelProviderFactory(factory);
factory->destroySharedInstance();
unregisterChannelProviderFactory(pva_factory);
if(!pva_factory.unique()) {
LOG(logLevelWarn, "ClientFactory::stop() finds shared client context with %u remaining users",
(unsigned)pva_factory.use_count());
}
pva_factory.reset();
}
}

View File

@@ -22,7 +22,7 @@ namespace epics {
namespace pvAccess {
void AbstractResponseHandler::handleResponse(osiSockAddr* responseFrom,
Transport::shared_pointer const & /*transport*/, int8 version, int8 command,
Transport::shared_pointer const & transport, int8 version, int8 command,
size_t payloadSize, ByteBuffer* payloadBuffer) {
if(_debugLevel >= 3) { // TODO make a constant of sth (0 - off, 1 - debug, 2 - more/trace, 3 - messages)
char ipAddrStr[48];
@@ -30,7 +30,7 @@ namespace epics {
ostringstream prologue;
prologue<<"Message [0x"<<hex<<(int)command<<", v0x"<<hex;
prologue<<(int)version<<"] received from "<<ipAddrStr;
prologue<<(int)version<<"] received from "<<ipAddrStr<<" on "<<transport->getRemoteName();
hexDump(prologue.str(), _description,
(const int8*)payloadBuffer->getArray(),

View File

@@ -24,36 +24,56 @@ namespace pvAccess {
BlockingTCPAcceptor::BlockingTCPAcceptor(
Context::shared_pointer const & context,
ResponseHandlerFactory::shared_pointer const & responseHandlerFactory,
ResponseHandler::shared_pointer const & responseHandler,
int port,
int receiveBufferSize) :
_context(context),
_responseHandlerFactory(responseHandlerFactory),
_responseHandler(responseHandler),
_bindAddress(),
_serverSocketChannel(INVALID_SOCKET),
_receiveBufferSize(receiveBufferSize),
_destroyed(false),
_threadId(0)
_thread(*this, "TCP-acceptor",
epicsThreadGetStackSize(
epicsThreadStackMedium),
epicsThreadPriorityMedium)
{
initialize(port);
_bindAddress.ia.sin_family = AF_INET;
_bindAddress.ia.sin_port = htons(port);
_bindAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
initialize();
}
BlockingTCPAcceptor::BlockingTCPAcceptor(Context::shared_pointer const & context,
ResponseHandler::shared_pointer const & responseHandler,
const osiSockAddr& addr, int receiveBufferSize) :
_context(context),
_responseHandler(responseHandler),
_bindAddress(),
_serverSocketChannel(INVALID_SOCKET),
_receiveBufferSize(receiveBufferSize),
_destroyed(false),
_thread(*this, "TCP-acceptor",
epicsThreadGetStackSize(
epicsThreadStackMedium),
epicsThreadPriorityMedium)
{
_bindAddress = addr;
initialize();
}
BlockingTCPAcceptor::~BlockingTCPAcceptor() {
destroy();
}
int BlockingTCPAcceptor::initialize(unsigned short port) {
// specified bind address
_bindAddress.ia.sin_family = AF_INET;
_bindAddress.ia.sin_port = htons(port);
_bindAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
int BlockingTCPAcceptor::initialize() {
char strBuffer[64];
char ipAddrStr[48];
ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr));
int tryCount = 0;
while(tryCount<2) {
char strBuffer[64];
LOG(logLevelDebug, "Creating acceptor to %s.", ipAddrStr);
@@ -80,7 +100,7 @@ namespace pvAccess {
LOG(
logLevelDebug,
"Configured TCP port %d is unavailable, trying to assign it dynamically.",
port);
ntohs(_bindAddress.ia.sin_port));
_bindAddress.ia.sin_port = htons(0);
}
else {
@@ -109,7 +129,7 @@ namespace pvAccess {
}
}
retval = ::listen(_serverSocketChannel, 1024);
retval = ::listen(_serverSocketChannel, 4);
if(retval<0) {
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
ostringstream temp;
@@ -118,14 +138,7 @@ namespace pvAccess {
THROW_BASE_EXCEPTION(temp.str().c_str());
}
_threadId
= epicsThreadCreate(
"TCP-acceptor",
epicsThreadPriorityMedium,
epicsThreadGetStackSize(
epicsThreadStackMedium),
BlockingTCPAcceptor::handleEventsRunner,
this);
_thread.start();
// all OK, return
return ntohs(_bindAddress.ia.sin_port);
@@ -139,7 +152,7 @@ namespace pvAccess {
THROW_BASE_EXCEPTION(temp.str().c_str());
}
void BlockingTCPAcceptor::handleEvents() {
void BlockingTCPAcceptor::run() {
// rise level if port is assigned dynamically
char ipAddrStr[48];
ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr));
@@ -194,12 +207,11 @@ namespace pvAccess {
/**
* Create transport, it registers itself to the registry.
*/
std::auto_ptr<ResponseHandler> responseHandler = _responseHandlerFactory->createResponseHandler();
detail::BlockingServerTCPTransportCodec::shared_pointer transport =
detail::BlockingServerTCPTransportCodec::create(
_context,
newClient,
responseHandler,
_responseHandler,
_socketSendBufferSize,
_receiveBufferSize);
@@ -235,21 +247,36 @@ namespace pvAccess {
}
}
void BlockingTCPAcceptor::handleEventsRunner(void* param) {
((BlockingTCPAcceptor*)param)->handleEvents();
}
void BlockingTCPAcceptor::destroy() {
Lock guard(_mutex);
if(_destroyed) return;
_destroyed = true;
SOCKET sock;
{
Lock guard(_mutex);
if(_destroyed) return;
_destroyed = true;
if(_serverSocketChannel!=INVALID_SOCKET) {
sock = _serverSocketChannel;
_serverSocketChannel = INVALID_SOCKET;
}
if(sock!=INVALID_SOCKET) {
char ipAddrStr[48];
ipAddrToDottedIP(&_bindAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Stopped accepting connections at %s.", ipAddrStr);
epicsSocketDestroy(_serverSocketChannel);
switch(epicsSocketSystemCallInterruptMechanismQuery())
{
case esscimqi_socketBothShutdownRequired:
shutdown(sock, SHUT_RDWR);
epicsSocketDestroy(sock);
_thread.exitWait();
break;
case esscimqi_socketSigAlarmRequired:
LOG(logLevelError, "SigAlarm close not implemented for this target\n");
case esscimqi_socketCloseRequired:
epicsSocketDestroy(sock);
_thread.exitWait();
break;
}
}
}

View File

@@ -68,7 +68,7 @@ namespace epics {
}
Transport::shared_pointer BlockingTCPConnector::connect(TransportClient::shared_pointer const & client,
std::auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& address,
ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& address,
int8 transportRevision, int16 priority) {
SOCKET socket = INVALID_SOCKET;

View File

@@ -19,7 +19,7 @@ namespace epics {
namespace pvAccess {
Transport::shared_pointer BlockingUDPConnector::connect(TransportClient::shared_pointer const & /*client*/,
auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& bindAddress,
ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& bindAddress,
int8 transportRevision, int16 /*priority*/) {
LOG(logLevelDebug, "Creating datagram socket to: %s.",
@@ -68,9 +68,10 @@ namespace epics {
}
// sockets are blocking by default
Transport::shared_pointer transport = BlockingUDPTransport::create(_serverFlag,
responseHandler, socket, bindAddress, transportRevision);
return transport;
BlockingUDPTransport::shared_pointer transport(new BlockingUDPTransport(_serverFlag, responseHandler,
socket, bindAddress, transportRevision));
return Transport::shared_pointer(transport);
}
}

View File

@@ -38,11 +38,13 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
}
#endif
// reserve some space for CMD_ORIGIN_TAG message
#define RECEIVE_BUFFER_PRE_RESERVE PVA_MESSAGE_HEADER_SIZE + 16
PVACCESS_REFCOUNT_MONITOR_DEFINE(blockingUDPTransport);
BlockingUDPTransport::BlockingUDPTransport(
bool serverFlag,
auto_ptr<ResponseHandler>& responseHandler, SOCKET channel,
BlockingUDPTransport::BlockingUDPTransport(bool serverFlag,
ResponseHandler::shared_pointer const & responseHandler, SOCKET channel,
osiSockAddr& bindAddress,
short /*remoteTransportRevision*/) :
_closed(),
@@ -51,15 +53,17 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
_bindAddress(bindAddress),
_sendAddresses(0),
_ignoredAddresses(0),
_tappedNIF(0),
_sendToEnabled(false),
_receiveBuffer(new ByteBuffer(MAX_UDP_RECV)),
_localMulticastAddressEnabled(false),
_receiveBuffer(new ByteBuffer(MAX_UDP_RECV+RECEIVE_BUFFER_PRE_RESERVE)),
_sendBuffer(new ByteBuffer(MAX_UDP_RECV)),
_lastMessageStartPosition(0),
_threadId(0),
_clientServerWithEndianFlag(
(serverFlag ? 0x40 : 0x00) | ((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00))
{
PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(blockingUDPTransport);
assert(_responseHandler.get());
osiSocklen_t sockLen = sizeof(sockaddr);
// read the actual socket info
@@ -71,6 +75,11 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
char strBuffer[64];
epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer));
LOG(logLevelDebug, "getsockname error: %s.", strBuffer);
_remoteName = "<unknown>:0";
} else {
char strBuffer[64];
sockAddrToA(&_remoteAddress.sa, strBuffer, sizeof(strBuffer));
_remoteName = strBuffer;
}
}
@@ -94,10 +103,10 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
LOG(logLevelTrace, "Starting thread: %s.", threadName.c_str());
}
_threadId = epicsThreadCreate(threadName.c_str(),
epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackSmall),
BlockingUDPTransport::threadRunner, this);
_thread.reset(new epicsThread(*this, threadName.c_str(),
epicsThreadGetStackSize(epicsThreadStackSmall),
epicsThreadPriorityMedium));
_thread->start();
}
void BlockingUDPTransport::close() {
@@ -149,7 +158,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
// wait for send thread to exit cleanly
if (waitForThreadToComplete)
if (_thread.get() && waitForThreadToComplete)
{
if (!_shutdownEvent.wait(5.0))
{
@@ -202,25 +211,31 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
_sendBuffer->getPosition()-_lastMessageStartPosition-PVA_MESSAGE_HEADER_SIZE);
}
void BlockingUDPTransport::processRead() {
void BlockingUDPTransport::run() {
// This function is always called from only one thread - this
// object's own thread.
osiSockAddr fromAddress;
osiSocklen_t addrStructSize = sizeof(sockaddr);
Transport::shared_pointer thisTransport = shared_from_this();
Transport::shared_pointer thisTransport = shared_from_this(),
replyTo(_replyTransport ? _replyTransport : thisTransport);
try {
char* recvfrom_buffer_start = (char*)(_receiveBuffer->getArray()+RECEIVE_BUFFER_PRE_RESERVE);
size_t recvfrom_buffer_len =_receiveBuffer->getSize()-RECEIVE_BUFFER_PRE_RESERVE;
while(!_closed.get())
{
// we poll to prevent blocking indefinitely
// data ready to be read
_receiveBuffer->clear();
// reserve some space for CMD_ORIGIN_TAG
_receiveBuffer->setPosition(RECEIVE_BUFFER_PRE_RESERVE);
int bytesRead = recvfrom(_channel, (char*)_receiveBuffer->getArray(),
_receiveBuffer->getRemaining(), 0, (sockaddr*)&fromAddress,
int bytesRead = recvfrom(_channel,
recvfrom_buffer_start, recvfrom_buffer_len,
0, (sockaddr*)&fromAddress,
&addrStructSize);
if(likely(bytesRead>0)) {
@@ -239,12 +254,11 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
}
if(likely(!ignore)) {
_receiveBuffer->setPosition(bytesRead);
_receiveBuffer->flip();
_receiveBuffer->setPosition(RECEIVE_BUFFER_PRE_RESERVE);
_receiveBuffer->setLimit(RECEIVE_BUFFER_PRE_RESERVE+bytesRead);
try{
processBuffer(thisTransport, fromAddress, _receiveBuffer.get());
processBuffer(replyTo, fromAddress, _receiveBuffer.get());
}catch(std::exception& e){
LOG(logLevelError,
"an exception caught while in UDP receiveThread at %s:%d: %s",
@@ -299,7 +313,8 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
_shutdownEvent.signal();
}
bool BlockingUDPTransport::processBuffer(Transport::shared_pointer const & thisTransport, osiSockAddr& fromAddress, ByteBuffer* receiveBuffer) {
bool BlockingUDPTransport::processBuffer(Transport::shared_pointer const & replyTransport,
osiSockAddr& fromAddress, ByteBuffer* receiveBuffer) {
// handle response(s)
while(likely((int)receiveBuffer->getRemaining()>=PVA_MESSAGE_HEADER_SIZE)) {
@@ -315,11 +330,10 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
// second byte version
int8 version = receiveBuffer->getByte();
// only data for UDP
int8 flags = receiveBuffer->getByte();
if (flags < 0)
if (flags & 0x80)
{
// 7-bit set
// 7th bit set
receiveBuffer->setEndianess(EPICS_ENDIAN_BIG);
}
else
@@ -331,34 +345,111 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
int8 command = receiveBuffer->getByte();
// TODO check this cast (size_t must be 32-bit)
size_t payloadSize = receiveBuffer->getInt();
// control message check (skip message)
if (flags & 0x01)
continue;
size_t nextRequestPosition = receiveBuffer->getPosition() + payloadSize;
// payload size check
if(unlikely(nextRequestPosition>receiveBuffer->getLimit())) return false;
// handle
_responseHandler->handleResponse(&fromAddress, thisTransport,
version, command, payloadSize,
_receiveBuffer.get());
// CMD_ORIGIN_TAG filtering
// NOTE: from design point of view this is not a right place to process application message here
if (unlikely(command == CMD_ORIGIN_TAG))
{
// enabled?
if (_tappedNIF)
{
// 128-bit IPv6 address
osiSockAddr originNIFAddress;
if (decodeAsIPv6Address(receiveBuffer, &originNIFAddress))
{
originNIFAddress.ia.sin_family = AF_INET;
/*
LOG(logLevelDebug, "Got CMD_ORIGIN_TAG message form %s tagged as %s.",
inetAddressToString(fromAddress, true).c_str(),
inetAddressToString(originNIFAddress, false).c_str());
*/
// filter
if (originNIFAddress.ia.sin_addr.s_addr != htonl(INADDR_ANY))
{
bool accept = false;
for(size_t i = 0; i < _tappedNIF->size(); i++)
{
if((*_tappedNIF)[i].ia.sin_addr.s_addr == originNIFAddress.ia.sin_addr.s_addr)
{
accept = true;
break;
}
}
// ignore messages from non-tapped NIFs
if (!accept)
return false;
}
}
}
}
else
{
// handle
_responseHandler->handleResponse(&fromAddress, replyTransport,
version, command, payloadSize,
_receiveBuffer.get());
}
// set position (e.g. in case handler did not read all)
receiveBuffer->setPosition(nextRequestPosition);
}
//all ok
// all ok
return true;
}
bool BlockingUDPTransport::send(const char* buffer, size_t length, const osiSockAddr& address)
{
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug, "Sending %d bytes to %s.",
length, inetAddressToString(address).c_str());
}
int retval = sendto(_channel, buffer,
length, 0, &(address.sa), sizeof(sockaddr));
if(unlikely(retval<0))
{
char errStr[64];
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
LOG(logLevelDebug, "Socket sendto to %s error: %s.",
inetAddressToString(address).c_str(), errStr);
return false;
}
return true;
}
bool BlockingUDPTransport::send(ByteBuffer* buffer, const osiSockAddr& address) {
buffer->flip();
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug, "Sending %d bytes to %s.",
buffer->getRemaining(), inetAddressToString(address).c_str());
}
int retval = sendto(_channel, buffer->getArray(),
buffer->getLimit(), 0, &(address.sa), sizeof(sockaddr));
if(unlikely(retval<0))
{
char errStr[64];
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
LOG(logLevelDebug, "Socket sendto error: %s.", errStr);
LOG(logLevelDebug, "Socket sendto to %s error: %s.",
inetAddressToString(address).c_str(), errStr);
return false;
}
@@ -382,6 +473,12 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
(target == inetAddressType_broadcast_multicast && _isSendAddressUnicast[i]))
continue;
if (IS_LOGGABLE(logLevelDebug))
{
LOG(logLevelDebug, "Sending %d bytes to %s.",
buffer->getRemaining(), inetAddressToString((*_sendAddresses)[i]).c_str());
}
int retval = sendto(_channel, buffer->getArray(),
buffer->getLimit(), 0, &((*_sendAddresses)[i].sa),
sizeof(sockaddr));
@@ -389,7 +486,8 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
{
char errStr[64];
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
LOG(logLevelDebug, "Socket sendto error: %s.", errStr);
LOG(logLevelDebug, "Socket sendto to %s error: %s.",
inetAddressToString((*_sendAddresses)[i]).c_str(), errStr);
allOK = false;
}
}
@@ -419,10 +517,6 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
return (size_t)sockBufSize;
}
void BlockingUDPTransport::threadRunner(void* param) {
((BlockingUDPTransport*)param)->processRead();
}
void BlockingUDPTransport::join(const osiSockAddr & mcastAddr, const osiSockAddr & nifAddr)
{
@@ -432,7 +526,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
imreq.imr_multiaddr.s_addr = mcastAddr.ia.sin_addr.s_addr;
imreq.imr_interface.s_addr = nifAddr.ia.sin_addr.s_addr;
// join multicast group on default interface
// join multicast group on the given interface
int status = ::setsockopt(_channel, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char*)&imreq, sizeof(struct ip_mreq));
if (status)
@@ -456,7 +550,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
char errStr[64];
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
throw std::runtime_error(
string("Failed to set multicast network inteface '") +
string("Failed to set multicast network interface '") +
inetAddressToString(nifAddr, false) + "': " + errStr);
}
@@ -469,12 +563,247 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
char errStr[64];
epicsSocketConvertErrnoToString(errStr, sizeof(errStr));
throw std::runtime_error(
string("Failed to enable multicast loopback on network inteface '") +
string("Failed to enable multicast loopback on network interface '") +
inetAddressToString(nifAddr, false) + "': " + errStr);
}
}
void initializeUDPTransports(bool serverFlag,
BlockingUDPTransportVector& udpTransports,
const IfaceNodeVector& ifaceList,
const ResponseHandler::shared_pointer& responseHandler,
BlockingUDPTransport::shared_pointer& sendTransport,
int32& listenPort,
bool autoAddressList,
const std::string& addressList,
const std::string& ignoreAddressList)
{
TransportClient::shared_pointer nullTransportClient;
auto_ptr<BlockingUDPConnector> connector(new BlockingUDPConnector(serverFlag, true, true));
// TODO configurable local NIF, address
osiSockAddr loAddr;
getLoopbackNIF(loAddr, "", 0);
// TODO configurable local multicast address
std::string mcastAddress("224.0.0.128");
osiSockAddr group;
aToIPAddr(mcastAddress.c_str(), listenPort, &group.ia);
//
// set ignore address list
//
auto_ptr<InetAddrVector> ignoreAddressVector;
if (!ignoreAddressList.empty())
ignoreAddressVector.reset(getSocketAddressList(ignoreAddressList, 0, 0));
//
// Setup UDP trasport(s) (per interface)
//
InetAddrVector tappedNIF;
for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++)
{
ifaceNode node = *iter;
LOG(logLevelDebug, "Setting up UDP for interface %s, broadcast %s.",
inetAddressToString(node.ifaceAddr, false).c_str(),
inetAddressToString(node.ifaceBCast, false).c_str());
try
{
// where to bind (listen) address
osiSockAddr listenLocalAddress;
listenLocalAddress.ia.sin_family = AF_INET;
listenLocalAddress.ia.sin_port = htons(listenPort);
listenLocalAddress.ia.sin_addr.s_addr = node.ifaceAddr.ia.sin_addr.s_addr;
BlockingUDPTransport::shared_pointer transport = static_pointer_cast<BlockingUDPTransport>(connector->connect(
nullTransportClient, responseHandler,
listenLocalAddress, PVA_PROTOCOL_REVISION,
PVA_DEFAULT_PRIORITY));
listenLocalAddress = *transport->getRemoteAddress();
// to allow automatic assignment of listen port (for testing)
if (listenPort == 0)
{
listenPort = ntohs(listenLocalAddress.ia.sin_port);
aToIPAddr(mcastAddress.c_str(), listenPort, &group.ia);
LOG(logLevelDebug, "Dynamic listen UDP port set to %d.", listenPort);
}
if (ignoreAddressVector.get() && ignoreAddressVector->size())
transport->setIgnoredAddresses(ignoreAddressVector.get());
tappedNIF.push_back(listenLocalAddress);
BlockingUDPTransport::shared_pointer transport2;
if(node.ifaceBCast.ia.sin_family == AF_UNSPEC ||
node.ifaceBCast.ia.sin_addr.s_addr == listenLocalAddress.ia.sin_addr.s_addr) {
LOG(logLevelWarn, "Unable to find broadcast address of interface %s.", inetAddressToString(node.ifaceAddr, false).c_str());
}
#if !defined(_WIN32)
else
{
/* An oddness of BSD sockets (not winsock) is that binding to
* INADDR_ANY will receive unicast and broadcast, but binding to
* a specific interface address receives only unicast. The trick
* is to bind a second socket to the interface broadcast address,
* which will then receive only broadcasts.
*/
osiSockAddr bcastAddress;
bcastAddress.ia.sin_family = AF_INET;
bcastAddress.ia.sin_port = htons(listenPort);
bcastAddress.ia.sin_addr.s_addr = node.ifaceBCast.ia.sin_addr.s_addr;
transport2 = static_pointer_cast<BlockingUDPTransport>(connector->connect(
nullTransportClient, responseHandler,
bcastAddress, PVA_PROTOCOL_REVISION,
PVA_DEFAULT_PRIORITY));
/* The other wrinkle is that nothing should be sent from this second
* socket. So replies are made through the unicast socket.
*/
transport2->setReplyTransport(transport);
if (ignoreAddressVector.get() && ignoreAddressVector->size())
transport2->setIgnoredAddresses(ignoreAddressVector.get());
tappedNIF.push_back(bcastAddress);
}
#endif
transport->setMutlicastNIF(loAddr, true);
transport->setLocalMulticastAddress(group);
transport->start();
udpTransports.push_back(transport);
if (transport2)
{
transport2->start();
udpTransports.push_back(transport2);
}
}
catch (std::exception& e)
{
THROW_BASE_EXCEPTION_CAUSE("Failed to initialize UDP transport.", e);
}
catch (...)
{
THROW_BASE_EXCEPTION("Failed to initialize UDP transport.");
}
}
//
// Create UDP transport for sending (to all network interfaces)
//
osiSockAddr anyAddress;
anyAddress.ia.sin_family = AF_INET;
anyAddress.ia.sin_port = htons(0);
anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
sendTransport = static_pointer_cast<BlockingUDPTransport>(connector->connect(
nullTransportClient, responseHandler,
anyAddress, PVA_PROTOCOL_REVISION,
PVA_DEFAULT_PRIORITY));
// TODO current implementation shares the port (aka beacon and search port)
int32 sendPort = listenPort;
//
// compile auto address list - where to send packets
//
InetAddrVector autoBCastAddr;
for (IfaceNodeVector::const_iterator iter = ifaceList.begin(); iter != ifaceList.end(); iter++)
{
ifaceNode node = *iter;
if (node.ifaceBCast.ia.sin_family != AF_UNSPEC)
{
node.ifaceBCast.ia.sin_port = htons(sendPort);
autoBCastAddr.push_back(node.ifaceBCast);
}
}
//
// set send address list
//
if (!addressList.empty())
{
// if auto is true, add it to specified list
if (!autoAddressList)
autoBCastAddr.clear();
auto_ptr<InetAddrVector> list(getSocketAddressList(addressList, sendPort, &autoBCastAddr));
if (list.get() && list->size())
{
sendTransport->setSendAddresses(list.get());
}
/*
else
{
// fallback
// set default (auto) address list
sendTransport->setSendAddresses(&autoBCastAddr);
}
*/
}
else if (autoAddressList)
{
// set default (auto) address list
sendTransport->setSendAddresses(&autoBCastAddr);
}
sendTransport->start();
udpTransports.push_back(sendTransport);
// debug output of broadcast addresses
InetAddrVector* blist = sendTransport->getSendAddresses();
if (!blist || !blist->size())
LOG(logLevelError,
"No broadcast addresses found or specified - empty address list!");
else
for (size_t i = 0; i < blist->size(); i++)
LOG(logLevelDebug,
"Broadcast address #%d: %s.", i, inetAddressToString((*blist)[i]).c_str());
//
// Setup local multicasting
//
BlockingUDPTransport::shared_pointer localMulticastTransport;
try
{
// NOTE: multicast receiver socket must be "bound" to INADDR_ANY or multicast address
localMulticastTransport = static_pointer_cast<BlockingUDPTransport>(connector->connect(
nullTransportClient, responseHandler,
group, PVA_PROTOCOL_REVISION,
PVA_DEFAULT_PRIORITY));
localMulticastTransport->setTappedNIF(&tappedNIF);
localMulticastTransport->join(group, loAddr);
localMulticastTransport->start();
udpTransports.push_back(localMulticastTransport);
LOG(logLevelDebug, "Local multicast enabled on %s/%s.",
inetAddressToString(loAddr, false).c_str(),
inetAddressToString(group).c_str());
}
catch (std::exception& ex)
{
LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what());
}
}
}
}

View File

@@ -32,6 +32,18 @@ using namespace std;
using namespace epics::pvData;
using namespace epics::pvAccess;
namespace {
struct BreakTransport : TransportSender
{
virtual ~BreakTransport() {}
virtual void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control)
{
throw epics::pvAccess::detail::connection_closed_exception("Break");
}
virtual void lock() {}
virtual void unlock() {}
};
} // namespace
namespace epics {
namespace pvAccess {
@@ -53,7 +65,7 @@ namespace epics {
//PROTECTED
_readMode(NORMAL), _version(0), _flags(0), _command(0), _payloadSize(0),
_remoteTransportSocketReceiveBufferSize(MAX_TCP_RECV), _totalBytesSent(0),
_blockingProcessQueue(false), _senderThread(0),
_senderThread(0),
_writeMode(PROCESS_SEND_QUEUE),
_writeOpReady(false),_lowLatency(false),
_socketBuffer(receiveBuffer),
@@ -98,7 +110,6 @@ namespace epics {
_maxSendPayloadSize =
_sendBuffer->getSize() - 2*PVA_MESSAGE_HEADER_SIZE;
_socketSendBufferSize = socketSendBufferSize;
_blockingProcessQueue = blockingProcessQueue;
}
@@ -851,7 +862,8 @@ namespace epics {
std::size_t senderProcessed = 0;
while (senderProcessed++ < MAX_MESSAGE_SEND)
{
TransportSender::shared_pointer sender = _sendQueue.take(-1);
TransportSender::shared_pointer sender;
_sendQueue.pop_front_try(sender);
if (sender.get() == 0)
{
// flush
@@ -860,19 +872,20 @@ namespace epics {
sendCompleted(); // do not schedule sending
if (_blockingProcessQueue) {
if (terminated()) // termination
if (terminated()) // termination
break;
sender = _sendQueue.take(0);
// termination (we want to process even if shutdown)
if (sender.get() == 0)
break;
}
else
return;
// termination (we want to process even if shutdown)
_sendQueue.pop_front(sender);
}
processSender(sender);
try{
processSender(sender);
}catch(...){
if (_sendBuffer->getPosition() > 0)
flush(true);
sendCompleted();
throw;
}
}
}
@@ -882,15 +895,9 @@ namespace epics {
}
void AbstractCodec::clearSendQueue()
{
_sendQueue.clean();
}
void AbstractCodec::enqueueSendRequest(
TransportSender::shared_pointer const & sender) {
_sendQueue.put(sender);
_sendQueue.push_back(sender);
scheduleSend();
}
@@ -1027,6 +1034,29 @@ namespace epics {
//
//
BlockingAbstractCodec::BlockingAbstractCodec(
bool serverFlag,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & receiveBuffer,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & sendBuffer,
int32_t socketSendBufferSize)
:AbstractCodec(serverFlag, receiveBuffer, sendBuffer, socketSendBufferSize, true)
,_readThread(Thread::Config(this, &BlockingAbstractCodec::receiveThread)
.prio(epicsThreadPriorityCAServerLow)
.name("TCP-rx")
.autostart(false))
,_sendThread(Thread::Config(this, &BlockingAbstractCodec::sendThread)
.prio(epicsThreadPriorityCAServerLow)
.name("TCP-tx")
.autostart(false))
{ _isOpen.getAndSet(true);}
BlockingAbstractCodec::~BlockingAbstractCodec()
{
assert(!_isOpen.get());
_sendThread.exitWait();
_readThread.exitWait();
}
void BlockingAbstractCodec::readPollOne() {
throw std::logic_error("should not be called for blocking IO");
}
@@ -1044,20 +1074,21 @@ namespace epics {
// always close in the same thread, same way, etc.
// wakeup processSendQueue
// clean resources
// clean resources (close socket)
internalClose(true);
// this is important to avoid cyclic refs (memory leak)
clearSendQueue();
_sendQueue.wakeup();
// Break sender from queue wait
BreakTransport::shared_pointer B(new BreakTransport);
enqueueSendRequest(B);
// post close
internalPostClose(true);
}
}
void BlockingAbstractCodec::internalClose(bool /*force*/) {
void BlockingAbstractCodec::internalClose(bool /*force*/)
{
this->internalDestroy();
}
void BlockingAbstractCodec::internalPostClose(bool /*force*/) {
@@ -1076,35 +1107,21 @@ namespace epics {
// NOTE: must not be called from constructor (e.g. needs shared_from_this())
void BlockingAbstractCodec::start() {
_readThread = epicsThreadCreate(
"BlockingAbstractCodec-readThread",
epicsThreadPriorityMedium,
epicsThreadGetStackSize(
epicsThreadStackMedium),
BlockingAbstractCodec::receiveThread,
this);
_readThread.start();
_sendThread = epicsThreadCreate(
"BlockingAbstractCodec-_sendThread",
epicsThreadPriorityMedium,
epicsThreadGetStackSize(
epicsThreadStackMedium),
BlockingAbstractCodec::sendThread,
this);
_sendThread.start();
}
void BlockingAbstractCodec::receiveThread(void *param)
void BlockingAbstractCodec::receiveThread()
{
Transport::shared_pointer ptr = this->shared_from_this();
BlockingAbstractCodec *bac = static_cast<BlockingAbstractCodec *>(param);
Transport::shared_pointer ptr = bac->shared_from_this();
while (bac->isOpen())
while (this->isOpen())
{
try {
bac->processRead();
this->processRead();
} catch (std::exception &e) {
LOG(logLevelError,
"an exception caught while in receiveThread at %s:%d: %s",
@@ -1116,22 +1133,20 @@ namespace epics {
}
}
bac->_shutdownEvent.signal();
this->_shutdownEvent.signal();
}
void BlockingAbstractCodec::sendThread(void *param)
void BlockingAbstractCodec::sendThread()
{
Transport::shared_pointer ptr = this->shared_from_this();
BlockingAbstractCodec *bac = static_cast<BlockingAbstractCodec *>(param);
Transport::shared_pointer ptr = bac->shared_from_this();
this->setSenderThread();
bac->setSenderThread();
while (bac->isOpen())
while (this->isOpen())
{
try {
bac->processWrite();
this->processWrite();
} catch (connection_closed_exception &cce) {
// noop
} catch (std::exception &e) {
@@ -1144,18 +1159,7 @@ namespace epics {
__FILE__, __LINE__);
}
}
/*
// wait read thread to die
// TODO rewise if this is really needed
// this timeout is needed where close() is initiated from the send thread,
// and not from the read thread as usualy - recv() does not exit until socket is not destroyed,
// which is done the internalDestroy() call below
bac->_shutdownEvent.wait(3.0);
*/
// call internal destroy
bac->internalDestroy();
_sendQueue.clear();
}
@@ -1197,7 +1201,13 @@ namespace epics {
LOG(logLevelError,
"Error fetching socket remote address: %s.",
errStr);
_socketName = "<unknown>:0";
} else {
char ipAddrStr[64];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
_socketName = ipAddrStr;
}
}
// must be called only once, when there will be no operation on socket (e.g. just before tx/rx thread exists)
@@ -1234,7 +1244,7 @@ namespace epics {
epicsSocketDestroy(_channel);
}
_channel = INVALID_SOCKET;
_channel = INVALID_SOCKET; //TODO: mutex to guard _channel
}
}
@@ -1432,14 +1442,13 @@ namespace epics {
BlockingServerTCPTransportCodec::BlockingServerTCPTransportCodec(
Context::shared_pointer const & context,
SOCKET channel,
std::auto_ptr<ResponseHandler>& responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
int32_t sendBufferSize,
int32_t receiveBufferSize) :
BlockingTCPTransportCodec(true, context, channel, responseHandler,
sendBufferSize, receiveBufferSize, PVA_DEFAULT_PRIORITY),
_lastChannelSID(0), _verifyOrVerified(false), _securityRequired(false)
{
// NOTE: priority not yet known, default priority is used to
//register/unregister
// TODO implement priorities in Reactor... not that user will
@@ -1585,12 +1594,10 @@ namespace epics {
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[64];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(
logLevelDebug,
"Transport to %s still has %zd channel(s) active and closing...",
ipAddrStr, _channels.size());
_socketName.c_str(), _channels.size());
}
std::map<pvAccessID, ServerChannel::shared_pointer>::iterator it = _channels.begin();
@@ -1610,9 +1617,7 @@ namespace epics {
{
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Authentication completed with status '%s' for PVA client: %s.", Status::StatusTypeName[status.getType()], ipAddrStr);
LOG(logLevelDebug, "Authentication completed with status '%s' for PVA client: %s.", Status::StatusTypeName[status.getType()], _socketName.c_str());
}
if (!isVerified()) // TODO sync
@@ -1659,9 +1664,7 @@ namespace epics {
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "No security plug-in installed, selecting default plug-in '%s' for PVA client: %s.", securityPlugin->getId().c_str(), ipAddrStr);
LOG(logLevelDebug, "No security plug-in installed, selecting default plug-in '%s' for PVA client: %s.", securityPlugin->getId().c_str(), _socketName.c_str());
}
}
}
@@ -1685,9 +1688,7 @@ namespace epics {
} catch (SecurityException &se) {
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Security plug-in '%s' failed to create a session for PVA client: %s.", initData->securityPluginName.c_str(), ipAddrStr);
LOG(logLevelDebug, "Security plug-in '%s' failed to create a session for PVA client: %s.", initData->securityPluginName.c_str(), _socketName.c_str());
}
Status status(Status::STATUSTYPE_ERROR, se.what());
verified(status);
@@ -1701,7 +1702,7 @@ namespace epics {
BlockingClientTCPTransportCodec::BlockingClientTCPTransportCodec(
Context::shared_pointer const & context,
SOCKET channel,
std::auto_ptr<ResponseHandler>& responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
int32_t sendBufferSize,
int32_t receiveBufferSize,
TransportClient::shared_pointer const & client,
@@ -1788,9 +1789,7 @@ namespace epics {
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Acquiring transport to %s.", ipAddrStr);
LOG(logLevelDebug, "Acquiring transport to %s.", _socketName.c_str());
}
_owners[client->getID()] = TransportClient::weak_pointer(client);
@@ -1825,12 +1824,10 @@ namespace epics {
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(
logLevelDebug,
"Transport to %s still has %d client(s) active and closing...",
ipAddrStr, refs);
_socketName.c_str(), refs);
}
TransportClientMap_t::iterator it = _owners.begin();
@@ -1854,9 +1851,7 @@ namespace epics {
if (IS_LOGGABLE(logLevelDebug))
{
char ipAddrStr[48];
ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr));
LOG(logLevelDebug, "Releasing TCP transport to %s.", ipAddrStr);
LOG(logLevelDebug, "Releasing TCP transport to %s.", _socketName.c_str());
}
_owners.erase(clientID);
@@ -1864,7 +1859,10 @@ namespace epics {
// not used anymore, close it
// TODO consider delayed destruction (can improve performance!!!)
if(_owners.size()==0) close(); // TODO close(false)
if(_owners.size()==0) {
lock.unlock();
close();
}
}
void BlockingClientTCPTransportCodec::aliveNotification() {

View File

@@ -58,7 +58,7 @@ namespace epics {
virtual ~BlockingTCPConnector();
virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client,
std::auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& address,
ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& address,
epics::pvData::int8 transportRevision, epics::pvData::int16 priority);
private:
/**
@@ -97,22 +97,12 @@ namespace epics {
};
class ResponseHandlerFactory
{
public:
POINTER_DEFINITIONS(ResponseHandlerFactory);
virtual ~ResponseHandlerFactory() {};
virtual std::auto_ptr<ResponseHandler> createResponseHandler() = 0;
};
/**
* Channel Access Server TCP acceptor.
* @author <a href="mailto:matej.sekoranjaATcosylab.com">Matej Sekoranja</a>
* @version $Id: BlockingTCPAcceptor.java,v 1.1 2010/05/03 14:45:42 mrkraimer Exp $
*/
class BlockingTCPAcceptor {
class BlockingTCPAcceptor : public epicsThreadRunable {
public:
POINTER_DEFINITIONS(BlockingTCPAcceptor);
@@ -123,13 +113,14 @@ namespace epics {
* @throws PVAException
*/
BlockingTCPAcceptor(Context::shared_pointer const & context,
ResponseHandlerFactory::shared_pointer const & responseHandlerFactory,
ResponseHandler::shared_pointer const & responseHandler,
int port, int receiveBufferSize);
BlockingTCPAcceptor(Context::shared_pointer const & context,
ResponseHandler::shared_pointer const & responseHandler,
const osiSockAddr& addr, int receiveBufferSize);
virtual ~BlockingTCPAcceptor();
void handleEvents();
/**
* Bind socket address.
* @return bind socket address, <code>null</code> if not binded.
@@ -144,15 +135,17 @@ namespace epics {
void destroy();
private:
virtual void run();
/**
* Context instance.
*/
Context::shared_pointer _context;
/**
* ResponseHandler factory.
* Response handler.
*/
ResponseHandlerFactory::shared_pointer _responseHandlerFactory;
ResponseHandler::shared_pointer _responseHandler;
/**
* Bind server socket address.
@@ -176,21 +169,19 @@ namespace epics {
epics::pvData::Mutex _mutex;
epicsThreadId _threadId;
epicsThread _thread;
/**
* Initialize connection acception.
* @return port where server is listening
*/
int initialize(unsigned short port);
int initialize();
/**
* Validate connection by sending a validation message request.
* @return <code>true</code> on success.
*/
bool validateConnection(Transport::shared_pointer const & transport, const char* address);
static void handleEventsRunner(void* param);
};
}

View File

@@ -40,24 +40,26 @@ namespace epics {
class BlockingUDPTransport : public epics::pvData::NoDefaultMethods,
public Transport,
public TransportSendControl,
public std::tr1::enable_shared_from_this<BlockingUDPTransport>
public std::tr1::enable_shared_from_this<BlockingUDPTransport>,
public epicsThreadRunable
{
public:
POINTER_DEFINITIONS(BlockingUDPTransport);
private:
BlockingUDPTransport(bool serverFlag,
std::auto_ptr<ResponseHandler> &responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
SOCKET channel, osiSockAddr &bindAddress,
short remoteTransportRevision);
public:
static shared_pointer create(bool serverFlag,
std::auto_ptr<ResponseHandler>& responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
SOCKET channel, osiSockAddr& bindAddress,
short remoteTransportRevision)
short remoteTransportRevision) EPICS_DEPRECATED
{
shared_pointer thisPointer(
new BlockingUDPTransport(serverFlag, responseHandler, channel, bindAddress, remoteTransportRevision)
new BlockingUDPTransport(serverFlag, responseHandler,
channel, bindAddress,
remoteTransportRevision)
);
return thisPointer;
}
@@ -68,10 +70,19 @@ namespace epics {
return _closed.get();
}
void setReplyTransport(const Transport::shared_pointer& T)
{
_replyTransport = T;
}
virtual const osiSockAddr* getRemoteAddress() const {
return &_remoteAddress;
}
virtual const std::string& getRemoteName() const {
return _remoteName;
}
virtual std::string getType() const {
return std::string("udp");
}
@@ -183,6 +194,19 @@ namespace epics {
_sendTo = sendTo;
}
virtual void setLocalMulticastAddress(const osiSockAddr& sendTo) {
_localMulticastAddressEnabled = true;
_localMulticastAddress = sendTo;
}
virtual bool hasLocalMulticastAddress() const {
return _localMulticastAddressEnabled;
}
virtual const osiSockAddr& getLocalMulticastAddress() const {
return _localMulticastAddress;
}
virtual void flushSerializeBuffer() {
// noop
}
@@ -219,7 +243,7 @@ namespace epics {
/**
* Set ignore list.
* @param addresses list of ignored addresses.
* @param address list of ignored addresses.
*/
void setIgnoredAddresses(InetAddrVector* addresses) {
if (addresses)
@@ -241,6 +265,32 @@ namespace epics {
return _ignoredAddresses;
}
/**
* Set tapped NIF list.
* @param NIF address list to tap.
*/
void setTappedNIF(InetAddrVector* addresses) {
if (addresses)
{
if (!_tappedNIF) _tappedNIF = new InetAddrVector;
*_tappedNIF = *addresses;
}
else
{
if (_tappedNIF) { delete _tappedNIF; _tappedNIF = 0; }
}
}
/**
* Get list of tapped NIF addresses.
* @return tapped NIF addresses.
*/
InetAddrVector* getTappedNIF() const {
return _tappedNIF;
}
bool send(const char* buffer, size_t length, const osiSockAddr& address);
bool send(epics::pvData::ByteBuffer* buffer, const osiSockAddr& address);
bool send(epics::pvData::ByteBuffer* buffer, InetAddressType target = inetAddressType_all);
@@ -303,13 +353,11 @@ namespace epics {
/**
* Response handler.
*/
std::auto_ptr<ResponseHandler> _responseHandler;
ResponseHandler::shared_pointer _responseHandler;
virtual void processRead();
virtual void run();
private:
static void threadRunner(void* param);
bool processBuffer(Transport::shared_pointer const & transport, osiSockAddr& fromAddress, epics::pvData::ByteBuffer* receiveBuffer);
void close(bool waitForThreadToComplete);
@@ -321,6 +369,13 @@ namespace epics {
*/
SOCKET _channel;
/** When provided, this transport is used for replies (passed to handler)
* instead of *this. This feature is used in the situation where broadcast
* traffic is received on one socket, but a different socket must be used
* for unicast replies.
*/
Transport::shared_pointer _replyTransport;
/**
* Bind address.
*/
@@ -330,6 +385,7 @@ namespace epics {
* Remote address.
*/
osiSockAddr _remoteAddress;
std::string _remoteName;
/**
* Send addresses.
@@ -343,12 +399,23 @@ namespace epics {
*/
InetAddrVector* _ignoredAddresses;
/**
* Tapped NIF addresses.
*/
InetAddrVector* _tappedNIF;
/**
* Send address.
*/
osiSockAddr _sendTo;
bool _sendToEnabled;
/**
* Local multicast address.
*/
osiSockAddr _localMulticastAddress;
bool _localMulticastAddressEnabled;
/**
* Receive buffer.
*/
@@ -374,7 +441,7 @@ namespace epics {
/**
* Thread ID
*/
epicsThreadId _threadId;
std::auto_ptr<epicsThread> _thread;
epics::pvData::int8 _clientServerWithEndianFlag;
@@ -402,7 +469,7 @@ namespace epics {
* NOTE: transport client is ignored for broadcast (UDP).
*/
virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client,
std::auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& bindAddress,
ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& bindAddress,
epics::pvData::int8 transportRevision, epics::pvData::int16 priority);
private:
@@ -424,6 +491,20 @@ namespace epics {
};
typedef std::vector<BlockingUDPTransport::shared_pointer> BlockingUDPTransportVector;
epicsShareExtern void initializeUDPTransports(
bool serverFlag,
BlockingUDPTransportVector& udpTransports,
const IfaceNodeVector& ifaceList,
const ResponseHandler::shared_pointer& responseHandler,
BlockingUDPTransport::shared_pointer& sendTransport,
epics::pvData::int32& listenPort,
bool autoAddressList,
const std::string& addressList,
const std::string& ignoreAddressList);
}
}

View File

@@ -41,11 +41,12 @@ class SearchInstance {
/**
* Search response from server (channel found).
* @param guid server GUID.
* @param minorRevision server minor PVA revision.
* @param serverAddress server address.
*/
// TODO make serverAddress an URI or similar
virtual void searchResponse(int8_t minorRevision, osiSockAddr* serverAddress) = 0;
virtual void searchResponse(const GUID & guid, int8_t minorRevision, osiSockAddr* serverAddress) = 0;
};
class ChannelSearchManager {
@@ -78,12 +79,13 @@ class ChannelSearchManager {
/**
* Search response from server (channel found).
* @param guid server GUID.
* @param cid client channel ID.
* @param seqNo search sequence number.
* @param minorRevision server minor PVA revision.
* @param serverAddress server address.
*/
virtual void searchResponse(pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress) = 0;
virtual void searchResponse(const GUID & guid, pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress) = 0;
/**
* New server detected.

View File

@@ -112,120 +112,6 @@ namespace epics {
#endif
// TODO replace this queue with lock-free implementation
template<typename T>
class queue {
public:
queue(void) { }
//TODO
/*queue(queue const &T) = delete;
queue(queue &&T) = delete;
queue& operator=(const queue &T) = delete;
*/
~queue(void)
{
}
bool empty(void)
{
epics::pvData::Lock lock(_queueMutex);
return _queue.empty();
}
void clean()
{
epics::pvData::Lock lock(_queueMutex);
_queue.clear();
}
void wakeup()
{
if (!_wakeup.getAndSet(true))
{
_queueEvent.signal();
}
}
void put(T const & elem)
{
{
epics::pvData::Lock lock(_queueMutex);
_queue.push_back(elem);
}
_queueEvent.signal();
}
// TODO very sub-optimal (locks and empty() - pop() sequence; at least 2 locks!)
T take(int timeOut)
{
while (true)
{
bool isEmpty = empty();
if (isEmpty)
{
if (timeOut < 0) {
return T();
}
while (isEmpty)
{
if (timeOut == 0) {
_queueEvent.wait();
}
else {
_queueEvent.wait(timeOut);
}
isEmpty = empty();
if (isEmpty)
{
if (timeOut > 0) { // TODO spurious wakeup, but not critical
return T();
}
else // if (timeout == 0) cannot be negative
{
if (_wakeup.getAndSet(false)) {
return T();
}
}
}
}
}
else
{
epics::pvData::Lock lock(_queueMutex);
if (_queue.empty())
return T();
T sender = _queue.front();
_queue.pop_front();
return sender;
}
}
}
size_t size() {
epics::pvData::Lock lock(_queueMutex);
return _queue.size();
}
private:
std::deque<T> _queue;
epics::pvData::Event _queueEvent;
epics::pvData::Mutex _queueMutex;
AtomicValue<bool> _wakeup;
};
class epicsShareClass io_exception: public std::runtime_error {
public:
@@ -307,7 +193,6 @@ namespace epics {
void processWrite();
void processRead();
void processSendQueue();
void clearSendQueue();
void enqueueSendRequest(TransportSender::shared_pointer const & sender);
void enqueueSendRequest(TransportSender::shared_pointer const & sender,
std::size_t requiredBufferSize);
@@ -327,6 +212,10 @@ namespace epics {
char* /*deserializeTo*/,
std::size_t /*elementCount*/, std::size_t /*elementSize*/);
bool sendQueueEmpty() const {
return _sendQueue.empty();
}
protected:
virtual void sendBufferFull(int tries) = 0;
@@ -341,7 +230,6 @@ namespace epics {
int32_t _payloadSize; // TODO why not size_t?
epics::pvData::int32 _remoteTransportSocketReceiveBufferSize;
int64_t _totalBytesSent;
bool _blockingProcessQueue;
//TODO initialize union
osiSockAddr _sendTo;
epicsThreadId _senderThread;
@@ -352,7 +240,7 @@ namespace epics {
std::tr1::shared_ptr<epics::pvData::ByteBuffer> _socketBuffer;
std::tr1::shared_ptr<epics::pvData::ByteBuffer> _sendBuffer;
queue<TransportSender::shared_pointer> _sendQueue;
fair_queue<TransportSender> _sendQueue;
private:
@@ -395,9 +283,8 @@ namespace epics {
bool serverFlag,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & receiveBuffer,
std::tr1::shared_ptr<epics::pvData::ByteBuffer> const & sendBuffer,
int32_t socketSendBufferSize):
AbstractCodec(serverFlag, receiveBuffer, sendBuffer, socketSendBufferSize, true),
_readThread(0), _sendThread(0) { _isOpen.getAndSet(true);}
int32_t socketSendBufferSize);
virtual ~BlockingAbstractCodec();
void readPollOne();
void writePollOne();
@@ -408,8 +295,9 @@ namespace epics {
bool isOpen();
void start();
static void receiveThread(void* param);
static void sendThread(void* param);
private:
void receiveThread();
void sendThread();
protected:
void sendBufferFull(int tries);
@@ -431,8 +319,7 @@ namespace epics {
private:
AtomicValue<bool> _isOpen;
volatile epicsThreadId _readThread;
volatile epicsThreadId _sendThread;
epics::pvData::Thread _readThread, _sendThread;
epics::pvData::Event _shutdownEvent;
};
@@ -461,6 +348,7 @@ namespace epics {
SOCKET _channel;
osiSockAddr _socketAddress;
std::string _socketName;
};
@@ -506,6 +394,9 @@ namespace epics {
return &_socketAddress;
}
const std::string& getRemoteName() const {
return _socketName;
}
epics::pvData::int8 getRevision() const {
return PVA_PROTOCOL_REVISION;
@@ -590,7 +481,7 @@ namespace epics {
bool serverFlag,
Context::shared_pointer const & context,
SOCKET channel,
std::auto_ptr<ResponseHandler>& responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
int32_t sendBufferSize,
int32_t receiveBufferSize,
epics::pvData::int16 priority
@@ -614,7 +505,7 @@ namespace epics {
private:
std::auto_ptr<ResponseHandler> _responseHandler;
ResponseHandler::shared_pointer _responseHandler;
size_t _remoteTransportReceiveBufferSize;
epics::pvData::int8 _remoteTransportRevision;
epics::pvData::int16 _priority;
@@ -638,7 +529,7 @@ namespace epics {
BlockingServerTCPTransportCodec(
Context::shared_pointer const & context,
SOCKET channel,
std::auto_ptr<ResponseHandler>& responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
int32_t sendBufferSize,
int32_t receiveBufferSize );
@@ -646,7 +537,7 @@ namespace epics {
static shared_pointer create(
Context::shared_pointer const & context,
SOCKET channel,
std::auto_ptr<ResponseHandler>& responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
int sendBufferSize,
int receiveBufferSize)
{
@@ -767,7 +658,7 @@ namespace epics {
BlockingClientTCPTransportCodec(
Context::shared_pointer const & context,
SOCKET channel,
std::auto_ptr<ResponseHandler>& responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
int32_t sendBufferSize,
int32_t receiveBufferSize,
TransportClient::shared_pointer const & client,
@@ -779,7 +670,7 @@ namespace epics {
static shared_pointer create(
Context::shared_pointer const & context,
SOCKET channel,
std::auto_ptr<ResponseHandler>& responseHandler,
ResponseHandler::shared_pointer const & responseHandler,
int32_t sendBufferSize,
int32_t receiveBufferSize,
TransportClient::shared_pointer const & client,

View File

@@ -24,6 +24,7 @@
#include <pv/timer.h>
#include <pv/pvData.h>
#include <pv/sharedPtr.h>
#include <pv/fairQueue.h>
#ifdef remoteEpicsExportSharedSymbols
# define epicsExportSharedSymbols
@@ -113,7 +114,8 @@ namespace epics {
CMD_MESSAGE = 18,
CMD_MULTIPLE_DATA = 19,
CMD_RPC = 20,
CMD_CANCEL_REQUEST = 21
CMD_CANCEL_REQUEST = 21,
CMD_ORIGIN_TAG = 22
};
enum ControlCommands {
@@ -142,7 +144,7 @@ namespace epics {
/**
* Interface defining transport sender (instance sending data over transport).
*/
class TransportSender : public Lockable {
class TransportSender : public Lockable, public fair_queue<TransportSender>::entry {
public:
POINTER_DEFINITIONS(TransportSender);
@@ -198,6 +200,8 @@ namespace epics {
*/
virtual const osiSockAddr* getRemoteAddress() const = 0;
virtual const std::string& getRemoteName() const = 0;
// TODO getContext?
/**
@@ -457,7 +461,7 @@ namespace epics {
* @return transport instance.
*/
virtual Transport::shared_pointer connect(TransportClient::shared_pointer const & client,
std::auto_ptr<ResponseHandler>& responseHandler, osiSockAddr& address,
ResponseHandler::shared_pointer const & responseHandler, osiSockAddr& address,
epics::pvData::int8 transportRevision, epics::pvData::int16 priority) = 0;
};

View File

@@ -89,12 +89,13 @@ class SimpleChannelSearchManagerImpl :
void unregisterSearchInstance(SearchInstance::shared_pointer const & channel);
/**
* Search response from server (channel found).
* @param guid server GUID.
* @param cid client channel ID.
* @param seqNo search sequence number.
* @param minorRevision server minor PVA revision.
* @param serverAddress server address.
*/
void searchResponse(pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress);
void searchResponse(const GUID & guid, pvAccessID cid, int32_t seqNo, int8_t minorRevision, osiSockAddr* serverAddress);
/**
* New server detected.
* Boost searching of all channels.

View File

@@ -137,7 +137,7 @@ void SimpleChannelSearchManagerImpl::unregisterSearchInstance(SearchInstance::sh
m_channels.erase(id);
}
void SimpleChannelSearchManagerImpl::searchResponse(pvAccessID cid, int32_t /*seqNo*/, int8_t minorRevision, osiSockAddr* serverAddress)
void SimpleChannelSearchManagerImpl::searchResponse(const GUID & guid, pvAccessID cid, int32_t /*seqNo*/, int8_t minorRevision, osiSockAddr* serverAddress)
{
Lock guard(m_channelMutex);
std::map<pvAccessID,SearchInstance::shared_pointer>::iterator channelsIter = m_channels.find(cid);
@@ -145,10 +145,10 @@ void SimpleChannelSearchManagerImpl::searchResponse(pvAccessID cid, int32_t /*se
{
guard.unlock();
// minor hack to enable duplicate reports
// enable duplicate reports
SearchInstance::shared_pointer si = std::tr1::dynamic_pointer_cast<SearchInstance>(m_context.lock()->getChannel(cid));
if (si)
si->searchResponse(minorRevision, serverAddress);
si->searchResponse(guid, minorRevision, serverAddress);
}
else
{
@@ -160,7 +160,7 @@ void SimpleChannelSearchManagerImpl::searchResponse(pvAccessID cid, int32_t /*se
guard.unlock();
// then notify SearchInstance
si->searchResponse(minorRevision, serverAddress);
si->searchResponse(guid, minorRevision, serverAddress);
}
}

View File

@@ -2873,7 +2873,7 @@ namespace epics {
{
transport->ensureData(4);
pvAccessID cid = payloadBuffer->getInt();
csm->searchResponse(cid, searchSequenceId, version, &serverAddress);
csm->searchResponse(guid, cid, searchSequenceId, version, &serverAddress);
}
@@ -2925,7 +2925,7 @@ namespace epics {
// to do the local multicast
//
// locally broadcast if unicast (qosCode & 0x80 == 0x80)
// locally broadcast if unicast (qosCode & 0x80 == 0x80) via UDP
//
if ((qosCode & 0x80) == 0x80)
{
@@ -2934,12 +2934,20 @@ namespace epics {
if (!context)
return;
BlockingUDPTransport::shared_pointer bt =
//context->getLocalMulticastTransport();
std::tr1::dynamic_pointer_cast<BlockingUDPTransport>(context->getSearchTransport());
if (bt)
BlockingUDPTransport::shared_pointer bt = dynamic_pointer_cast<BlockingUDPTransport>(transport);
if (bt && bt->hasLocalMulticastAddress())
{
// RECEIVE_BUFFER_PRE_RESERVE allows to pre-fix message
size_t newStartPos = (startPosition-PVA_MESSAGE_HEADER_SIZE)-PVA_MESSAGE_HEADER_SIZE-16;
payloadBuffer->setPosition(newStartPos);
// copy part of a header, and add: command, payloadSize, NIF address
payloadBuffer->put(payloadBuffer->getArray(), startPosition-PVA_MESSAGE_HEADER_SIZE, PVA_MESSAGE_HEADER_SIZE-5);
payloadBuffer->putByte(CMD_ORIGIN_TAG);
payloadBuffer->putInt(16);
// encode this socket bind address
encodeAsIPv6Address(payloadBuffer, bt->getBindAddress());
// clear unicast flag
payloadBuffer->put(startPosition+4, (int8)(qosCode & ~0x80));
@@ -2947,9 +2955,12 @@ namespace epics {
payloadBuffer->setPosition(startPosition+8);
encodeAsIPv6Address(payloadBuffer, &responseAddress);
payloadBuffer->setPosition(payloadBuffer->getLimit()); // send will call flip()
// set to end of a message
payloadBuffer->setPosition(payloadBuffer->getLimit());
bt->send(payloadBuffer->getArray()+newStartPos, payloadBuffer->getPosition()-newStartPos,
bt->getLocalMulticastAddress());
bt->send(payloadBuffer, context->getLocalBroadcastAddress());
return;
}
}
@@ -3176,6 +3187,33 @@ namespace epics {
};
class DestroyChannelHandler : public AbstractClientResponseHandler, private epics::pvData::NoDefaultMethods {
public:
DestroyChannelHandler(ClientContextImpl::shared_pointer const & context) :
AbstractClientResponseHandler(context, "Destroy channel")
{
}
virtual ~DestroyChannelHandler() {
}
virtual void handleResponse(osiSockAddr* responseFrom,
Transport::shared_pointer const & transport, int8 version, int8 command,
size_t payloadSize, epics::pvData::ByteBuffer* payloadBuffer)
{
AbstractClientResponseHandler::handleResponse(responseFrom, transport, version, command, payloadSize, payloadBuffer);
transport->ensureData(4);
pvAccessID cid = payloadBuffer->getInt();
/*pvAccessID sid =*/ payloadBuffer->getInt();
// TODO optimize
ChannelImpl::shared_pointer channel = static_pointer_cast<ChannelImpl>(_context.lock()->getChannel(cid));
if (channel.get())
channel->channelDestroyedOnServer();
}
};
/**
* PVA response handler - main handler which dispatches responses to appripriate handlers.
@@ -3211,7 +3249,7 @@ namespace epics {
m_handlerTable[CMD_AUTHNZ].reset(new AuthNZHandler(context.get())); /* 5 */
m_handlerTable[CMD_ACL_CHANGE].reset(new NoopResponse(context, "Access rights change")); /* 6 */
m_handlerTable[CMD_CREATE_CHANNEL].reset(new CreateChannelHandler(context)); /* 7 */
m_handlerTable[CMD_DESTROY_CHANNEL].reset(new NoopResponse(context, "Destroy channel")); /* 8 */ // TODO it might be useful to implement this...
m_handlerTable[CMD_DESTROY_CHANNEL].reset(new DestroyChannelHandler(context)); /* 8 */
m_handlerTable[CMD_CONNECTION_VALIDATED].reset(new ClientConnectionValidatedHandler(context)); /* 9 */
m_handlerTable[CMD_GET] = dataResponse; /* 10 - get response */
m_handlerTable[CMD_PUT] = dataResponse; /* 11 - put response */
@@ -3280,45 +3318,12 @@ namespace epics {
public ClientContextImpl,
public std::tr1::enable_shared_from_this<InternalClientContextImpl>
{
class ChannelProviderImpl;
/*
class ChannelImplFind : public ChannelFind
{
public:
ChannelImplFind(ChannelProvider::shared_pointer const & provider) : m_provider(provider)
{
}
virtual void destroy()
{
// one instance for all, do not delete at all
}
virtual ChannelProvider::shared_pointer getChannelProvider()
{
return m_provider;
};
virtual void cancel()
{
throw std::runtime_error("not supported");
}
private:
// only to be destroyed by it
friend class ChannelProviderImpl;
virtual ~ChannelImplFind() {}
ChannelProvider::shared_pointer m_provider;
};
*/
public:
POINTER_DEFINITIONS(InternalClientContextImpl);
class ChannelProviderImpl : public ChannelProvider {
public:
ChannelProviderImpl(std::tr1::shared_ptr<ClientContextImpl> const & context) :
ChannelProviderImpl(InternalClientContextImpl* context) :
m_context(context)
{
MB_INIT;
@@ -3339,9 +3344,7 @@ namespace epics {
{
// TODO not implemented
std::tr1::shared_ptr<ClientContextImpl> context = m_context.lock();
if (context.get())
context->checkChannelName(channelName);
m_context->checkChannelName(channelName);
if (!channelFindRequester.get())
throw std::runtime_error("null requester");
@@ -3379,15 +3382,6 @@ namespace epics {
short priority,
std::string const & addressesStr)
{
std::tr1::shared_ptr<ClientContextImpl> context = m_context.lock();
if (!context.get())
{
Status errorStatus(Status::STATUSTYPE_ERROR, "context already destroyed");
Channel::shared_pointer nullChannel;
EXCEPTION_GUARD(channelRequester->channelCreated(errorStatus, nullChannel));
return nullChannel;
}
auto_ptr<InetAddrVector> addresses;
if (!addressesStr.empty())
{
@@ -3396,7 +3390,7 @@ namespace epics {
addresses.reset();
}
Channel::shared_pointer channel = context->createChannelInternal(channelName, channelRequester, priority, addresses);
Channel::shared_pointer channel = m_context->createChannelInternal(channelName, channelRequester, priority, addresses);
if (channel.get())
channelRequester->channelCreated(Status::Ok, channel);
return channel;
@@ -3406,37 +3400,31 @@ namespace epics {
virtual void configure(epics::pvData::PVStructure::shared_pointer configuration)
{
std::tr1::shared_ptr<ClientContextImpl> context = m_context.lock();
if (context.get())
context->configure(configuration);
LOG(logLevelError, "Client context must be configured at construction (see ChannelProvider::newInstance(conf))");
}
virtual void flush()
{
std::tr1::shared_ptr<ClientContextImpl> context = m_context.lock();
if (context.get())
context->flush();
m_context->flush();
}
virtual void poll()
{
std::tr1::shared_ptr<ClientContextImpl> context = m_context.lock();
if (context.get())
context->poll();
m_context->poll();
}
~ChannelProviderImpl() {};
private:
std::tr1::weak_ptr<ClientContextImpl> m_context;
InternalClientContextImpl* const m_context;
};
private:
/**
* Implementation of PVAJ JCA <code>Channel</code>.
*/
@@ -3450,7 +3438,7 @@ namespace epics {
/**
* Context.
*/
ClientContextImpl::shared_pointer m_context;
std::tr1::shared_ptr<InternalClientContextImpl> m_context;
/**
* Client channel ID.
@@ -3531,6 +3519,11 @@ namespace epics {
/// Used by SearchInstance.
int32_t m_userValue;
/**
* @brief Server GUID.
*/
GUID m_guid;
/**
* Constructor.
* @param context
@@ -3539,7 +3532,7 @@ namespace epics {
* @throws PVAException
*/
InternalChannelImpl(
ClientContextImpl::shared_pointer const & context,
InternalClientContextImpl::shared_pointer const & context,
pvAccessID channelID,
string const & name,
ChannelRequester::shared_pointer const & requester,
@@ -3573,7 +3566,7 @@ namespace epics {
public:
static ChannelImpl::shared_pointer create(ClientContextImpl::shared_pointer context,
static ChannelImpl::shared_pointer create(InternalClientContextImpl::shared_pointer context,
pvAccessID channelID,
string const & name,
ChannelRequester::shared_pointer requester,
@@ -3613,7 +3606,7 @@ namespace epics {
virtual ChannelProvider::shared_pointer getProvider()
{
return m_context->getProvider();
return ChannelProvider::shared_pointer(m_context->m_provider_ptr);
}
// NOTE: synchronization guarantees that <code>transport</code> is non-<code>0</code> and <code>state == CONNECTED</code>.
@@ -3621,12 +3614,11 @@ namespace epics {
{
Lock guard(m_channelMutex);
if (m_connectionState != CONNECTED) {
static string emptyString;
return emptyString;
return "";
}
else
{
return inetAddressToString(*m_transport->getRemoteAddress());
return m_transport->getRemoteName();
}
}
@@ -3888,6 +3880,9 @@ namespace epics {
* @param remoteDestroy issue channel destroy request.
*/
void disconnect(bool initiateSearch, bool remoteDestroy) {
// order of oldchan and guard is important to ensure
// oldchan is destoryed after unlock
Transport::shared_pointer oldchan;
Lock guard(m_channelMutex);
if (m_connectionState != CONNECTED)
@@ -3911,13 +3906,23 @@ namespace epics {
}
m_transport->release(getID());
m_transport.reset();
oldchan.swap(m_transport);
}
if (initiateSearch)
this->initiateSearch();
}
void channelDestroyedOnServer() {
if (isConnected())
{
disconnect(true, false);
// should be called without any lock hold
reportChannelStateChange();
}
}
#define STATIC_SEARCH_BASE_DELAY_SEC 5
#define STATIC_SEARCH_MAX_MULTIPLIER 10
@@ -3953,21 +3958,23 @@ namespace epics {
m_addressIndex = m_addresses->size()*STATIC_SEARCH_MAX_MULTIPLIER;
// NOTE: calls channelConnectFailed() on failure
searchResponse(PVA_PROTOCOL_REVISION, &((*m_addresses)[ix]));
static GUID guid = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
searchResponse(guid, PVA_PROTOCOL_REVISION, &((*m_addresses)[ix]));
}
virtual void timerStopped() {
// noop
}
virtual void searchResponse(int8 minorRevision, osiSockAddr* serverAddress) {
virtual void searchResponse(const GUID & guid, int8 minorRevision, osiSockAddr* serverAddress) {
Lock guard(m_channelMutex);
Transport::shared_pointer transport = m_transport;
if (transport.get())
{
// TODO use GUID to determine whether there are multiple servers with the same channel
// multiple defined PV or reconnect request (same server address)
if (!sockAddrAreIdentical(transport->getRemoteAddress(), serverAddress))
// GUID check case: same server listening on different NIF
if (!sockAddrAreIdentical(transport->getRemoteAddress(), serverAddress) &&
!std::equal(guid.value, guid.value + 12, m_guid.value))
{
EXCEPTION_GUARD(m_requester->message("More than one channel with name '" + m_name +
"' detected, connected to: " + inetAddressToString(*transport->getRemoteAddress()) + ", ignored: " + inetAddressToString(*serverAddress), warningMessage));
@@ -3982,6 +3989,10 @@ namespace epics {
return;
}
// remember GUID
std::copy(guid.value, guid.value + 12, m_guid.value);
// create channel
createChannel(transport);
}
@@ -4118,7 +4129,7 @@ namespace epics {
if (issueCreateMessage)
{
control->startMessage((int8)7, 2+4);
control->startMessage((int8)CMD_CREATE_CHANNEL, 2+4);
// count
buffer->putShort((int16)1);
@@ -4131,7 +4142,7 @@ namespace epics {
}
else
{
control->startMessage((int8)8, 4+4);
control->startMessage((int8)CMD_DESTROY_CHANNEL, 4+4);
// SID
m_channelMutex.lock();
pvAccessID sid = m_serverChannelID;
@@ -4310,11 +4321,9 @@ namespace epics {
public:
private:
InternalClientContextImpl() :
InternalClientContextImpl(const Configuration::shared_pointer& conf) :
m_addressList(""), m_autoAddressList(true), m_connectionTimeout(30.0f), m_beaconPeriod(15.0f),
m_broadcastPort(PVA_BROADCAST_PORT), m_receiveBufferSize(MAX_TCP_RECV),
m_namedLocker(), m_lastCID(0), m_lastIOID(0),
@@ -4323,40 +4332,18 @@ namespace epics {
EPICS_PVA_MINOR_VERSION,
EPICS_PVA_MAINTENANCE_VERSION,
EPICS_PVA_DEVELOPMENT_FLAG),
m_provider(this),
m_contextState(CONTEXT_NOT_INITIALIZED),
m_configuration(new SystemConfigurationImpl()),
m_configuration(conf),
m_flushStrategy(DELAYED)
{
PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(remoteClientContext);
if(!m_configuration) m_configuration = ConfigurationFactory::getConfiguration("pvAccess-client");
m_flushTransports.reserve(64);
loadConfiguration();
}
public:
static shared_pointer create()
{
// TODO use std::make_shared
std::tr1::shared_ptr<InternalClientContextImpl> tp(new InternalClientContextImpl(), delayed_destroyable_deleter());
shared_pointer thisPointer = tp;
static_cast<InternalClientContextImpl*>(thisPointer.get())->activate();
return thisPointer;
}
void activate()
{
m_provider.reset(new ChannelProviderImpl(shared_from_this()));
}
virtual Configuration::shared_pointer getConfiguration() {
/*
TODO
final ConfigurationProvider configurationProvider = ConfigurationFactory.getProvider();
Configuration config = configurationProvider.getConfiguration("pvAccess-client");
if (!config)
config = configurationProvider.getConfiguration("system");
return config;
*/
return m_configuration;
}
@@ -4364,10 +4351,6 @@ TODO
return m_version;
}
virtual ChannelProvider::shared_pointer const & getProvider() {
return m_provider;
}
virtual Timer::shared_pointer getTimer()
{
return m_timer;
@@ -4456,11 +4439,6 @@ TODO
PVACCESS_REFCOUNT_MONITOR_DESTRUCT(remoteClientContext);
};
virtual const osiSockAddr& getLocalBroadcastAddress() const
{
return m_localBroadcastAddress;
}
private:
void loadConfiguration() {
@@ -4486,6 +4464,8 @@ TODO
m_connector.reset(new BlockingTCPConnector(thisPointer, m_receiveBufferSize, m_connectionTimeout));
m_transportRegistry.reset(new TransportRegistry());
m_responseHandler.reset(new ClientResponseHandler(shared_from_this()));
// preinitialize security plugins
SecurityPluginRegistry::instance();
@@ -4505,103 +4485,24 @@ TODO
*/
bool initializeUDPTransport() {
// quary broadcast addresses of all IFs
// query broadcast addresses of all IFs
SOCKET socket = epicsSocketCreate(AF_INET, SOCK_DGRAM, 0);
if (socket == INVALID_SOCKET) return false;
auto_ptr<InetAddrVector> broadcastAddresses(getBroadcastAddresses(socket, m_broadcastPort));
if (socket == INVALID_SOCKET)
{
LOG(logLevelError, "Failed to create a socket to introspect network interfaces.");
return false;
}
IfaceNodeVector ifaceList;
if (discoverInterfaces(ifaceList, socket, 0) || ifaceList.size() == 0)
{
LOG(logLevelError, "Failed to introspect interfaces or no network interfaces available.");
return false;
}
epicsSocketDestroy (socket);
// set broadcast address list
if (!m_addressList.empty())
{
// if auto is true, add it to specified list
InetAddrVector* appendList = 0;
if (m_autoAddressList)
appendList = broadcastAddresses.get();
auto_ptr<InetAddrVector> list(getSocketAddressList(m_addressList, m_broadcastPort, appendList));
if (list.get() && list->size()) {
// delete old list and take ownership of a new one
broadcastAddresses = list;
}
}
for (size_t i = 0; broadcastAddresses.get() && i < broadcastAddresses->size(); i++)
LOG(logLevelDebug,
"Broadcast address #%d: %s.", i, inetAddressToString((*broadcastAddresses)[i]).c_str());
// where to bind (listen) address
osiSockAddr listenLocalAddress;
listenLocalAddress.ia.sin_family = AF_INET;
listenLocalAddress.ia.sin_port = htons(m_broadcastPort);
listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
ClientContextImpl::shared_pointer thisPointer = shared_from_this();
TransportClient::shared_pointer nullTransportClient;
auto_ptr<ResponseHandler> clientResponseHandler(new ClientResponseHandler(thisPointer));
auto_ptr<BlockingUDPConnector> broadcastConnector(new BlockingUDPConnector(false, true, true));
m_broadcastTransport = static_pointer_cast<BlockingUDPTransport>(broadcastConnector->connect(
nullTransportClient, clientResponseHandler,
listenLocalAddress, PVA_PROTOCOL_REVISION,
PVA_DEFAULT_PRIORITY));
if (!m_broadcastTransport.get())
return false;
m_broadcastTransport->setSendAddresses(broadcastAddresses.get());
// undefined address
osiSockAddr undefinedAddress;
undefinedAddress.ia.sin_family = AF_INET;
undefinedAddress.ia.sin_port = htons(0);
undefinedAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
clientResponseHandler.reset(new ClientResponseHandler(thisPointer));
auto_ptr<BlockingUDPConnector> searchConnector(new BlockingUDPConnector(false, false, true));
m_searchTransport = static_pointer_cast<BlockingUDPTransport>(searchConnector->connect(
nullTransportClient, clientResponseHandler,
undefinedAddress, PVA_PROTOCOL_REVISION,
PVA_DEFAULT_PRIORITY));
if (!m_searchTransport.get())
return false;
m_searchTransport->setSendAddresses(broadcastAddresses.get());
// TODO do not use searchBroadcast in future
// setup local broadcasting
// TODO configurable local NIF, address
osiSockAddr loAddr;
getLoopbackNIF(loAddr, "", 0);
if (true)
{
try
{
//osiSockAddr group;
aToIPAddr("224.0.0.128", m_broadcastPort, &m_localBroadcastAddress.ia);
m_broadcastTransport->join(m_localBroadcastAddress, loAddr);
// NOTE: this disables usage of multicast addresses in EPICS_PVA_ADDR_LIST
m_searchTransport->setMutlicastNIF(loAddr, true);
//InetAddrVector sendAddressList;
//sendAddressList.push_back(group);
//m_searchTransport->setSendAddresses(&sendAddressList);
LOG(logLevelDebug, "Local multicast enabled on %s using network interface %s.",
inetAddressToString(m_localBroadcastAddress).c_str(), inetAddressToString(loAddr, false).c_str());
}
catch (std::exception& ex)
{
LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what());
}
}
else
{
LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled.");
}
// become active
m_broadcastTransport->start();
m_searchTransport->start();
initializeUDPTransports(false, m_udpTransports, ifaceList, m_responseHandler, m_searchTransport,
m_broadcastPort, m_autoAddressList, m_addressList, std::string());
return true;
}
@@ -4616,8 +4517,14 @@ TODO
destroyAllChannels();
// stop UDPs
m_searchTransport->close();
m_broadcastTransport->close();
for (BlockingUDPTransportVector::const_iterator iter = m_udpTransports.begin();
iter != m_udpTransports.end(); iter++)
(*iter)->close();
m_udpTransports.clear();
// stop UDPs
if (m_searchTransport)
m_searchTransport->close();
// wait for all transports to cleanly exit
int tries = 40;
@@ -4841,9 +4748,7 @@ TODO
{
try
{
// TODO we are creating a new response handler even-though we might not need a new transprot !!!
auto_ptr<ResponseHandler> handler(new ClientResponseHandler(shared_from_this()));
Transport::shared_pointer t = m_connector->connect(client, handler, *serverAddress, minorRevision, priority);
Transport::shared_pointer t = m_connector->connect(client, m_responseHandler, *serverAddress, minorRevision, priority);
// TODO !!!
//static_pointer_cast<BlockingTCPTransport>(t)->setFlushStrategy(m_flushStrategy);
return t;
@@ -4924,7 +4829,7 @@ TODO
}
virtual void configure(epics::pvData::PVStructure::shared_pointer configuration)
{
{// remove?
if (m_transportRegistry->numberOfActiveTransports() > 0)
throw std::runtime_error("Configure must be called when there is no transports active.");
@@ -5014,9 +4919,9 @@ TODO
Timer::shared_pointer m_timer;
/**
* Broadcast transport needed to listen for broadcasts.
* UDP transports needed to receive channel searches.
*/
BlockingUDPTransport::shared_pointer m_broadcastTransport;
BlockingUDPTransportVector m_udpTransports;
/**
* UDP transport needed for channel searches.
@@ -5034,6 +4939,11 @@ TODO
*/
TransportRegistry::shared_pointer m_transportRegistry;
/**
* Response handler.
*/
ClientResponseHandler::shared_pointer m_responseHandler;
/**
* Context instance.
*/
@@ -5099,10 +5009,13 @@ TODO
*/
Version m_version;
public:
/**
* Provider implementation.
*/
ChannelProviderImpl::shared_pointer m_provider;
ChannelProviderImpl m_provider;
ChannelProviderImpl::weak_pointer m_provider_ptr;
private:
/**
* Context state.
@@ -5121,14 +5034,39 @@ TODO
TransportRegistry::transportVector_t m_flushTransports;
FlushStrategy m_flushStrategy;
osiSockAddr m_localBroadcastAddress;
};
ClientContextImpl::shared_pointer createClientContextImpl()
namespace {
struct nested {
ClientContextImpl::shared_pointer ptr;
nested(const ClientContextImpl::shared_pointer& p) :ptr(p) {}
void operator()(ChannelProvider*) {
ptr.reset();
}
};
}
ChannelProvider::shared_pointer createClientProvider(const Configuration::shared_pointer& conf)
{
ClientContextImpl::shared_pointer ptr = InternalClientContextImpl::create();
return ptr;
/* For PVA the context and provider have a tight 1-to-1 relationship
* were each must know about the other, and both must be referenced by shared_ptr
* from outside code (provider) and Channels (context and provider.
*
* So we compose the provider within the context and use a nested shared_ptr
* to the context for the provider.
*/
// TODO use make::shared
InternalClientContextImpl::shared_pointer t(new InternalClientContextImpl(conf));
ClientContextImpl::shared_pointer ctxt(t);
InternalClientContextImpl *self = static_cast<InternalClientContextImpl*>(ctxt.get());
// a wrapped shared_ptr to the provider which really holds a reference to the context
ChannelProvider::shared_pointer prov(&self->m_provider, nested(ctxt));
self->m_provider_ptr = prov; // keep a weak_ptr for the context itself so that it can give out refs. to it's provider
ctxt->initialize();
return prov;
}
}};

View File

@@ -48,6 +48,7 @@ namespace epics {
virtual void connectionCompleted(pvAccessID sid/*, rights*/) = 0;
virtual void createChannelFailed() = 0;
virtual std::tr1::shared_ptr<ClientContextImpl> getContext() = 0;
virtual void channelDestroyedOnServer() = 0;
virtual pvAccessID getServerChannelID() = 0;
virtual void registerResponseRequest(ResponseRequest::shared_pointer const & responseRequest) = 0;
@@ -77,13 +78,13 @@ namespace epics {
/**
* Initialize client context. This method is called immediately after instance construction (call of constructor).
*/
virtual void initialize() = 0;
virtual void initialize() = 0; // public?
/**
* Get channel provider implementation.
* @return the channel provider.
*/
virtual ChannelProvider::shared_pointer const & getProvider() = 0;
//virtual ChannelProvider::shared_pointer const & getProvider() = 0;
/**
* Prints detailed information about the context to the standard output stream.
@@ -128,11 +129,9 @@ namespace epics {
virtual void poll() = 0;
virtual void destroy() = 0;
virtual const osiSockAddr& getLocalBroadcastAddress() const = 0;
};
epicsShareExtern ClientContextImpl::shared_pointer createClientContextImpl();
epicsShareExtern ChannelProvider::shared_pointer createClientProvider(const Configuration::shared_pointer& conf);
}
}

View File

@@ -263,7 +263,6 @@ namespace pvAccess {
ChannelSecuritySession::shared_pointer const & _css;
epics::pvData::Status _status;
epics::pvData::Mutex _mutex;
void createChannelFailedResponse(epics::pvData::ByteBuffer* buffer, TransportSendControl* control, const epics::pvData::Status& status);
};
/****************************************************************************************/

View File

@@ -110,16 +110,16 @@ public:
class epicsShareClass ServerContextImpl :
public ServerContext,
public Context,
public ResponseHandlerFactory,
public std::tr1::enable_shared_from_this<ServerContextImpl>
{
public:
typedef std::tr1::shared_ptr<ServerContextImpl> shared_pointer;
typedef std::tr1::shared_ptr<const ServerContextImpl> const_shared_pointer;
protected:
private:
ServerContextImpl();
public:
static shared_pointer create();
static shared_pointer create(const Configuration::shared_pointer& conf);
virtual ~ServerContextImpl();
@@ -142,12 +142,9 @@ public:
TransportRegistry::shared_pointer getTransportRegistry();
std::map<std::string, std::tr1::shared_ptr<SecurityPlugin> >& getSecurityPlugins();
std::auto_ptr<ResponseHandler> createResponseHandler();
virtual void newServerDetected();
BlockingUDPTransport::shared_pointer getLocalMulticastTransport();
epicsTimeStamp& getStartTime();
@@ -233,12 +230,6 @@ public:
*/
epics::pvData::int32 getServerPort();
/**
* Set server port number.
* @param port new server port number.
*/
void setServerPort(epics::pvData::int32 port);
/**
* Get broadcast port.
* @return broadcast port.
@@ -263,13 +254,13 @@ public:
*/
osiSockAddr* getServerInetAddress();
/**
* Broadcast transport.
* @return broadcast transport.
*/
BlockingUDPTransport::shared_pointer getBroadcastTransport();
/**
* Broadcast (UDP send) transport.
* @return broadcast transport.
*/
BlockingUDPTransport::shared_pointer getBroadcastTransport();
/**
/**
* Get channel provider registry implementation used by this instance.
* @return channel provider registry used by this instance.
*/
@@ -317,6 +308,13 @@ private:
*/
std::string _beaconAddressList;
/**
* List of used NIF.
*/
IfaceNodeVector _ifaceList;
osiSockAddr _ifaceAddr;
/**
* A space-separated list of address from which to ignore name resolution requests.
* Each address must be of the form: ip.number:port or host.name:port
@@ -354,14 +352,14 @@ private:
epics::pvData::Timer::shared_pointer _timer;
/**
* Broadcast transport needed for channel searches.
* UDP transports needed to receive channel searches.
*/
BlockingUDPTransport::shared_pointer _broadcastTransport;
BlockingUDPTransportVector _udpTransports;
/**
* Local broadcast transport needed for local fan-out.
* UDP socket used to sending.
*/
BlockingUDPTransport::shared_pointer _localMulticastTransport;
BlockingUDPTransport::shared_pointer _broadcastTransport;
/**
* Beacon emitter.
@@ -379,7 +377,12 @@ private:
*/
TransportRegistry::shared_pointer _transportRegistry;
/**
/**
* Response handler.
*/
ResponseHandler::shared_pointer _responseHandler;
/**
* Channel access.
*/
ChannelProviderRegistry::shared_pointer _channelProviderRegistry;

View File

@@ -258,16 +258,25 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom,
// TODO DoS attack?
const bool responseRequired = (QOS_REPLY_REQUIRED & qosCode) != 0;
// TODO bloom filter or similar server selection (by GUID)
//
// locally broadcast if unicast (qosCode & 0x80 == 0x80)
// locally broadcast if unicast (qosCode & 0x80 == 0x80) via UDP
//
if ((qosCode & 0x80) == 0x80)
{
BlockingUDPTransport::shared_pointer bt = _context->getLocalMulticastTransport();
if (bt)
BlockingUDPTransport::shared_pointer bt = dynamic_pointer_cast<BlockingUDPTransport>(transport);
if (bt && bt->hasLocalMulticastAddress())
{
// RECEIVE_BUFFER_PRE_RESERVE allows to pre-fix message
size_t newStartPos = (startPosition-PVA_MESSAGE_HEADER_SIZE)-PVA_MESSAGE_HEADER_SIZE-16;
payloadBuffer->setPosition(newStartPos);
// copy part of a header, and add: command, payloadSize, NIF address
payloadBuffer->put(payloadBuffer->getArray(), startPosition-PVA_MESSAGE_HEADER_SIZE, PVA_MESSAGE_HEADER_SIZE-5);
payloadBuffer->putByte(CMD_ORIGIN_TAG);
payloadBuffer->putInt(16);
// encode this socket bind address
encodeAsIPv6Address(payloadBuffer, bt->getBindAddress());
// clear unicast flag
payloadBuffer->put(startPosition+4, (int8)(qosCode & ~0x80));
@@ -275,9 +284,12 @@ void ServerSearchHandler::handleResponse(osiSockAddr* responseFrom,
payloadBuffer->setPosition(startPosition+8);
encodeAsIPv6Address(payloadBuffer, &responseAddress);
payloadBuffer->setPosition(payloadBuffer->getLimit()); // send will call flip()
// set to end of a message
payloadBuffer->setPosition(payloadBuffer->getLimit());
bt->send(payloadBuffer->getArray()+newStartPos, payloadBuffer->getPosition()-newStartPos,
bt->getLocalMulticastAddress());
bt->send(payloadBuffer);
return;
}
}
@@ -760,7 +772,7 @@ ChannelRequester::shared_pointer ServerChannelRequesterImpl::create(
std::tr1::shared_ptr<ServerChannelRequesterImpl> tp(new ServerChannelRequesterImpl(transport, channelName, cid, css));
ChannelRequester::shared_pointer cr = tp;
// TODO exception guard and report error back
provider->createChannel(channelName, cr, transport->getPriority());
provider->createChannel(channelName, cr, transport->getPriority());
return cr;
}
@@ -817,7 +829,7 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
LOG(logLevelDebug, "Exception caught when creating channel: %s", _channelName.c_str());
{
Lock guard(_mutex);
_status = Status(Status::STATUSTYPE_FATAL, "failed to create channel", e.what());
_status = Status(Status::STATUSTYPE_FATAL, "failed to create channel", e.what());
}
TransportSender::shared_pointer thisSender = shared_from_this();
transport->enqueueSendRequest(thisSender);
@@ -830,7 +842,7 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
LOG(logLevelDebug, "Exception caught when creating channel: %s", _channelName.c_str());
{
Lock guard(_mutex);
_status = Status(Status::STATUSTYPE_FATAL, "failed to create channel");
_status = Status(Status::STATUSTYPE_FATAL, "failed to create channel");
}
TransportSender::shared_pointer thisSender = shared_from_this();
transport->enqueueSendRequest(thisSender);
@@ -841,16 +853,45 @@ void ServerChannelRequesterImpl::channelCreated(const Status& status, Channel::s
}
}
void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer const & /*channel*/, const Channel::ConnectionState /*isConnected*/)
void ServerChannelRequesterImpl::channelStateChange(Channel::shared_pointer const & /*channel*/, const Channel::ConnectionState isConnected)
{
// TODO should we notify remote side?
if(isConnected==Channel::CONNECTED || isConnected==Channel::NEVER_CONNECTED)
return;
if(Transport::shared_pointer transport = _transport.lock())
{
ChannelHostingTransport::shared_pointer casTransport = dynamic_pointer_cast<ChannelHostingTransport>(transport);
if (!casTransport)
return;
ServerChannelImpl::shared_pointer channel;
{
Lock guard(_mutex);
channel= dynamic_pointer_cast<ServerChannelImpl>(_serverChannel.lock());
}
if (!channel)
return;
// destroy
channel->destroy();
// .. and unregister
casTransport->unregisterChannel(channel->getSID());
// send response back
TransportSender::shared_pointer sr(new ServerDestroyChannelHandlerTransportSender(channel->getCID(), channel->getSID()));
transport->enqueueSendRequest(sr);
}
}
string ServerChannelRequesterImpl::getRequesterName()
{
std::stringstream name;
name << "ServerChannelRequesterImpl/" << _channelName << "[" << _cid << "]";
return name.str();
Transport::shared_pointer transport = _transport.lock();
if (transport)
return transport->getRemoteName();
else
return "<unknown>:0";
}
void ServerChannelRequesterImpl::message(std::string const & message, MessageType messageType)
@@ -878,32 +919,29 @@ void ServerChannelRequesterImpl::send(ByteBuffer* buffer, TransportSendControl*
status = _status;
}
// error response
if (serverChannel.get() == NULL)
{
createChannelFailedResponse(buffer, control, status);
}
// OK
else if (Transport::shared_pointer transport = _transport.lock())
{
ServerChannelImpl::shared_pointer serverChannelImpl = dynamic_pointer_cast<ServerChannelImpl>(serverChannel);
control->startMessage((int8)CMD_CREATE_CHANNEL, 2*sizeof(int32)/sizeof(int8));
buffer->putInt(serverChannelImpl->getCID());
buffer->putInt(serverChannelImpl->getSID());
status.serialize(buffer, control);
}
}
void ServerChannelRequesterImpl::createChannelFailedResponse(ByteBuffer* buffer, TransportSendControl* control, const Status& status)
{
if (Transport::shared_pointer transport = _transport.lock())
{
control->startMessage((int8)CMD_CREATE_CHANNEL, 2*sizeof(int32)/sizeof(int8));
buffer->putInt(_cid);
buffer->putInt(-1);
status.serialize(buffer, control);
}
if (Transport::shared_pointer transport = _transport.lock())
{
// error response
if (!serverChannel)
{
control->startMessage((int8)CMD_CREATE_CHANNEL, 2*sizeof(int32)/sizeof(int8));
buffer->putInt(_cid);
buffer->putInt(-1);
// error status is expected or channel has been destroyed locally
if (status.isSuccess())
status = Status(Status::STATUSTYPE_ERROR, "channel has been destroyed");
status.serialize(buffer, control);
}
// OK
else
{
ServerChannelImpl::shared_pointer serverChannelImpl = dynamic_pointer_cast<ServerChannelImpl>(serverChannel);
control->startMessage((int8)CMD_CREATE_CHANNEL, 2*sizeof(int32)/sizeof(int8));
buffer->putInt(serverChannelImpl->getCID());
buffer->putInt(serverChannelImpl->getSID());
status.serialize(buffer, control);
}
}
}
/****************************************************************************************/
@@ -919,7 +957,7 @@ void ServerDestroyChannelHandler::handleResponse(osiSockAddr* responseFrom,
ChannelHostingTransport::shared_pointer casTransport = dynamic_pointer_cast<ChannelHostingTransport>(transport);
transport->ensureData(2*sizeof(int32)/sizeof(int8));
transport->ensureData(8);
const pvAccessID sid = payloadBuffer->getInt();
const pvAccessID cid = payloadBuffer->getInt();
@@ -1989,7 +2027,7 @@ void ServerMonitorRequesterImpl::monitorConnect(const Status& status, Monitor::s
{
Lock guard(_mutex);
_status = status;
_channelMonitor = monitor;
_channelMonitor = monitor; //TODO inconsistent locking for _channelMonitor
_structure = structure;
}
TransportSender::shared_pointer thisSender = shared_from_this();

View File

@@ -33,7 +33,6 @@ ServerContextImpl::ServerContextImpl():
_serverPort(PVA_SERVER_PORT),
_receiveBufferSize(MAX_TCP_RECV),
_timer(),
_broadcastTransport(),
_beaconEmitter(),
_acceptor(),
_transportRegistry(),
@@ -51,13 +50,22 @@ ServerContextImpl::ServerContextImpl():
epicsSignalInstallSigPipeIgnore ();
generateGUID();
initializeLogger();
loadConfiguration();
initializeLogger();
}
ServerContextImpl::shared_pointer ServerContextImpl::create()
{
ServerContextImpl::shared_pointer thisPointer(new ServerContextImpl());
thisPointer->loadConfiguration();
return thisPointer;
}
ServerContextImpl::shared_pointer ServerContextImpl::create(
const Configuration::shared_pointer& conf)
{
ServerContextImpl::shared_pointer thisPointer(new ServerContextImpl());
thisPointer->configuration = conf;
thisPointer->loadConfiguration();
return thisPointer;
}
@@ -102,11 +110,6 @@ void ServerContextImpl::initializeLogger()
//createFileLogger("serverContextImpl.log");
}
struct noop_deleter
{
template<class T> void operator()(T * p) {}
};
Configuration::shared_pointer ServerContextImpl::getConfiguration()
{
Lock guard(_mutex);
@@ -134,6 +137,12 @@ void ServerContextImpl::loadConfiguration()
if (debugLevel > 0)
SET_LOG_LEVEL(logLevelDebug);
// TODO multiple addresses
_ifaceAddr.ia.sin_family = AF_INET;
_ifaceAddr.ia.sin_addr.s_addr = htonl(INADDR_ANY);
_ifaceAddr.ia.sin_port = 0;
config->getPropertyAsAddress("EPICS_PVAS_INTF_ADDR_LIST", &_ifaceAddr);
_beaconAddressList = config->getPropertyAsString("EPICS_PVA_ADDR_LIST", _beaconAddressList);
_beaconAddressList = config->getPropertyAsString("EPICS_PVAS_BEACON_ADDR_LIST", _beaconAddressList);
@@ -145,6 +154,7 @@ void ServerContextImpl::loadConfiguration()
_serverPort = config->getPropertyAsInteger("EPICS_PVA_SERVER_PORT", _serverPort);
_serverPort = config->getPropertyAsInteger("EPICS_PVAS_SERVER_PORT", _serverPort);
_ifaceAddr.ia.sin_port = htons(_serverPort);
_broadcastPort = config->getPropertyAsInteger("EPICS_PVA_BROADCAST_PORT", _broadcastPort);
_broadcastPort = config->getPropertyAsInteger("EPICS_PVAS_BROADCAST_PORT", _broadcastPort);
@@ -154,6 +164,25 @@ void ServerContextImpl::loadConfiguration()
_channelProviderNames = config->getPropertyAsString("EPICS_PVA_PROVIDER_NAMES", _channelProviderNames);
_channelProviderNames = config->getPropertyAsString("EPICS_PVAS_PROVIDER_NAMES", _channelProviderNames);
//
// introspect network interfaces
//
SOCKET sock = epicsSocketCreate(AF_INET, SOCK_STREAM, 0);
if (!sock) {
THROW_BASE_EXCEPTION("Failed to create a socket needed to introspect network interfaces.");
}
if (discoverInterfaces(_ifaceList, sock, &_ifaceAddr))
{
THROW_BASE_EXCEPTION("Failed to introspect network interfaces.");
}
else if (_ifaceList.size() == 0)
{
THROW_BASE_EXCEPTION("No (specified) network interface(s) available.");
}
epicsSocketDestroy(sock);
}
bool ServerContextImpl::isChannelProviderNamePreconfigured()
@@ -167,7 +196,7 @@ void ServerContextImpl::initialize(ChannelProviderRegistry::shared_pointer const
Lock guard(_mutex);
if (!channelProviderRegistry.get())
{
THROW_BASE_EXCEPTION("non null channelProviderRegistry expected");
THROW_BASE_EXCEPTION("channelProviderRegistry == NULL");
}
if (_state == DESTROYED)
@@ -227,12 +256,6 @@ void ServerContextImpl::initialize(ChannelProviderRegistry::shared_pointer const
_state = INITIALIZED;
}
std::auto_ptr<ResponseHandler> ServerContextImpl::createResponseHandler()
{
ServerContextImpl::shared_pointer thisContext = shared_from_this();
return std::auto_ptr<ResponseHandler>(new ServerResponseHandler(thisContext));
}
void ServerContextImpl::internalInitialize()
{
osiSockAttach();
@@ -241,128 +264,22 @@ void ServerContextImpl::internalInitialize()
_transportRegistry.reset(new TransportRegistry());
ServerContextImpl::shared_pointer thisServerContext = shared_from_this();
_responseHandler.reset(new ServerResponseHandler(thisServerContext));
_acceptor.reset(new BlockingTCPAcceptor(thisServerContext, thisServerContext, _serverPort, _receiveBufferSize));
_acceptor.reset(new BlockingTCPAcceptor(thisServerContext, _responseHandler, _ifaceAddr, _receiveBufferSize));
_serverPort = ntohs(_acceptor->getBindAddress()->ia.sin_port);
// setup broadcast UDP transport
initializeBroadcastTransport();
// TODO introduce a constant
// TODO introduce "tcp" a constant
_beaconEmitter.reset(new BeaconEmitter("tcp", _broadcastTransport, thisServerContext));
}
void ServerContextImpl::initializeBroadcastTransport()
{
// setup UDP transport
try
{
// where to bind (listen) address
osiSockAddr listenLocalAddress;
listenLocalAddress.ia.sin_family = AF_INET;
listenLocalAddress.ia.sin_port = htons(_broadcastPort);
listenLocalAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
// where to send addresses
SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket == INVALID_SOCKET)
{
THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport");
}
auto_ptr<InetAddrVector> broadcastAddresses(getBroadcastAddresses(socket,_broadcastPort));
epicsSocketDestroy(socket);
TransportClient::shared_pointer nullTransportClient;
auto_ptr<BlockingUDPConnector> broadcastConnector(new BlockingUDPConnector(true, true, true));
auto_ptr<epics::pvAccess::ResponseHandler> responseHandler = createResponseHandler();
_broadcastTransport = static_pointer_cast<BlockingUDPTransport>(broadcastConnector->connect(
nullTransportClient, responseHandler,
listenLocalAddress, PVA_PROTOCOL_REVISION,
PVA_DEFAULT_PRIORITY));
_broadcastTransport->setSendAddresses(broadcastAddresses.get());
// set ignore address list
if (!_ignoreAddressList.empty())
{
// we do not care about the port
auto_ptr<InetAddrVector> list(getSocketAddressList(_ignoreAddressList, 0, NULL));
if (list.get() != NULL && list->size() > 0)
{
_broadcastTransport->setIgnoredAddresses(list.get());
}
}
// set broadcast address list
if (!_beaconAddressList.empty())
{
// if auto is true, add it to specified list
InetAddrVector* appendList = NULL;
if (_autoBeaconAddressList == true)
{
appendList = _broadcastTransport->getSendAddresses();
}
auto_ptr<InetAddrVector> list(getSocketAddressList(_beaconAddressList, _broadcastPort, appendList));
if (list.get() != NULL && list->size() > 0)
{
_broadcastTransport->setSendAddresses(list.get());
}
}
// setup local broadcasting
// TODO configurable local NIF, address
osiSockAddr loAddr;
getLoopbackNIF(loAddr, "", 0);
if (true)
{
try
{
osiSockAddr group;
aToIPAddr("224.0.0.128", _broadcastPort, &group.ia);
_broadcastTransport->join(group, loAddr);
osiSockAddr anyAddress;
anyAddress.ia.sin_family = AF_INET;
anyAddress.ia.sin_port = htons(0);
anyAddress.ia.sin_addr.s_addr = htonl(INADDR_ANY);
// NOTE: localMulticastTransport is not started (no read is called on a socket)
auto_ptr<epics::pvAccess::ResponseHandler> responseHandler2 = createResponseHandler();
_localMulticastTransport = static_pointer_cast<BlockingUDPTransport>(broadcastConnector->connect(
nullTransportClient, responseHandler2,
anyAddress, PVA_PROTOCOL_REVISION,
PVA_DEFAULT_PRIORITY));
_localMulticastTransport->setMutlicastNIF(loAddr, true);
InetAddrVector sendAddressList;
sendAddressList.push_back(group);
_localMulticastTransport->setSendAddresses(&sendAddressList);
LOG(logLevelDebug, "Local multicast enabled on %s using network interface %s.",
inetAddressToString(group).c_str(), inetAddressToString(loAddr, false).c_str());
}
catch (std::exception& ex)
{
LOG(logLevelDebug, "Failed to initialize local multicast, funcionality disabled. Reason: %s.", ex.what());
}
}
else
{
LOG(logLevelDebug, "Failed to detect a loopback network interface, local multicast disabled.");
}
_broadcastTransport->start();
if (_localMulticastTransport)
_localMulticastTransport->start();
}
catch (std::exception& e)
{
THROW_BASE_EXCEPTION_CAUSE("Failed to initialize broadcast UDP transport", e);
}
catch (...)
{
THROW_BASE_EXCEPTION("Failed to initialize broadcast UDP transport");
}
initializeUDPTransports(true, _udpTransports, _ifaceList, _responseHandler, _broadcastTransport,
_broadcastPort, _autoBeaconAddressList, _beaconAddressList, _ignoreAddressList);
}
void ServerContextImpl::run(int32 seconds)
@@ -396,7 +313,7 @@ void ServerContextImpl::run(int32 seconds)
}
// run...
_beaconEmitter->start();
_beaconEmitter->start();
//TODO review this
if(seconds == 0)
@@ -451,32 +368,32 @@ void ServerContextImpl::destroy()
void ServerContextImpl::internalDestroy()
{
// stop responding to search requests
if (_broadcastTransport.get())
{
_broadcastTransport->close();
_broadcastTransport.reset();
}
// and close local multicast transport
if (_localMulticastTransport.get())
for (BlockingUDPTransportVector::const_iterator iter = _udpTransports.begin();
iter != _udpTransports.end(); iter++)
(*iter)->close();
_udpTransports.clear();
// stop emitting beacons
if (_beaconEmitter)
{
_localMulticastTransport->close();
_localMulticastTransport.reset();
_beaconEmitter->destroy();
_beaconEmitter.reset();
}
// close UDP sent transport
if (_broadcastTransport)
{
_broadcastTransport->close();
_broadcastTransport.reset();
}
// stop accepting connections
if (_acceptor.get())
if (_acceptor)
{
_acceptor->destroy();
_acceptor.reset();
}
// stop emitting beacons
if (_beaconEmitter.get())
{
_beaconEmitter->destroy();
_beaconEmitter.reset();
}
// this will also destroy all channels
destroyAllTransports();
}
@@ -541,7 +458,8 @@ void ServerContextImpl::printInfo(ostream& str)
<< "SERVER_PORT : " << _serverPort << endl \
<< "RCV_BUFFER_SIZE : " << _receiveBufferSize << endl \
<< "IGNORE_ADDR_LIST: " << _ignoreAddressList << endl \
<< "STATE : " << ServerContextImpl::StateNames[_state] << endl;
<< "INTF_ADDR_LIST : " << inetAddressToString(_ifaceAddr, false) << endl \
<< "STATE : " << ServerContextImpl::StateNames[_state] << endl;
}
void ServerContextImpl::dispose()
@@ -550,6 +468,10 @@ void ServerContextImpl::dispose()
{
destroy();
}
catch(std::exception& e)
{
std::cerr<<"Error in: ServerContextImpl::dispose: "<<e.what()<<"\n";
}
catch(...)
{
std::cerr<<"Oh no, something when wrong in ServerContextImpl::dispose!\n";
@@ -598,11 +520,6 @@ int32 ServerContextImpl::getServerPort()
return _serverPort;
}
void ServerContextImpl::setServerPort(int32 port)
{
_serverPort = port;
}
int32 ServerContextImpl::getBroadcastPort()
{
return _broadcastPort;
@@ -629,12 +546,7 @@ osiSockAddr* ServerContextImpl::getServerInetAddress()
BlockingUDPTransport::shared_pointer ServerContextImpl::getBroadcastTransport()
{
return _broadcastTransport;
}
BlockingUDPTransport::shared_pointer ServerContextImpl::getLocalMulticastTransport()
{
return _localMulticastTransport;
return _broadcastTransport;
}
ChannelProviderRegistry::shared_pointer ServerContextImpl::getChannelProviderRegistry()

View File

@@ -11,6 +11,7 @@ INC += pv/referenceCountingLock.h
INC += pv/configuration.h
INC += pv/likely.h
INC += pv/wildcard.h
INC += pv/fairQueue.h
LIBSRCS += hexDump.cpp
LIBSRCS += inetAddressUtil.cpp

View File

@@ -7,8 +7,10 @@
#include <algorithm>
#include <pv/epicsException.h>
#include <pv/typeCast.h>
#include <osiSock.h>
#include <epicsStdlib.h>
#define epicsExportSharedSymbols
#include <pv/configuration.h>
@@ -24,211 +26,109 @@ namespace pvAccess {
using namespace epics::pvData;
using namespace std;
Properties::Properties()
{
_fileName = "";
_infile.reset(new ifstream());
_infile->exceptions (ifstream::failbit | ifstream::badbit );
_outfile.reset(new ofstream());
_outfile->exceptions (ofstream::failbit | ofstream::badbit );
}
Properties::Properties() {}
Properties::Properties(const string &fileName)
{
_fileName = fileName;
_infile.reset(new ifstream());
_infile->exceptions (ifstream::failbit | ifstream::badbit );
_outfile.reset(new ofstream());
_outfile->exceptions (ofstream::failbit | ofstream::badbit );
}
Properties::Properties(const string &fileName) : _fileName(fileName) {}
Properties::~Properties()
const std::string &Properties::getProperty(const string &key) const
{
}
void Properties::setProperty(const string &key, const string &value)
{
string oldValue;
std::map<std::string,std::string>::iterator propertiesIterator = _properties.find(key);
if(propertiesIterator != _properties.end()) //found in map
{
_properties.erase(propertiesIterator);
}
_properties[key] = value;
}
string Properties::getProperty(const string &key)
{
std::map<std::string,std::string>::iterator propertiesIterator = _properties.find(key);
if(propertiesIterator != _properties.end()) //found in map
{
return string(propertiesIterator->second);
}
else
{
string errMsg = "Property not found in the map: " + key;
THROW_BASE_EXCEPTION(errMsg.c_str());
_properties_t::const_iterator propertiesIterator = _properties.find(key);
if(propertiesIterator != _properties.end()) {
return propertiesIterator->second;
} else {
THROW_BASE_EXCEPTION(string("Property not found in the map: ") + key);
}
}
string Properties::getProperty(const string &key, const string &defaultValue)
const std::string &Properties::getProperty(const string &key, const string &defaultValue) const
{
std::map<std::string,std::string>::iterator propertiesIterator = _properties.find(key);
if(propertiesIterator != _properties.end()) //found in map
{
return string(propertiesIterator->second);
}
return defaultValue;
_properties_t::const_iterator propertiesIterator = _properties.find(key);
if(propertiesIterator != _properties.end()) {
return propertiesIterator->second;
} else {
return defaultValue;
}
}
bool Properties::hasProperty(const string &key)
void Properties::Properties::load()
{
return (_properties.find(key) != _properties.end());
}
void Properties::load()
{
_properties.clear();
#ifdef NO_STREAM_EXCEPTIONS
_infile->open(_fileName.c_str(),ifstream::in);
if (_infile->fail())
#else
try
{
_infile->open(_fileName.c_str(),ifstream::in);
}
catch (ifstream::failure& e)
#endif
{
string errMsg = "Error opening file: " + string(_fileName.c_str());
THROW_BASE_EXCEPTION(errMsg.c_str());
}
string line;
string property;
string key;
#ifndef NO_STREAM_EXCEPTIONS
try
{
#endif
while(!_infile->eof())
{
line.erase();
std::getline(*_infile,line);
#ifdef NO_STREAM_EXCEPTIONS
if (_infile->fail())
{
_infile->close();
if(_infile->eof())
{
return; //end of file
}
string errMsg = "Error reading file: " + _fileName;
THROW_BASE_EXCEPTION(errMsg.c_str());
}
#endif
//remove trailing spaces
truncate(line);
//empty line
if(line.length() == 0)
{
continue;
}
// comment
if(line[0] == '#')
{
continue;
}
//line is in format: propertyName=propertyValue
size_t pos = line.find_first_of('=',0);
if(pos == string::npos) //bad value (= not found)
{
string errMsg = "Bad property line found: " + line;
THROW_BASE_EXCEPTION(errMsg.c_str());
}
key = line.substr(0,pos);
truncate(key);
property = line.substr(pos + 1,line.length());
truncate(property);
_properties[key] = property;
}
#ifndef NO_STREAM_EXCEPTIONS
}
catch (ifstream::failure& e)
{
_infile->close();
if(_infile->eof())
{
return; //end of file
}
string errMsg = "Error reading file: " + _fileName;
THROW_BASE_EXCEPTION(errMsg.c_str());
}
#endif
_infile->close();
load(_fileName);
}
void Properties::load(const string &fileName)
{
_fileName = fileName;
load();
ifstream strm(fileName.c_str());
load(strm);
}
void Properties::store()
namespace {
string trim(const string& in)
{
#ifdef NO_STREAM_EXCEPTIONS
_outfile->open(_fileName.c_str(),ifstream::trunc);
if (_outfile->fail())
#else
try
{
_outfile->open(_fileName.c_str(),ifstream::trunc);
}
catch (ofstream::failure& e)
#endif
{
string errMsg = "Error opening file: " + string(_fileName.c_str());
THROW_BASE_EXCEPTION(errMsg.c_str());
}
for (std::map<std::string,std::string>::iterator propertiesIterator = _properties.begin();
propertiesIterator != _properties.end();
propertiesIterator++ )
{
#ifndef NO_STREAM_EXCEPTIONS
try
{
#endif
string line = string(propertiesIterator->first) + string("=") + string(propertiesIterator->second) + string("\n");
_outfile->write(line.c_str(),line.length());
#ifdef NO_STREAM_EXCEPTIONS
if(_outfile->fail())
#else
}
catch (ofstream::failure& e)
#endif
{
_outfile->close();
string errMsg = "Error writing to file: " + string(_fileName.c_str());
THROW_BASE_EXCEPTION(errMsg.c_str());
}
}
_outfile->close();
size_t A = in.find_first_not_of(" \t\r"),
B = in.find_last_not_of(" \t\r");
if(A==B)
return string();
else
return in.substr(A, B-A+1);
}
}
void Properties::store(const string &fileName)
void Properties::load(std::istream& strm)
{
_fileName = fileName;
store();
_properties_t newmap;
std::string line;
unsigned lineno = 0;
while(getline(strm, line).good()) {
lineno++;
size_t idx = line.find_first_not_of(" \t\r");
if(idx==line.npos || line[idx]=='#')
continue;
idx = line.find_first_of('=');
if(idx==line.npos) {
ostringstream msg;
msg<<"Malformed line "<<lineno<<" expected '='";
throw runtime_error(msg.str());
}
string key(trim(line.substr(0, idx))),
value(trim(line.substr(idx+1)));
if(key.empty()) {
ostringstream msg;
msg<<"Malformed line "<<lineno<<" expected name before '='";
throw runtime_error(msg.str());
}
newmap[key] = value;
}
if(strm.bad()) {
ostringstream msg;
msg<<"Malformed line "<<lineno<<" I/O error";
throw runtime_error(msg.str());
}
_properties.swap(newmap);
}
void Properties::store() const
{
store(_fileName);
}
void Properties::store(const std::string& fname) const
{
ofstream strm(fname.c_str());
store(strm);
}
void Properties::store(std::ostream& strm) const
{
for(_properties_t::const_iterator it=_properties.begin(), end=_properties.end();
it!=end && strm.good(); ++it)
{
strm << it->first << " = " << it->second << "\n";
}
}
void Properties::list()
@@ -241,105 +141,60 @@ void Properties::list()
}
}
SystemConfigurationImpl::SystemConfigurationImpl() :
_properties(new Properties())
bool Configuration::getPropertyAsBoolean(const std::string &name, const bool defaultValue) const
{
// no exception, default value is taken
//_ibuffer.exceptions ( ifstream::failbit | ifstream::badbit );
//_obuffer.exceptions ( ifstream::failbit | ifstream::badbit );
}
string value = getPropertyAsString(name, defaultValue ? "1" : "0");
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
SystemConfigurationImpl::~SystemConfigurationImpl()
{
}
bool SystemConfigurationImpl::getPropertyAsBoolean(const string &name, const bool defaultValue)
{
/*
bool retval;
_ibuffer.clear();
_obuffer.clear();
_obuffer.str("");
_obuffer << defaultValue;
_ibuffer.str(getPropertyAsString(name,_obuffer.str()));
_ibuffer >> retval;
if (_ibuffer.fail() || _ibuffer.bad())
return defaultValue;
else
return retval;
*/
string value = getPropertyAsString(name, defaultValue ? "1" : "0");
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
bool isTrue = (value == "1") || (value == "true") || (value == "yes");
bool isTrue = (value == "1") || (value == "true") || (value == "yes");
if (isTrue)
return true;
bool isFalse = (value == "0") || (value == "false") || (value == "no");
bool isFalse = (value == "0") || (value == "false") || (value == "no");
if (isFalse)
return false;
// invalid value
// invalid value
return defaultValue;
}
int32 SystemConfigurationImpl::getPropertyAsInteger(const string &name, const int32 defaultValue)
epics::pvData::int32 Configuration::getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue) const
{
int32 retval = defaultValue;
_ibuffer.clear();
_obuffer.clear();
_obuffer.str("");
_obuffer << defaultValue;
_ibuffer.str(getPropertyAsString(name, _obuffer.str()));
_ibuffer >> retval;
if (_ibuffer.fail() || _ibuffer.bad())
try{
return castUnsafe<epics::pvData::int32>(getPropertyAsString(name, ""));
}catch(std::runtime_error&){
return defaultValue;
else
return retval;
}
}
float SystemConfigurationImpl::getPropertyAsFloat(const string &name, const float defaultValue)
float Configuration::getPropertyAsFloat(const std::string &name, const float defaultValue) const
{
float retval;
_ibuffer.clear();
_obuffer.clear();
_obuffer.str("");
_obuffer << defaultValue;
_ibuffer.str(getPropertyAsString(name, _obuffer.str()));
_ibuffer >> retval;
if (_ibuffer.fail() || _ibuffer.bad())
try{
return castUnsafe<float>(getPropertyAsString(name, ""));
}catch(std::runtime_error&){
return defaultValue;
else
return retval;
}
}
float SystemConfigurationImpl::getPropertyAsDouble(const string &name, const double defaultValue)
double Configuration::getPropertyAsDouble(const std::string &name, const double defaultValue) const
{
float retval;
_ibuffer.clear();
_obuffer.clear();
_obuffer.str("");
_obuffer << defaultValue;
_ibuffer.str(getPropertyAsString(name, _obuffer.str()));
_ibuffer >> retval;
if (_ibuffer.fail() || _ibuffer.bad())
try {
return castUnsafe<double>(getPropertyAsString(name, ""));
}catch(std::runtime_error&){
return defaultValue;
else
return retval;
}
}
string SystemConfigurationImpl::getPropertyAsString(const string &name, const string &defaultValue)
std::string Configuration::getPropertyAsString(const std::string &name, const std::string &defaultValue) const
{
const char* val = getenv(name.c_str());
if(val != NULL)
{
return _properties->getProperty(name, string(val));
}
return _properties->getProperty(name, defaultValue);
std::string val;
if(tryGetPropertyAsString(name, &val))
return val;
else
return defaultValue;
}
bool SystemConfigurationImpl::getPropertyAsAddress(const std::string& name, osiSockAddr* addr)
bool Configuration::getPropertyAsAddress(const std::string& name, osiSockAddr* addr) const
{
unsigned short dftport=0;
if(addr->sa.sa_family==AF_INET)
@@ -355,19 +210,87 @@ bool SystemConfigurationImpl::getPropertyAsAddress(const std::string& name, osiS
return true;
}
bool SystemConfigurationImpl::hasProperty(const string &key)
bool Configuration::hasProperty(const std::string &name) const
{
const char* val = getenv(key.c_str());
return (val != NULL) || _properties->hasProperty(key);
return tryGetPropertyAsString(name, NULL);
}
ConfigurationProviderImpl::ConfigurationProviderImpl()
bool ConfigurationMap::tryGetPropertyAsString(const std::string& name, std::string* val) const
{
properties_t::const_iterator it = properties.find(name);
if(it==properties.end())
return false;
if(val)
*val = it->second;
return true;
}
ConfigurationProviderImpl::~ConfigurationProviderImpl()
bool ConfigurationEnviron::tryGetPropertyAsString(const std::string& name, std::string* val) const
{
const char *env = getenv(name.c_str());
if(!env || !*env)
return false;
if(val)
*val = env;
return true;
}
bool ConfigurationStack::tryGetPropertyAsString(const std::string& name, std::string* val) const
{
for(confs_t::const_reverse_iterator it = confs.rbegin(), end = confs.rend();
it!=end; ++it)
{
Configuration& conf = **it;
if(conf.tryGetPropertyAsString(name, val))
return true;
}
return false;
}
ConfigurationBuilder::ConfigurationBuilder() :stack(new ConfigurationStack) {}
ConfigurationBuilder& ConfigurationBuilder::push_env()
{
Configuration::shared_pointer env(new ConfigurationEnviron);
stack->push_back(env);
return *this;
}
ConfigurationBuilder& ConfigurationBuilder::push_map()
{
Configuration::shared_pointer env(new ConfigurationMap(mymap));
stack->push_back(env);
mymap.clear();
return *this;
}
ConfigurationBuilder&
ConfigurationBuilder::push_config(const Configuration::shared_pointer& conf)
{
stack->push_back(conf);
return *this;
}
ConfigurationBuilder&
ConfigurationBuilder::_add(const std::string& name, const std::string& val)
{
if(name.find_first_of(" \t\r\n")!=name.npos)
THROW_EXCEPTION2(std::invalid_argument, "Key name may not contain whitespace");
mymap[name] = val;
return *this;
}
Configuration::shared_pointer ConfigurationBuilder::build()
{
if(!mymap.empty())
THROW_EXCEPTION2(std::logic_error, "Missing call to .push_map()");
if(stack->size()==0) {
return Configuration::shared_pointer(new ConfigurationMap); // empty map
} else if(stack->size()==1) {
return stack->pop_back();
} else {
return stack;
}
}
void ConfigurationProviderImpl::registerConfiguration(const string &name, Configuration::shared_pointer const & configuration)
@@ -384,12 +307,15 @@ void ConfigurationProviderImpl::registerConfiguration(const string &name, Config
Configuration::shared_pointer ConfigurationProviderImpl::getConfiguration(const string &name)
{
std::map<std::string,Configuration::shared_pointer>::iterator configsIter = _configs.find(name);
Lock guard(_mutex);
std::map<std::string,Configuration::shared_pointer>::iterator configsIter = _configs.find(name);
if(configsIter != _configs.end())
{
return configsIter->second;
}
return Configuration::shared_pointer();
Configuration::shared_pointer env(new ConfigurationEnviron); // default to environment only
_configs[name] = env; // ensure that a later attempt to define this config will fail
return env;
}
ConfigurationProvider::shared_pointer configurationProvider;
@@ -401,8 +327,7 @@ ConfigurationProvider::shared_pointer ConfigurationFactory::getProvider()
if(configurationProvider.get() == NULL)
{
configurationProvider.reset(new ConfigurationProviderImpl());
// default
Configuration::shared_pointer systemConfig(new SystemConfigurationImpl());
Configuration::shared_pointer systemConfig(new ConfigurationEnviron);
configurationProvider->registerConfiguration("system", systemConfig);
}
return configurationProvider;

View File

@@ -26,6 +26,7 @@ namespace pvAccess {
void addDefaultBroadcastAddress(InetAddrVector* v, unsigned short p) {
osiSockAddr pNewNode;
pNewNode.ia.sin_family = AF_INET;
// TODO this does not work in case of no active interfaces, should return 127.0.0.1 then
pNewNode.ia.sin_addr.s_addr = htonl(INADDR_BROADCAST);
pNewNode.ia.sin_port = htons(p);
v->push_back(pNewNode);
@@ -47,6 +48,9 @@ InetAddrVector* getBroadcastAddresses(SOCKET sock,
v->push_back(sn->addr);
}
ellFree(&as);
// add fallback address
if (!v->size())
addDefaultBroadcastAddress(v, defaultPort);
return v;
}
@@ -208,5 +212,199 @@ int getLoopbackNIF(osiSockAddr &loAddr, string const & localNIF, unsigned short
return 1;
}
// copy of base-3.14.12.4/src/libCom/osi/os/default/osdNetIntf.c
// TODO support windows (see osi/os/WIN32/osdNetIntf.c)
#include <osiSock.h>
//#include <epicsAssert.h>
#include <errlog.h>
/*
* Determine the size of an ifreq structure
* Made difficult by the fact that addresses larger than the structure
* size may be returned from the kernel.
*/
static size_t ifreqSize ( struct ifreq *pifreq )
{
size_t size;
size = ifreq_size ( pifreq );
if ( size < sizeof ( *pifreq ) ) {
size = sizeof ( *pifreq );
}
return size;
}
/*
* Move to the next ifreq structure
*/
static struct ifreq * ifreqNext ( struct ifreq *pifreq )
{
struct ifreq *ifr;
ifr = ( struct ifreq * )( ifreqSize (pifreq) + ( char * ) pifreq );
return ifr;
}
int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr)
{
static const unsigned nelem = 100;
int status;
struct ifconf ifconf;
struct ifreq *pIfreqList;
struct ifreq *pIfreqListEnd;
struct ifreq *pifreq;
struct ifreq *pnextifreq;
int match;
/*
* use pool so that we avoid using too much stack space
*
* nelem is set to the maximum interfaces
* on one machine here
*/
pIfreqList = (struct ifreq *) calloc ( nelem, sizeof(*pifreq) );
if (!pIfreqList) {
errlogPrintf ("discoverInterfaces(): no memory to complete request\n");
return -1;
}
ifconf.ifc_len = nelem * sizeof(*pifreq);
ifconf.ifc_req = pIfreqList;
status = socket_ioctl (socket, SIOCGIFCONF, &ifconf);
if (status < 0 || ifconf.ifc_len == 0) {
/*ifDepenDebugPrintf(("discoverInterfaces(): status: 0x08x, ifconf.ifc_len: %d\n", status, ifconf.ifc_len));*/
errlogPrintf ("discoverInterfaces(): unable to fetch network interface configuration\n");
free (pIfreqList);
return -1;
}
pIfreqListEnd = (struct ifreq *) (ifconf.ifc_len + (char *) pIfreqList);
pIfreqListEnd--;
for ( pifreq = pIfreqList; pifreq <= pIfreqListEnd; pifreq = pnextifreq ) {
uint32_t current_ifreqsize;
/*
* find the next ifreq
*/
pnextifreq = ifreqNext (pifreq);
/* determine ifreq size */
current_ifreqsize = ifreqSize ( pifreq );
/* copy current ifreq to aligned bufferspace (to start of pIfreqList buffer) */
memmove(pIfreqList, pifreq, current_ifreqsize);
/*ifDepenDebugPrintf (("discoverInterfaces(): found IFACE: %s len: 0x%x current_ifreqsize: 0x%x \n",
pIfreqList->ifr_name,
ifreq_size(pifreq),
current_ifreqsize));*/
/*
* If its not an internet interface then dont use it
*/
if ( pIfreqList->ifr_addr.sa_family != AF_INET ) {
/*ifDepenDebugPrintf ( ("discoverInterfaces(): interface \"%s\" was not AF_INET\n", pIfreqList->ifr_name) );*/
continue;
}
/*
* if it isnt a wildcarded interface then look for
* an exact match
*/
match = 0;
if ( pMatchAddr && pMatchAddr->sa.sa_family != AF_UNSPEC ) {
if ( pMatchAddr->sa.sa_family != AF_INET ) {
continue;
}
if ( pMatchAddr->ia.sin_addr.s_addr != htonl (INADDR_ANY) ) {
struct sockaddr_in *pInetAddr = (struct sockaddr_in *) &pIfreqList->ifr_addr;
if ( pInetAddr->sin_addr.s_addr != pMatchAddr->ia.sin_addr.s_addr ) {
/*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" didnt match\n", pIfreqList->ifr_name) );*/
continue;
}
else
match = 1;
}
}
status = socket_ioctl ( socket, SIOCGIFFLAGS, pIfreqList );
if ( status ) {
errlogPrintf ("discoverInterfaces(): net intf flags fetch for \"%s\" failed\n", pIfreqList->ifr_name);
continue;
}
/*
* dont bother with interfaces that have been disabled
*/
if ( ! ( pIfreqList->ifr_flags & IFF_UP ) ) {
/*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" was down\n", pIfreqList->ifr_name) );*/
continue;
}
/*
* dont use the loop back interface, unless it maches pMatchAddr
*/
if (!match) {
if ( pIfreqList->ifr_flags & IFF_LOOPBACK ) {
/*ifDepenDebugPrintf ( ("discoverInterfaces(): ignoring loopback interface: \"%s\"\n", pIfreqList->ifr_name) );*/
continue;
}
}
ifaceNode node;
node.ifaceAddr.sa = pIfreqList->ifr_addr;
/*
* If this is an interface that supports
* broadcast fetch the broadcast address.
*
* Otherwise if this is a point to point
* interface then use the destination address.
*
* Otherwise CA will not query through the
* interface.
*/
if ( pIfreqList->ifr_flags & IFF_BROADCAST ) {
status = socket_ioctl (socket, SIOCGIFBRDADDR, pIfreqList);
if ( status ) {
errlogPrintf ("discoverInterfaces(): net intf \"%s\": bcast addr fetch fail\n", pIfreqList->ifr_name);
continue;
}
node.ifaceBCast.sa = pIfreqList->ifr_broadaddr;
/*ifDepenDebugPrintf ( ( "found broadcast addr = %x\n", ntohl ( pNewNode->addr.ia.sin_addr.s_addr ) ) );*/
}
#if defined (IFF_POINTOPOINT)
else if ( pIfreqList->ifr_flags & IFF_POINTOPOINT ) {
status = socket_ioctl ( socket, SIOCGIFDSTADDR, pIfreqList);
if ( status ) {
/*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\": pt to pt addr fetch fail\n", pIfreqList->ifr_name) );*/
continue;
}
node.ifaceBCast.sa = pIfreqList->ifr_dstaddr;
}
#endif
else {
if (match)
node.ifaceBCast.sa.sa_family = AF_UNSPEC;
else
{
/*ifDepenDebugPrintf ( ( "discoverInterfaces(): net intf \"%s\": not point to point or bcast?\n", pIfreqList->ifr_name ) );*/
continue;
}
}
/*ifDepenDebugPrintf ( ("discoverInterfaces(): net intf \"%s\" found\n", pIfreqList->ifr_name) );*/
list.push_back(node);
}
free ( pIfreqList );
return 0;
}
}
}

View File

@@ -39,41 +39,34 @@ namespace pvAccess {
class epicsShareClass Properties
{
public:
Properties();
Properties(const std::string &fileName);
virtual ~Properties();
Properties() EPICS_DEPRECATED;
Properties(const std::string &fileName) EPICS_DEPRECATED;
void setProperty(const std::string &key,const std::string &value);
std::string getProperty(const std::string &key);
std::string getProperty(const std::string &key, const std::string &defaultValue);
bool hasProperty(const std::string &key);
inline void setProperty(const std::string &key,const std::string &value)
{ _properties[key] = value; }
const std::string& getProperty(const std::string &key) const;
const std::string& getProperty(const std::string &key, const std::string &defaultValue) const;
inline bool hasProperty(const std::string &key) const
{ return _properties.find(key) != _properties.end(); }
void store();
void store(const std::string &fileName);
void load();
void store() const;
void store(const std::string &fileName) const;
void store(std::ostream& strm) const;
void load();
void load(const std::string &fileName);
void load(std::istream& strm);
void list();
inline size_t size() const {return _properties.size();}
private:
std::map<std::string,std::string> _properties;
std::auto_ptr<std::ifstream> _infile;
std::auto_ptr<std::ofstream> _outfile;
std::string _fileName;
inline void truncate(std::string& str)
{
while(str.length() != 0 && (str.at(0) == ' ' || str.at(0) == '\t'))
{
str.erase(0,1);
}
while(str.length() != 0 && (str.at(str.length()-1) == ' ' || str.at(str.length()-1) == '\t'))
{
str.erase(str.length()-1,1);
}
}
typedef std::map<std::string,std::string> _properties_t;
_properties_t _properties;
std::string _fileName;
public:
inline const _properties_t& map() const {return _properties;}
};
class ConfigurationStack;
/**
* Configuration
@@ -96,7 +89,7 @@ public:
*
* @return environment variable value as bool or default value if it does not exist.
*/
virtual bool getPropertyAsBoolean(const std::string &name, const bool defaultValue) = 0;
bool getPropertyAsBoolean(const std::string &name, const bool defaultValue) const;
/**
* Get the environment variable specified by name or return default value
* if it does not exist.
@@ -106,7 +99,7 @@ public:
*
* @return environment variable value as int32 or default value if it does not exist.
*/
virtual epics::pvData::int32 getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue) = 0;
epics::pvData::int32 getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue) const;
/**
* Get the environment variable specified by name or return default value
* if it does not exist.
@@ -116,7 +109,7 @@ public:
*
* @return environment variable value as float or default value if it does not exist.
*/
virtual float getPropertyAsFloat(const std::string &name, const float defaultValue) = 0;
float getPropertyAsFloat(const std::string &name, const float defaultValue) const;
/**
* Get the environment variable specified by name or return default value
* if it does not exist.
@@ -126,7 +119,7 @@ public:
*
* @return environment variable value as double or default value if it does not exist.
*/
virtual float getPropertyAsDouble(const std::string &name, const double defaultValue) = 0;
double getPropertyAsDouble(const std::string &name, const double defaultValue) const;
/**
* Get the environment variable specified by name or return default value
* if it does not exist.
@@ -136,7 +129,7 @@ public:
*
* @return environment variable value as std::string or default value if it does not exist.
*/
virtual std::string getPropertyAsString(const std::string &name, const std::string &defaultValue) = 0;
std::string getPropertyAsString(const std::string &name, const std::string &defaultValue) const;
/**
* Fetch and parse as a socket address and port number (address family set accordingly).
* At present only numeric addresses are parsed (eg. "127.0.0.1:4242").
@@ -147,28 +140,76 @@ public:
* @pram addr pointer to the address struct to be filled in
* @return true if addr now contains an address, false otherwise
*/
virtual bool getPropertyAsAddress(const std::string& name, osiSockAddr* addr) = 0;
bool getPropertyAsAddress(const std::string& name, osiSockAddr* addr) const;
virtual bool hasProperty(const std::string &name) = 0;
bool hasProperty(const std::string &name) const;
protected:
friend class ConfigurationStack;
virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const = 0;
};
class epicsShareClass SystemConfigurationImpl: public Configuration
//! Lookup configuration strings from an in memory store
class epicsShareClass ConfigurationMap: public Configuration
{
public:
SystemConfigurationImpl();
~SystemConfigurationImpl();
bool getPropertyAsBoolean(const std::string &name, const bool defaultValue);
epics::pvData::int32 getPropertyAsInteger(const std::string &name, const epics::pvData::int32 defaultValue);
float getPropertyAsFloat(const std::string &name, const float defaultValue);
float getPropertyAsDouble(const std::string &name, const double defaultValue);
std::string getPropertyAsString(const std::string &name, const std::string &defaultValue);
bool getPropertyAsAddress(const std::string& name, osiSockAddr* addr);
bool hasProperty(const std::string &name);
std::auto_ptr<Properties> _properties;
typedef std::map<std::string, std::string> properties_t;
properties_t properties;
ConfigurationMap() {}
ConfigurationMap(const properties_t& p) :properties(p) {}
private:
std::istringstream _ibuffer;
std::ostringstream _obuffer;
virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const;
};
//! Lookup configuration strings from the process environment
class epicsShareClass ConfigurationEnviron: public Configuration
{
private:
virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const;
};
typedef ConfigurationEnviron SystemConfigurationImpl;
//! Lookup configuration strings from a heap of sub-Configurations.
//! Most recently push'd is checked first.
class epicsShareClass ConfigurationStack : public Configuration
{
typedef std::vector<std::tr1::shared_ptr<Configuration> > confs_t;
confs_t confs;
virtual bool tryGetPropertyAsString(const std::string& name, std::string* val) const;
public:
inline void push_back(const confs_t::value_type& conf) {
confs.push_back(conf);
}
inline confs_t::value_type pop_back() {
if(confs.empty())
throw std::runtime_error("Stack empty");
confs_t::value_type ret(confs.back());
confs.pop_back();
return ret;
}
inline size_t size() const {return confs.size();}
};
struct ConfigurationBuilder
{
ConfigurationBuilder();
ConfigurationBuilder& push_env();
ConfigurationBuilder& push_map();
ConfigurationBuilder& push_config(const Configuration::shared_pointer&);
template<typename V>
ConfigurationBuilder& add(const std::string& name, const V& val)
{
std::ostringstream strm;
strm<<val;
return _add(name, strm.str());
}
Configuration::shared_pointer build();
private:
ConfigurationBuilder& _add(const std::string& name, const std::string& val);
ConfigurationMap::properties_t mymap;
std::tr1::shared_ptr<ConfigurationStack> stack;
friend ConfigurationBuilder& operator<<(ConfigurationBuilder&, const std::string& s);
};
/**
@@ -202,11 +243,11 @@ public:
class ConfigurationProviderImpl: public ConfigurationProvider
{
public:
ConfigurationProviderImpl();
ConfigurationProviderImpl() {}
/**
* Destructor. Note: Registered configurations will be deleted!!
*/
~ConfigurationProviderImpl();
~ConfigurationProviderImpl() {}
Configuration::shared_pointer getConfiguration(const std::string &name);
void registerConfiguration(const std::string &name, Configuration::shared_pointer const & configuration);
private:
@@ -231,6 +272,14 @@ public:
* @return configuration provider
*/
static ConfigurationProvider::shared_pointer getProvider();
static void registerConfiguration(const std::string &name, Configuration::shared_pointer const & configuration)
{
getProvider()->registerConfiguration(name, configuration);
}
static Configuration::shared_pointer getConfiguration(const std::string& name)
{
return getProvider()->getConfiguration(name);
}
private:
ConfigurationFactory() {};

190
src/utils/pv/fairQueue.h Normal file
View File

@@ -0,0 +1,190 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#ifndef FAIRQUEUE_H
#define FAIRQUEUE_H
#include <shareLib.h>
#include <epicsEvent.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
#include <ellLib.h>
#include <dbDefs.h>
#include <pv/sharedPtr.h>
namespace epics {namespace pvAccess {
/** @brief An intrusive, loss-less, unbounded, round-robin queue
*
* The parameterized type 'T' must be a sub-class of @class fair_queue<T>::entry
*
* @li Intrusive. Entries in the queue must derive from @class entry
*
* @li Loss-less. An entry will be returned by pop_front() corresponding to
* each call to push_back().
*
* @li Un-bounded. There is no upper limit to the number of times an entry
* may be queued other than machine constraints.
*
* @li Round robin. The order that entries are returned may not match
* the order they were added in. "Fairness" is achived by returning
* entries in a rotating fashion based on the order in which they were
* first added. Re-adding the same entry before it is popped does not change
* this order.
* Adding [A, A, B, A, C, C] would give out [A, B, C, A, C, A].
*
* @warning Only one thread should call pop_front()
* as push_back() does not broadcast (only wakes up one waiter)
*/
template<typename T>
class epicsShareClass fair_queue
{
typedef epicsGuard<epicsMutex> guard_t;
public:
typedef std::tr1::shared_ptr<T> value_type;
class epicsShareClass entry {
/* In c++, use of ellLib (which implies offsetof()) should be restricted
* to POD structs. So enode_t exists as a POD struct for which offsetof()
* is safe and well defined. enode_t::self is used in place of
* casting via CONTAINER(penode, entry, enode)
*/
struct enode_t {
ELLNODE node;
entry *self;
} enode;
unsigned Qcnt;
value_type holder;
#ifndef NDEBUG
fair_queue *owner;
#endif
friend class fair_queue;
entry(const entry&);
entry& operator=(const entry&);
public:
entry() :Qcnt(0), holder()
#ifndef NDEBUG
, owner(NULL)
#endif
{
enode.node.next = enode.node.previous = NULL;
enode.self = this;
}
~entry() {
// nodes should be removed from the list before deletion
assert(!enode.node.next && !enode.node.previous);
#ifndef NDEBUG
assert(!owner);
#endif
}
};
fair_queue()
{
ellInit(&list);
}
~fair_queue()
{
clear();
assert(ellCount(&list)==0);
}
void clear()
{
value_type C;
guard_t G(mutex);
do {
pop_front_try(C);
} while(C);
}
bool empty() const {
guard_t G(mutex);
return ellFirst(&list)==NULL;
}
void push_back(const value_type& ent)
{
bool wake;
entry *P = ent.get();
{
guard_t G(mutex);
wake = ellFirst(&list)==NULL; // empty queue
if(P->Qcnt++==0) {
// not in list
assert(P->owner==NULL);
P->owner = this;
P->holder = ent; // the list will hold a reference
ellAdd(&list, &P->enode.node); // push_back
} else
assert(P->owner==this);
}
if(wake) wakeup.signal();
}
bool pop_front_try(value_type& ret)
{
guard_t G(mutex);
ELLNODE *cur = ellGet(&list); // pop_front
if(cur) {
typedef typename entry::enode_t enode_t;
enode_t *PN = CONTAINER(cur, enode_t, node);
entry *P = PN->self;
assert(P->owner==this);
assert(P->Qcnt>0);
if(--P->Qcnt==0) {
PN->node.previous = PN->node.next = NULL;
P->owner = NULL;
ret.swap(P->holder);
} else {
ellAdd(&list, &P->enode.node); // push_back
ret = P->holder;
}
return true;
} else {
ret.reset();
return false;
}
}
void pop_front(value_type& ret)
{
while(1) {
pop_front_try(ret);
if(ret)
break;
wakeup.wait();
}
}
bool pop_front(value_type& ret, double timeout)
{
while(1) {
pop_front_try(ret);
if(ret)
return true;
if(!wakeup.wait(timeout))
return false;
}
}
private:
ELLLIST list;
mutable epicsMutex mutex;
mutable epicsEvent wakeup;
};
}} // namespace
#endif // FAIRQUEUE_H

View File

@@ -42,6 +42,17 @@ namespace pvAccess {
*/
epicsShareFunc InetAddrVector* getBroadcastAddresses(SOCKET sock, unsigned short defaultPort);
struct ifaceNode {
osiSockAddr ifaceAddr, ifaceBCast;
};
typedef std::vector<ifaceNode> IfaceNodeVector;
epicsShareFunc int discoverInterfaces(IfaceNodeVector &list, SOCKET socket, const osiSockAddr *pMatchAddr = 0);
/**
* Returns NIF index for given interface address, or -1 on failure.
*/
epicsShareFunc int discoverInterfaceIndex(SOCKET socket, const osiSockAddr *pMatchAddr);
/**
* Encode IPv4 address as IPv6 address.
* @param buffer byte-buffer where to put encoded data.

View File

@@ -3,7 +3,7 @@
SRC_DIRS += $(PVACCESS_TEST)/remote
TESTPROD_HOST += testChannelAccess
testChannelAccess_SRCS = testChannelAccess channelAccessIFTest
testChannelAccess_SRCS = channelAccessIFTest.cpp
testHarness_SRCS += testChannelAccess.cpp channelAccessIFTest.cpp
TESTS += testChannelAccess
@@ -48,3 +48,5 @@ rpcClientExample_SRCS += rpcClientExample.cpp
PROD_HOST += pipelineServiceExample
pipelineServiceExample_SRCS += pipelineServiceExample.cpp
TESTPROD_HOST += testClientFactory
testClientFactory_SRCS += testClientFactory.cpp

View File

@@ -21,6 +21,9 @@
#include "channelAccessIFTest.h"
//#define ENABLE_STRESS_TESTS
#define TESTSERVERNOMAIN
#include "testServer.cpp"
using namespace std::tr1;
@@ -38,14 +41,45 @@ std::string ChannelAccessIFTest::TEST_SUMRPC_CHANNEL_NAME = "testSum";
// double[] value
std::string ChannelAccessIFTest::TEST_ARRAY_CHANNEL_NAME = "testArray1";
#ifdef ENABLE_STRESS_TESTS
#define EXTRA_STRESS_TESTS 5
#else
#define EXTRA_STRESS_TESTS 0
#endif
namespace {
struct ScopedClientFactory {
ScopedClientFactory() { ClientFactory::start(); }
~ScopedClientFactory() { ClientFactory::stop(); }
};
}
int ChannelAccessIFTest::runAllTest() {
#ifdef ENABLE_STRESS_TESTS
testPlan(158);
#else
testPlan(153);
#endif
testPlan(153+EXTRA_STRESS_TESTS);
Configuration::shared_pointer base_config(ConfigurationBuilder()
//.add("EPICS_PVA_DEBUG", "3")
.add("EPICS_PVAS_INTF_ADDR_LIST", "127.0.0.1")
.add("EPICS_PVA_ADDR_LIST", "127.0.0.1")
.add("EPICS_PVA_AUTO_ADDR_LIST","0")
.add("EPICS_PVA_SERVER_PORT", "0")
.add("EPICS_PVA_BROADCAST_PORT", "0")
.push_map()
.build());
TestServer::shared_pointer tstserv(new TestServer(base_config));
tstserv->start();
testDiag("TestServer on ports TCP=%u UDP=%u\n",
tstserv->getServerPort(),
tstserv->getBroadcastPort());
ConfigurationFactory::registerConfiguration("pvAccess-client",
ConfigurationBuilder()
.push_config(base_config)
.add("EPICS_PVA_BROADCAST_PORT", tstserv->getBroadcastPort())
.push_map()
.build());
ScopedClientFactory SCF;
test_implementation();
test_providerName();
@@ -2348,3 +2382,30 @@ PVStructure::shared_pointer ChannelAccessIFTest::createArrayPvRequest() {
pvFieldName->put("value");
return pvRequest;
}
class ChannelAccessIFRemoteTest: public ChannelAccessIFTest {
public:
virtual ChannelProvider::shared_pointer getChannelProvider() {
return getChannelProviderRegistry()->getProvider(
"pva");
}
virtual long getTimeoutSec() {
return 3;
}
virtual bool isLocal() { return false;}
};
MAIN(testChannelAccess)
{
SET_LOG_LEVEL(logLevelError);
ChannelAccessIFRemoteTest caRemoteTest;
return caRemoteTest.runAllTest();
}

View File

@@ -1,104 +0,0 @@
/*
* testChannelAccess.cpp
*/
#ifdef _WIN32
#define NOMINMAX
#endif
// TODO not nice
// disable buggy boost enable_shared_from_this assert code
#define BOOST_DISABLE_ASSERTS
#define TESTSERVERNOMAIN
#include <envDefs.h>
#include <epicsExit.h>
#include <epicsUnitTest.h>
#include <testMain.h>
#include <pv/serverContext.h>
#include <pv/clientFactory.h>
#include "channelAccessIFTest.h"
#include "testServer.cpp"
class ServerContextAction : public Runnable {
public:
ServerContextAction():
m_serverThread(){}
virtual void run()
{
testServer(0);
}
void stop() {
testServerShutdown();
}
void start() {
m_serverThread.reset(new epics::pvData::Thread("pvAccess", highPriority, this));
}
private:
auto_ptr<epics::pvData::Thread> m_serverThread;
};
class ChannelAccessIFRemoteTest: public ChannelAccessIFTest {
public:
ChannelAccessIFRemoteTest(): m_serverContextAction()
{
m_serverContextAction.start();
ClientFactory::start();
}
virtual ChannelProvider::shared_pointer getChannelProvider() {
return getChannelProviderRegistry()->getProvider(
"pva");
}
virtual long getTimeoutSec() {
return 3;
}
virtual bool isLocal() { return false;}
~ChannelAccessIFRemoteTest() {
m_serverContextAction.stop();
ClientFactory::stop();
// shutdown SIGSEG problems
epicsThreadSleep(2.0);
}
private:
ServerContextAction m_serverContextAction;
};
MAIN(testChannelAccess)
{
// note: this leaks memory (uses putenv)
epicsEnvSet("EPICS_PVA_ADDR_LIST", "127.0.0.1");
epicsEnvSet("EPICS_PVA_AUTO_ADDR_LIST", "0");
SET_LOG_LEVEL(logLevelError);
ChannelAccessIFRemoteTest caRemoteTest;
return caRemoteTest.runAllTest();
}

View File

@@ -0,0 +1,15 @@
#include <epicsExit.h>
#include <pv/clientFactory.h>
#include <pv/pvAccess.h>
int main()
{
epics::pvAccess::ClientFactory::start();
epics::pvAccess::getChannelProviderRegistry()->getProvider("pva");
epics::pvAccess::ClientFactory::stop();
//epicsThreadSleep ( 3.0 );
//epicsExitCallAtExits();
return 0;
}

View File

@@ -22,6 +22,33 @@ namespace epics {
namespace pvAccess {
struct sender_break : public connection_closed_exception
{
sender_break() : connection_closed_exception("break") {}
};
struct TransportSenderDisconnect: public TransportSender {
void unlock() {}
void lock() {}
void send(ByteBuffer *buffer, TransportSendControl *control)
{
control->flush(true);
throw sender_break();
}
};
struct TransportSenderSignal: public TransportSender {
Event *evt;
TransportSenderSignal(Event& evt) :evt(&evt) {}
void unlock() {}
void lock() {}
void send(ByteBuffer *buffer, TransportSendControl *control)
{
evt->signal();
}
};
class PVAMessage {
public:
@@ -257,13 +284,6 @@ namespace epics {
}
void endBlockedProcessSendQueue() {
//TODO not thread safe
_blockingProcessQueue = false;
_sendQueue.wakeup();
}
void close() { _closedCount++; }
bool isOpen() { return _closedCount == 0; }
@@ -288,6 +308,10 @@ namespace epics {
void sendCompleted() { _sendCompletedCount++; }
void breakSender() {
enqueueSendRequest(std::tr1::shared_ptr<TransportSender>(new TransportSenderDisconnect()));
}
bool terminated() { return false; }
void cachedSerialize(
@@ -326,6 +350,8 @@ namespace epics {
}
const osiSockAddr* getRemoteAddress() const { return 0; }
std::string dummyRemoteName;
const std::string& getRemoteName() const {return dummyRemoteName;}
epics::pvData::int8 getRevision() const
{
@@ -412,7 +438,7 @@ namespace epics {
public:
int runAllTest() {
testPlan(5882);
testPlan(5883);
testHeaderProcess();
testInvalidHeaderMagic();
testInvalidHeaderSegmentedInNormal();
@@ -438,7 +464,6 @@ namespace epics {
testSendException();
testSendHugeMessagePartes();
testRecipient();
testClearSendQueue();
testInvalidArguments();
testDefaultModes();
testEnqueueSendRequestExceptionThrown();
@@ -2223,7 +2248,6 @@ namespace epics {
"%s: codec._closedCount == 1", CURRENT_FUNCTION);
}
class TransportSenderForTestEnqueueSendRequest:
public TransportSender {
public:
@@ -2290,7 +2314,10 @@ namespace epics {
// process
codec.enqueueSendRequest(sender);
codec.enqueueSendRequest(sender2);
codec.processSendQueue();
codec.breakSender();
try{
codec.processSendQueue();
}catch(sender_break&) {}
codec.transferToReadBuffer();
@@ -2430,11 +2457,16 @@ namespace epics {
//was processed
testOk(0 == codec._sendCompletedCount,
"%s: 0 == codec._sendCompletedCount", CURRENT_FUNCTION);
testOk1(!codec.sendQueueEmpty());
codec.processSendQueue();
codec.breakSender();
try{
codec.processSendQueue();
}catch(sender_break&) {}
testOk(1 == codec._sendCompletedCount,
"%s: 1 == codec._sendCompletedCount", CURRENT_FUNCTION);
testOk1(codec.sendQueueEmpty());
codec.transferToReadBuffer();
@@ -2483,6 +2515,13 @@ namespace epics {
"%s: 0 == codec.getSendBuffer()->getPosition()",
CURRENT_FUNCTION);
testOk1(codec.sendQueueEmpty());
testDiag("%u %u", (unsigned)codec._scheduleSendCount,
(unsigned)codec._sendCompletedCount);
testOk1(3 == codec._scheduleSendCount);
testOk1(1 == codec._sendCompletedCount);
// now queue is empty and thread is right
codec.enqueueSendRequest(sender2, PVA_MESSAGE_HEADER_SIZE);
@@ -2491,12 +2530,17 @@ namespace epics {
"%s: PVA_MESSAGE_HEADER_SIZE == "
"codec.getSendBuffer()->getPosition()",
CURRENT_FUNCTION);
testOk(3 == codec._scheduleSendCount,
"%s: 3 == codec._scheduleSendCount", CURRENT_FUNCTION);
testOk(1 == codec._sendCompletedCount,
"%s: 1 == codec._sendCompletedCount", CURRENT_FUNCTION);
codec.processWrite();
testDiag("%u %u", (unsigned)codec._scheduleSendCount,
(unsigned)codec._sendCompletedCount);
testOk1(4 == codec._scheduleSendCount);
testOk1(1 == codec._sendCompletedCount);
codec.breakSender();
try{
codec.processWrite();
}catch(sender_break&) {}
testOk(2 == codec._sendCompletedCount,
"%s: 2 == codec._sendCompletedCount", CURRENT_FUNCTION);
@@ -2575,7 +2619,10 @@ namespace epics {
// process
codec.enqueueSendRequest(sender);
codec.processSendQueue();
codec.breakSender();
try{
codec.processSendQueue();
}catch(sender_break&) {}
codec.transferToReadBuffer();
@@ -2664,7 +2711,7 @@ namespace epics {
// process
codec.enqueueSendRequest(sender);
codec.breakSender();
try
{
codec.processSendQueue();
@@ -2781,7 +2828,10 @@ namespace epics {
// process
codec.enqueueSendRequest(sender);
codec.processSendQueue();
codec.breakSender();
try{
codec.processSendQueue();
}catch(sender_break&) {}
codec.addToReadBuffer();
@@ -2886,37 +2936,6 @@ namespace epics {
TestCodec &_codec;
};
void testClearSendQueue()
{
testDiag("BEGIN TEST %s:", CURRENT_FUNCTION);
TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE);
std::tr1::shared_ptr<TransportSender> sender =
std::tr1::shared_ptr<TransportSender>(
new TransportSenderForTestClearSendQueue(codec));
std::tr1::shared_ptr<TransportSender> sender2 =
std::tr1::shared_ptr<TransportSender>(
new TransportSender2ForTestClearSendQueue(codec));
codec.enqueueSendRequest(sender);
codec.enqueueSendRequest(sender2);
codec.clearSendQueue();
codec.processSendQueue();
testOk(0 == codec.getSendBuffer()->getPosition(),
"%s: 0 == codec.getSendBuffer()->getPosition()",
CURRENT_FUNCTION);
testOk(0 == codec._writeBuffer.getPosition(),
"%s: 0 == codec._writeBuffer.getPosition()",
CURRENT_FUNCTION);
}
void testInvalidArguments()
{
testDiag("BEGIN TEST %s:", CURRENT_FUNCTION);
@@ -3128,6 +3147,7 @@ namespace epics {
TransportSendControl* control)
{
_codec.putControlMessage((int8_t)0x01, 0x00112233);
_codec.flush(true);
}
private:
@@ -3135,16 +3155,20 @@ namespace epics {
};
class ValueHolder {
class ValueHolder : public Runnable {
public:
ValueHolder(
TestCodec &testCodec,
AtomicValue<bool> &processTreadExited):
_testCodec(testCodec),
_processTreadExited(processTreadExited) {}
ValueHolder(TestCodec &testCodec):
_testCodec(testCodec) {}
TestCodec &_testCodec;
AtomicValue<bool> & _processTreadExited;
Event waiter;
virtual void run() {
waiter.signal();
try{
_testCodec.processSendQueue();
}catch(sender_break&) {}
}
};
@@ -3155,56 +3179,40 @@ namespace epics {
TestCodec codec(DEFAULT_BUFFER_SIZE,
DEFAULT_BUFFER_SIZE, true);
_processTreadExited.getAndSet(false);
std::tr1::shared_ptr<TransportSender> sender =
std::tr1::shared_ptr<TransportSender>(
new TransportSenderForTestBlockingProcessQueueTest(codec));
ValueHolder valueHolder(codec, _processTreadExited);
ValueHolder valueHolder(codec);
Event done;
epicsThreadCreate(
"testBlockingProcessQueueTest-processThread",
epicsThreadPriorityMedium,
epicsThreadGetStackSize(
epicsThreadStackMedium),
CodecTest::blockingProcessQueueThread,
&valueHolder);
Thread thr(Thread::Config(&valueHolder)
.name("testBlockingProcessQueueTest-processThread"));
epicsThreadSleep(3);
testOk(_processTreadExited.get() == false,
"%s: _processTreadExited.get() == false",
CURRENT_FUNCTION);
valueHolder.waiter.wait();
// let's put something into it
codec.enqueueSendRequest(sender);
codec.enqueueSendRequest(std::tr1::shared_ptr<TransportSender>(new TransportSenderSignal(done)));
epicsThreadSleep(1);
testDiag("Waiting for work");
done.wait();
testOk((std::size_t)PVA_MESSAGE_HEADER_SIZE ==
codec._writeBuffer.getPosition(),
"%s: PVA_MESSAGE_HEADER_SIZE == "
"codec._writeBuffer.getPosition()",
CURRENT_FUNCTION);
"codec._writeBuffer.getPosition() (%u)",
CURRENT_FUNCTION,
(unsigned)codec._writeBuffer.getPosition());
codec.endBlockedProcessSendQueue();
codec.breakSender();
epicsThreadSleep(1);
testOk(_processTreadExited.get() == true,
"%s: _processTreadExited.get() == true", CURRENT_FUNCTION);
thr.exitWait();
}
private:
void static blockingProcessQueueThread(void *param) {
ValueHolder *valueHolder = static_cast<ValueHolder *>(param);
// this should block
valueHolder->_testCodec.processSendQueue();
valueHolder->_processTreadExited.getAndSet(true);
}
AtomicValue<bool> _processTreadExited;
};
}

View File

@@ -371,17 +371,6 @@ int main()
{
for (int i = 0; i < 10; i++) {
{
/*
ClientContextImpl::shared_pointer context = createClientContextImpl();
context->printInfo();
context->initialize();
context->printInfo();
epicsThreadSleep ( SLEEP_TIME );
ChannelProvider::shared_pointer provider = context->getProvider();
*/
ClientFactory::start();
ChannelProvider::shared_pointer provider = getChannelProviderRegistry()->getProvider("pva");

View File

@@ -1554,12 +1554,14 @@ public:
m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), result);
}
#ifndef TESTSERVERNOMAIN
else if (channelName.find("testServerShutdown") == 0)
{
PVStructure::shared_pointer nullPtr;
m_channelRPCRequester->requestDone(Status::Ok, shared_from_this(), nullPtr);
testServerShutdown();
}
#endif
else
{
/*
@@ -2649,6 +2651,7 @@ public:
{
if (address == "local")
{
// this is a server instance provider, address holds remote socket IP
if (channelName == "testCounter")
{
channelRequester->channelCreated(Status::Ok, m_counterChannel);
@@ -2727,44 +2730,89 @@ public:
};
static ServerContextImpl::shared_pointer ctx;
void testServer(int timeToRun)
struct TestServer : public Runnable
{
POINTER_DEFINITIONS(TestServer);
MockChannelProviderFactory::shared_pointer factory(new MockChannelProviderFactory());
registerChannelProviderFactory(factory);
static TestServer::shared_pointer ctx;
//ServerContextImpl::shared_pointer ctx = ServerContextImpl::create();
ctx = ServerContextImpl::create();
ctx->initialize(getChannelProviderRegistry());
Configuration::shared_pointer conf;
ServerContextImpl::shared_pointer context;
Event startup;
Thread runner;
MockChannelProviderFactory::shared_pointer factory;
ctx->printInfo();
ctx->run(timeToRun);
ctx->destroy();
unregisterChannelProviderFactory(factory);
structureChangedListeners.clear();
TestServer(const Configuration::shared_pointer& conf)
:conf(conf)
,runner(Thread::Config(this).name("TestServer").autostart(false))
,factory(new MockChannelProviderFactory())
{
Lock guard(structureStoreMutex);
structureStore.clear();
registerChannelProviderFactory(factory);
context = ServerContextImpl::create(conf);
context->initialize(getChannelProviderRegistry());
}
void start(bool inSameThread = false)
{
if (inSameThread)
{
context->run(conf->getPropertyAsInteger("timeToRun", 0)); // default is no timeout
}
else
{
runner.start();
startup.wait(); // wait for thread to start
}
}
ctx.reset();
unregisterChannelProviderFactory(factory);
~TestServer()
{
context->shutdown();
runner.exitWait();
context->destroy();
unregisterChannelProviderFactory(factory);
structureChangedListeners.clear();
{
Lock guard(structureStoreMutex);
structureStore.clear();
}
ctx.reset();
unregisterChannelProviderFactory(factory);
shutdownSimADCs();
}
shutdownSimADCs();
}
// Use with EPICS_PVA_SERVER_PORT==0 for dynamic port (unit-tests)
unsigned short getServerPort()
{
return context->getServerPort();
}
unsigned short getBroadcastPort()
{
return context->getBroadcastPort();
}
virtual void run()
{
startup.signal();
context->run(conf->getPropertyAsInteger("timeToRun", 0)); // default is no timeout
}
void waitForShutdown() {
context->shutdown();
}
void shutdown() {
context->shutdown();
}
};
TestServer::shared_pointer TestServer::ctx;
void testServerShutdown()
{
// NOTE: this is not thread-safe TODO
ctx->shutdown();
TestServer::ctx->shutdown();
}
#include <epicsGetopt.h>
@@ -2789,7 +2837,7 @@ int main(int argc, char *argv[])
int opt; /* getopt() current option */
bool debug = false;
bool cleanupAndReport = false;
int timeToRun = 0;
std::string timeToRun("0");
setvbuf(stdout,NULL,_IOLBF,BUFSIZ); /* Set stdout to line buffering */
@@ -2799,7 +2847,7 @@ int main(int argc, char *argv[])
usage(argv);
return 0;
case 't': /* Print usage */
timeToRun = atoi(optarg);
timeToRun = optarg;
break;
case 'd': /* Debug log level */
debug = true;
@@ -2827,7 +2875,14 @@ int main(int argc, char *argv[])
srand ( time(NULL) );
testServer(timeToRun);
TestServer::shared_pointer srv(new TestServer(ConfigurationBuilder()
.push_env()
.add("timeToRun", timeToRun)
.push_map()
.build()));
TestServer::ctx = srv;
srv->context->printInfo();
srv->start(true);
cout << "Done" << endl;

View File

@@ -22,8 +22,6 @@ testInetAddressUtils_SYS_LIBS_WIN32 += ws2_32
testHarness_SRCS += testInetAddressUtils.cpp
TESTS += testInetAddressUtils
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
#TESTPROD_HOST += loggerTest
#loggerTest_SRCS += loggerTest.cpp
#testHarness_SRCS += loggerTest.cpp
@@ -49,3 +47,7 @@ configurationTest_SRCS += configurationTest.cpp
configurationTest_SYS_LIBS_WIN32 += ws2_32
#testHarness_SRCS += configurationTest.cpp
TESTS += configurationTest
TESTPROD_HOST += testFairQueue
testFairQueue_SRCS += testFairQueue
TESTS += testFairQueue

View File

@@ -14,6 +14,7 @@
#include <epicsAssert.h>
#include <epicsExit.h>
#include <envDefs.h>
#include <epicsString.h>
#include <osiSock.h>
#include <epicsUnitTest.h>
@@ -30,7 +31,58 @@ void setenv(char * a, char * b, int c)
using namespace epics::pvAccess;
using namespace epics::pvData;
using namespace std;
static const char indata[] =
"hello = world \n"
" # oops\n"
" #dd=da\n"
" empty = \n"
" this = is a test\n\n"
;
static const char expectdata[] =
"empty = \n"
"hello = world\n"
"this = is a test\n"
;
static
void showEscaped(const char *msg, const std::string& s)
{
std::vector<char> chars(epicsStrnEscapedFromRawSize(s.c_str(), s.size())+1);
epicsStrnEscapedFromRaw(&chars[0], chars.size(), s.c_str(), s.size());
testDiag("%s: '%s", msg, &chars[0]);
}
static
void testProp()
{
Properties plist;
{
std::istringstream input(indata);
plist.load(input);
testOk1(!input.bad());
testOk1(input.eof());
}
testOk1(plist.size()==3);
testOk1(plist.getProperty("hello")=="world");
testOk1(plist.getProperty("this")=="is a test");
testOk1(!plist.hasProperty("foobar"));
{
std::ostringstream output;
plist.store(output);
std::string expect(expectdata), actual(output.str());
testOk1(!output.bad());
testOk(expect.size()==actual.size(), "%u == %u", (unsigned)expect.size(), (unsigned)actual.size());
testOk1(actual==expectdata);
showEscaped("actual", actual);
showEscaped("expect", expect);
}
}
static void showEnv(const char *name)
{
@@ -43,6 +95,25 @@ static void setEnv(const char *name, const char *val)
testDiag("%s = \"%s\"", name, getenv(name));
}
static void testBuilder()
{
Configuration::shared_pointer C(ConfigurationBuilder()
.add("TESTKEY","value1")
.push_map()
.push_env()
.add("OTHERKEY","value3")
.push_map()
.build());
testOk1(C->getPropertyAsString("key", "X")=="X");
testOk1(C->getPropertyAsString("TESTKEY", "X")=="value1");
testOk1(C->getPropertyAsString("OTHERKEY", "X")=="value3");
setEnv("TESTKEY", "value2");
setEnv("OTHERKEY","value2");
testOk1(C->getPropertyAsString("TESTKEY", "X")=="value2");
testOk1(C->getPropertyAsString("OTHERKEY", "X")=="value3");
}
static void showAddr(const osiSockAddr& addr)
{
char buf[40];
@@ -60,9 +131,9 @@ static void showAddr(const osiSockAddr& addr)
} while(0)
MAIN(configurationTest)
static
void testConfig()
{
testPlan(35);
testDiag("Default configuration");
Configuration::shared_pointer configuration(new SystemConfigurationImpl());
@@ -126,7 +197,14 @@ MAIN(configurationTest)
Configuration::shared_pointer configurationOut(configProvider->getConfiguration("conf1"));
testOk1(configurationOut.get() == configuration.get());
}
MAIN(configurationTest)
{
testPlan(49);
testProp();
testBuilder();
testConfig();
return testDone();
}

View File

@@ -0,0 +1,75 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvAccessCPP is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#include <vector>
#include <pv/fairQueue.h>
#include <epicsUnitTest.h>
#include <testMain.h>
namespace {
struct Qnode : public epics::pvAccess::fair_queue<Qnode>::entry {
unsigned i;
Qnode(unsigned i):i(i) {}
};
} // namespace
static unsigned Ninput[] = {0,0,0,1,0,2,1,0,1,0,0};
static unsigned Nexpect[] = {0,1,2,0,1,0,1,0,0,0,0};
static
void testOrder()
{
epics::pvAccess::fair_queue<Qnode> Q;
typedef epics::pvAccess::fair_queue<Qnode>::value_type value_type;
std::vector<value_type> unique, inputs, outputs;
unique.resize(3);
unique[0].reset(new Qnode(0));
unique[1].reset(new Qnode(1));
unique[2].reset(new Qnode(2));
testDiag("Queueing");
for(unsigned i=0; i<NELEMENTS(Ninput); i++) {
testDiag("[%u] = %u", i, Ninput[i]);
Q.push_back(unique[Ninput[i]]);
}
testDiag("De-queue");
{
for(unsigned i=0; i<=NELEMENTS(Nexpect); i++) {
value_type E;
Q.pop_front_try(E);
if(!E) break;
outputs.push_back(E);
testDiag("Dequeue %u", E->i);
}
}
testOk(outputs.size()==NELEMENTS(Nexpect), "sizes match actual %u expected %u",
(unsigned)outputs.size(), (unsigned)NELEMENTS(Nexpect));
for(unsigned i=0; i<NELEMENTS(Nexpect); i++) {
if(i>=outputs.size()) {
testFail("output truncated");
continue;
}
testOk(outputs[i]->i==Nexpect[i], "[%u] %u == %u",
i, (unsigned)outputs[i]->i, Nexpect[i]);
}
}
MAIN(testFairQueue)
{
testPlan(12);
testOrder();
return testDone();
}

View File

@@ -180,8 +180,9 @@ void test_getBroadcastAddresses()
SOCKET socket = epicsSocketCreate(AF_INET, SOCK_STREAM, IPPROTO_TCP);
auto_ptr<InetAddrVector> broadcasts(getBroadcastAddresses(socket, 6678));
// at least one is expected
// at least one is expected, in case of no network connection a fallback address is returned
testOk1(static_cast<size_t>(0) < broadcasts->size());
//testDiag("getBroadcastAddresses() returned %zu entry/-ies.", broadcasts->size());
epicsSocketDestroy(socket);
// debug