manual merge
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -12,3 +12,8 @@ configure/*.local
|
||||
!configure/ExampleRELEASE.local
|
||||
**/O.*
|
||||
QtC-*
|
||||
*.config
|
||||
*.creator
|
||||
*.files
|
||||
*.includes
|
||||
*.user
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}};
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
/****************************************************************************************/
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
190
src/utils/pv/fairQueue.h
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
15
testApp/remote/testClientFactory.cpp
Normal file
15
testApp/remote/testClientFactory.cpp
Normal 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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
75
testApp/utils/testFairQueue.cpp
Normal file
75
testApp/utils/testFairQueue.cpp
Normal 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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user