Merged changes from default branch
This commit is contained in:
@@ -122,3 +122,4 @@ pvAccessApp/ca/caChannel.h
|
||||
pvAccessApp/ca/caChannel.cpp
|
||||
testApp/utils/Makefile
|
||||
testApp/remote/channelAccessIFTest.cpp
|
||||
testApp/remote/channelAccessIFTest.h
|
||||
|
||||
@@ -155,7 +155,7 @@ namespace epics {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void startMessage(epics::pvData::int8 command, std::size_t ensureCapacity);
|
||||
virtual void startMessage(epics::pvData::int8 command, std::size_t ensureCapacity, epics::pvData::int32 payloadSize = 0);
|
||||
virtual void endMessage();
|
||||
|
||||
virtual void flush(bool /*lastMessageCompleted*/) {
|
||||
|
||||
@@ -173,13 +173,13 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so
|
||||
// noop (note different sent addresses are possible)
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::startMessage(int8 command, size_t /*ensureCapacity*/) {
|
||||
void BlockingUDPTransport::startMessage(int8 command, size_t /*ensureCapacity*/, int32 payloadSize) {
|
||||
_lastMessageStartPosition = _sendBuffer->getPosition();
|
||||
_sendBuffer->putByte(PVA_MAGIC);
|
||||
_sendBuffer->putByte(PVA_VERSION);
|
||||
_sendBuffer->putByte((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00); // data + 7-bit endianess
|
||||
_sendBuffer->putByte(command); // command
|
||||
_sendBuffer->putInt(0); // temporary zero payload
|
||||
_sendBuffer->putInt(payloadSize);
|
||||
}
|
||||
|
||||
void BlockingUDPTransport::endMessage() {
|
||||
|
||||
+185
-15
@@ -95,6 +95,7 @@ namespace epics {
|
||||
}
|
||||
|
||||
|
||||
// thows io_exception, connection_closed_exception, invalid_stream_exception
|
||||
void AbstractCodec::processRead() {
|
||||
switch (_readMode)
|
||||
{
|
||||
@@ -565,7 +566,8 @@ namespace epics {
|
||||
|
||||
void AbstractCodec::startMessage(
|
||||
epics::pvData::int8 command,
|
||||
std::size_t ensureCapacity) {
|
||||
std::size_t ensureCapacity,
|
||||
epics::pvData::int32 payloadSize) {
|
||||
|
||||
_lastMessageStartPosition =
|
||||
std::numeric_limits<size_t>::max(); // TODO revise this
|
||||
@@ -577,7 +579,7 @@ namespace epics {
|
||||
_sendBuffer->putByte(
|
||||
(_lastSegmentedMessageType | _byteOrderFlag)); // data + endian
|
||||
_sendBuffer->putByte(command); // command
|
||||
_sendBuffer->putInt(0); // temporary zero payload
|
||||
_sendBuffer->putInt(payloadSize);
|
||||
|
||||
// apply offset
|
||||
if (_nextMessagePayloadOffset > 0)
|
||||
@@ -699,11 +701,7 @@ namespace epics {
|
||||
flush(false);
|
||||
}
|
||||
|
||||
|
||||
void AbstractCodec::flush(bool lastMessageCompleted) {
|
||||
|
||||
// automatic end
|
||||
endMessage(!lastMessageCompleted);
|
||||
void AbstractCodec::flushSendBuffer() {
|
||||
|
||||
_sendBuffer->flip();
|
||||
|
||||
@@ -722,6 +720,15 @@ namespace epics {
|
||||
_sendBuffer->clear();
|
||||
|
||||
_lastMessageStartPosition = std::numeric_limits<size_t>::max();
|
||||
}
|
||||
|
||||
void AbstractCodec::flush(bool lastMessageCompleted) {
|
||||
|
||||
// automatic end
|
||||
endMessage(!lastMessageCompleted);
|
||||
|
||||
// flush send buffer
|
||||
flushSendBuffer();
|
||||
|
||||
// start with last header
|
||||
if (!lastMessageCompleted && _lastSegmentedMessageType != 0)
|
||||
@@ -729,9 +736,9 @@ namespace epics {
|
||||
}
|
||||
|
||||
|
||||
// thows io_exception, connection_closed_exception
|
||||
void AbstractCodec::processWrite() {
|
||||
|
||||
// TODO catch ConnectionClosedException, InvalidStreamException?
|
||||
switch (_writeMode)
|
||||
{
|
||||
case PROCESS_SEND_QUEUE:
|
||||
@@ -885,7 +892,7 @@ namespace epics {
|
||||
std::ostringstream msg;
|
||||
msg << "an exception caught while processing a send message: "
|
||||
<< e.what();
|
||||
LOG(logLevelWarn, "%s at %s:%d",
|
||||
LOG(logLevelDebug, "%s at %s:%d",
|
||||
msg.str().c_str(), __FILE__, __LINE__);
|
||||
|
||||
try {
|
||||
@@ -935,6 +942,146 @@ namespace epics {
|
||||
}
|
||||
|
||||
|
||||
bool AbstractCodec::directSerialize(ByteBuffer* /*existingBuffer*/, const char* toSerialize,
|
||||
std::size_t elementCount, std::size_t elementSize)
|
||||
{
|
||||
// TODO overflow check of "size_t count", overflow int32 field of payloadSize header field
|
||||
// TODO max message size in connection validation
|
||||
std::size_t count = elementCount * elementSize;
|
||||
|
||||
// TODO find smart limit
|
||||
// check if direct mode actually pays off
|
||||
if (count < 64*1024)
|
||||
return false;
|
||||
|
||||
//
|
||||
// first end current message, and write a header of next "directly serialized" message
|
||||
//
|
||||
|
||||
// first end current message indicating the we will segment
|
||||
endMessage(true);
|
||||
|
||||
// append segmented message header with payloadSize == count
|
||||
// TODO size_t to int32
|
||||
startMessage(_lastSegmentedMessageCommand, 0, static_cast<int32>(count));
|
||||
|
||||
// flush
|
||||
flushSendBuffer();
|
||||
|
||||
// TODO think if alignment is preserved after...
|
||||
|
||||
//
|
||||
// send toSerialize buffer
|
||||
//
|
||||
ByteBuffer wrappedBuffer(const_cast<char*>(toSerialize), count);
|
||||
send(&wrappedBuffer);
|
||||
|
||||
//
|
||||
// continue where we left before calling directSerialize
|
||||
//
|
||||
startMessage(_lastSegmentedMessageCommand, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbstractCodec::directDeserialize(ByteBuffer *existingBuffer, char* deserializeTo,
|
||||
std::size_t elementCount, std::size_t elementSize)
|
||||
{
|
||||
return false;
|
||||
// _socketBuffer == existingBuffer
|
||||
/*
|
||||
std::size_t bytesToBeDeserialized = elementCount*elementSize;
|
||||
|
||||
// TODO check if bytesToDeserialized < threshold that direct pays off?
|
||||
|
||||
_directPayloadRead = bytesToBeDeserialized;
|
||||
_directBuffer = deserializeTo;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// retrieve what's already in buffers
|
||||
size_t availableBytes = min(_directPayloadRead, _socketBuffer->getRemaining());
|
||||
existingBuffer->getArray(_directBuffer, availableBytes);
|
||||
_directPayloadRead -= availableBytes;
|
||||
|
||||
if (_directPayloadRead == 0)
|
||||
return true;
|
||||
|
||||
_directBuffer += availableBytes;
|
||||
|
||||
// subtract what was already processed
|
||||
size_t pos = _socketBuffer->getPosition();
|
||||
_storedPayloadSize -= pos -_storedPosition;
|
||||
_storedPosition = pos;
|
||||
|
||||
// no more data and we have some payload left => read buffer
|
||||
if (likely(_storedPayloadSize > 0))
|
||||
{
|
||||
size_t bytesToRead = std::min(_directPayloadRead, _storedPayloadSize);
|
||||
processReadIntoDirectBuffer(bytesToRead);
|
||||
// std::cout << "d: " << bytesToRead << std::endl;
|
||||
_storedPayloadSize -= bytesToRead;
|
||||
_directPayloadRead -= bytesToRead;
|
||||
}
|
||||
|
||||
if (_directPayloadRead == 0)
|
||||
return true;
|
||||
|
||||
_stage = PROCESS_HEADER;
|
||||
processReadCached(true, UNDEFINED_STAGE, _directPayloadRead);
|
||||
|
||||
_storedPosition = _socketBuffer->getPosition();
|
||||
_storedLimit = _socketBuffer->getLimit();
|
||||
_socketBuffer->setLimit(
|
||||
min(_storedPosition + _storedPayloadSize, _storedLimit)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
/*
|
||||
void AbstractCodec::processReadIntoDirectBuffer(size_t bytesToRead)
|
||||
{
|
||||
while (bytesToRead > 0)
|
||||
{
|
||||
ssize_t bytesRead = recv(_channel, _directBuffer, bytesToRead, 0);
|
||||
|
||||
// std::cout << "d: " << bytesRead << std::endl;
|
||||
|
||||
if(unlikely(bytesRead<=0))
|
||||
{
|
||||
|
||||
if (bytesRead<0)
|
||||
{
|
||||
int socketError = SOCKERRNO;
|
||||
|
||||
// interrupted or timeout
|
||||
if (socketError == EINTR ||
|
||||
socketError == EAGAIN ||
|
||||
socketError == EWOULDBLOCK)
|
||||
continue;
|
||||
}
|
||||
|
||||
// error (disconnect, end-of-stream) detected
|
||||
close();
|
||||
|
||||
THROW_BASE_EXCEPTION("bytesRead < 0");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bytesToRead -= bytesRead;
|
||||
_directBuffer += bytesRead;
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
@@ -1018,10 +1165,16 @@ namespace epics {
|
||||
{
|
||||
try {
|
||||
bac->processRead();
|
||||
} catch (io_exception &e) {
|
||||
} catch (connection_closed_exception &cce) {
|
||||
// noop
|
||||
} catch (std::exception &e) {
|
||||
LOG(logLevelWarn,
|
||||
"an exception caught while in receiveThread at %s:%d: %s",
|
||||
"an exception caught while in sendThread at %s:%d: %s",
|
||||
__FILE__, __LINE__, e.what());
|
||||
} catch (...) {
|
||||
LOG(logLevelError,
|
||||
"unknown exception caught while in sendThread at %s:%d",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1041,10 +1194,16 @@ namespace epics {
|
||||
{
|
||||
try {
|
||||
bac->processWrite();
|
||||
} catch (io_exception &e) {
|
||||
} catch (connection_closed_exception &cce) {
|
||||
// noop
|
||||
} catch (std::exception &e) {
|
||||
LOG(logLevelWarn,
|
||||
"an exception caught while in sendThread at %s:%d: %s",
|
||||
__FILE__, __LINE__, e.what());
|
||||
} catch (...) {
|
||||
LOG(logLevelError,
|
||||
"unknown exception caught while in sendThread at %s:%d",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1143,6 +1302,8 @@ namespace epics {
|
||||
|
||||
// NOTE: do not log here, you might override SOCKERRNO relevant to recv() operation above
|
||||
|
||||
// TODO winsock return 0 on disconnect (blocking socket) ?
|
||||
|
||||
if(unlikely(bytesSent<0)) {
|
||||
|
||||
int socketError = SOCKERRNO;
|
||||
@@ -1150,6 +1311,8 @@ namespace epics {
|
||||
// spurious EINTR check
|
||||
if (socketError==SOCK_EINTR)
|
||||
continue;
|
||||
else if (socketError==SOCK_ENOBUFS)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (bytesSent > 0) {
|
||||
@@ -1211,10 +1374,11 @@ namespace epics {
|
||||
{
|
||||
int socketError = SOCKERRNO;
|
||||
|
||||
// TODO SOCK_ENOBUFS, for read?
|
||||
// interrupted or timeout
|
||||
if (socketError == EINTR ||
|
||||
if (socketError == SOCK_EINTR ||
|
||||
socketError == EAGAIN ||
|
||||
socketError == EWOULDBLOCK)
|
||||
socketError == SOCK_EWOULDBLOCK)
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1229,6 +1393,12 @@ namespace epics {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BlockingServerTCPTransportCodec::BlockingServerTCPTransportCodec(
|
||||
Context::shared_pointer const & context,
|
||||
SOCKET channel,
|
||||
@@ -1372,7 +1542,7 @@ namespace epics {
|
||||
int32_t sendBufferSize,
|
||||
int32_t receiveBufferSize,
|
||||
TransportClient::shared_pointer const & client,
|
||||
epics::pvData::int8 remoteTransportRevision,
|
||||
epics::pvData::int8 /*remoteTransportRevision*/,
|
||||
float beaconInterval,
|
||||
int16_t priority ) :
|
||||
BlockingTCPTransportCodec(context, channel, responseHandler,
|
||||
|
||||
+17
-23
@@ -244,7 +244,8 @@ namespace epics {
|
||||
void alignData(std::size_t alignment);
|
||||
void startMessage(
|
||||
epics::pvData::int8 command,
|
||||
std::size_t ensureCapacity);
|
||||
std::size_t ensureCapacity = 0,
|
||||
epics::pvData::int32 payloadSize = 0);
|
||||
void putControlMessage(
|
||||
epics::pvData::int8 command,
|
||||
epics::pvData::int32 data);
|
||||
@@ -265,10 +266,21 @@ namespace epics {
|
||||
|
||||
static std::size_t alignedValue(std::size_t value, std::size_t alignment);
|
||||
|
||||
bool directSerialize(
|
||||
epics::pvData::ByteBuffer * /*existingBuffer*/,
|
||||
const char* /*toSerialize*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/);
|
||||
|
||||
|
||||
bool directDeserialize(epics::pvData::ByteBuffer * /*existingBuffer*/,
|
||||
char* /*deserializeTo*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void sendBufferFull(int tries) = 0;
|
||||
void send(epics::pvData::ByteBuffer *buffer);
|
||||
virtual void sendBufferFull(int tries) = 0;
|
||||
void send(epics::pvData::ByteBuffer *buffer);
|
||||
void flushSendBuffer();
|
||||
|
||||
|
||||
ReadMode _readMode;
|
||||
@@ -487,24 +499,6 @@ namespace epics {
|
||||
}
|
||||
|
||||
|
||||
bool directSerialize(
|
||||
epics::pvData::ByteBuffer * /*existingBuffer*/,
|
||||
const char* /*toSerialize*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/)
|
||||
{
|
||||
// TODO !!!!
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool directDeserialize(epics::pvData::ByteBuffer * /*existingBuffer*/,
|
||||
char* /*deserializeTo*/,
|
||||
std::size_t /*elementCount*/, std::size_t /*elementSize*/) {
|
||||
// TODO !!!
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void flushSendQueue() { };
|
||||
|
||||
|
||||
@@ -586,7 +580,7 @@ namespace epics {
|
||||
|
||||
public:
|
||||
|
||||
bool acquire(std::tr1::shared_ptr<TransportClient> const & client)
|
||||
bool acquire(std::tr1::shared_ptr<TransportClient> const & /*client*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -621,7 +615,7 @@ namespace epics {
|
||||
// noop
|
||||
}
|
||||
|
||||
bool verify(epics::pvData::int32 timeoutMs) {
|
||||
bool verify(epics::pvData::int32 /*timeoutMs*/) {
|
||||
TransportSender::shared_pointer transportSender =
|
||||
std::tr1::dynamic_pointer_cast<TransportSender>(shared_from_this());
|
||||
enqueueSendRequest(transportSender);
|
||||
|
||||
+1
-1
@@ -122,7 +122,7 @@ namespace epics {
|
||||
|
||||
virtual ~TransportSendControl() {}
|
||||
|
||||
virtual void startMessage(epics::pvData::int8 command, std::size_t ensureCapacity) = 0;
|
||||
virtual void startMessage(epics::pvData::int8 command, std::size_t ensureCapacity, epics::pvData::int32 payloadSize = 0) = 0;
|
||||
virtual void endMessage() = 0;
|
||||
|
||||
virtual void flush(bool lastMessageCompleted) = 0;
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
void endMessage() {}
|
||||
void flush(bool /*lastMessageCompleted*/) {}
|
||||
void setRecipient(const osiSockAddr& /*sendTo*/) {}
|
||||
void startMessage(epics::pvData::int8 /*command*/, std::size_t /*ensureCapacity*/) {}
|
||||
void startMessage(epics::pvData::int8 /*command*/, std::size_t /*ensureCapacity*/, epics::pvData::int32 /*payloadSize*/) {}
|
||||
void ensureBuffer(std::size_t /*size*/) {}
|
||||
void alignBuffer(std::size_t /*alignment*/) {}
|
||||
void flushSerializeBuffer() {}
|
||||
|
||||
@@ -97,7 +97,7 @@ class ChannelRPCRequesterImpl : public ChannelRPCRequester
|
||||
}
|
||||
}
|
||||
|
||||
virtual void requestDone (const epics::pvData::Status &status, epics::pvData::PVStructure::shared_pointer const &pvResponse)
|
||||
virtual void requestDone(const epics::pvData::Status &status, epics::pvData::PVStructure::shared_pointer const &pvResponse)
|
||||
{
|
||||
if (status.isSuccess())
|
||||
{
|
||||
@@ -112,23 +112,22 @@ class ChannelRPCRequesterImpl : public ChannelRPCRequester
|
||||
Lock lock(m_pointerMutex);
|
||||
|
||||
response = pvResponse;
|
||||
// this is OK since calle holds also owns it
|
||||
// this is OK since calle holds reference to it
|
||||
m_channelRPC.reset();
|
||||
}
|
||||
|
||||
m_event.signal();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "[" << m_channelName << "] failed to RPC: " << status.toString() << std::endl;
|
||||
{
|
||||
Lock lock(m_pointerMutex);
|
||||
// this is OK since caller holds also owns it
|
||||
// this is OK since calle holds reference to it
|
||||
m_channelRPC.reset();
|
||||
}
|
||||
}
|
||||
|
||||
m_event.signal();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -244,8 +243,6 @@ PVStructure::shared_pointer RPCClientImpl::request(PVStructure::shared_pointer p
|
||||
|
||||
bool allOK = true;
|
||||
|
||||
//ClientFactory::start();
|
||||
|
||||
if (m_connected || connect(timeOut))
|
||||
{
|
||||
shared_ptr<ChannelRPCRequesterImpl> rpcRequesterImpl(new ChannelRPCRequesterImpl(m_channel->getChannelName()));
|
||||
@@ -275,8 +272,6 @@ PVStructure::shared_pointer RPCClientImpl::request(PVStructure::shared_pointer p
|
||||
throw epics::pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, errMsg);
|
||||
}
|
||||
|
||||
//ClientFactory::stop();
|
||||
|
||||
if (!allOK)
|
||||
{
|
||||
throw epics::pvAccess::RPCRequestException(Status::STATUSTYPE_ERROR, "RPC request failed");
|
||||
@@ -289,6 +284,8 @@ PVStructure::shared_pointer RPCClientImpl::request(PVStructure::shared_pointer p
|
||||
|
||||
RPCClient::shared_pointer RPCClientFactory::create(const std::string & serviceName)
|
||||
{
|
||||
ClientFactory::start();
|
||||
|
||||
return RPCClient::shared_pointer(new RPCClientImpl(serviceName));
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
# undef rpcClientEpicsExportSharedSymbols
|
||||
#endif
|
||||
|
||||
#include <pv/rpcService.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
namespace epics
|
||||
@@ -43,6 +45,7 @@ namespace pvAccess
|
||||
* @param pvArgument the argument for the rpc
|
||||
* @param timeout the time in seconds to wait for the response
|
||||
* @return request response.
|
||||
* @throws RPCRequestException exception thrown on error on timeout.
|
||||
*/
|
||||
virtual epics::pvData::PVStructure::shared_pointer request(epics::pvData::PVStructure::shared_pointer pvRequest,
|
||||
double timeOut) = 0;
|
||||
@@ -52,7 +55,7 @@ namespace pvAccess
|
||||
|
||||
|
||||
|
||||
class RPCClientFactory
|
||||
class epicsShareClass RPCClientFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
@@ -67,13 +70,13 @@ namespace pvAccess
|
||||
/**
|
||||
* Performs complete blocking RPC call, opening a channel and connecting to the
|
||||
* service and sending the request.
|
||||
* The PVStructure sent on connection is null.
|
||||
*
|
||||
* @param serviceName the name of the service to connect to
|
||||
* @param request the request sent to the service
|
||||
* @return the result of the RPC call.
|
||||
* @throws RPCRequestException exception thrown on error on timeout.
|
||||
*/
|
||||
epics::pvData::PVStructure::shared_pointer sendRequest(const std::string & serviceName,
|
||||
epicsShareExtern epics::pvData::PVStructure::shared_pointer sendRequest(const std::string & serviceName,
|
||||
epics::pvData::PVStructure::shared_pointer request, double timeOut);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ static void stopPVAClientRegister(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
epicsExportRegistrar(startPVAClientRegister);
|
||||
epicsExportRegistrar(stopPVAClientRegister);
|
||||
extern "C" {
|
||||
epicsExportRegistrar(startPVAClientRegister);
|
||||
epicsExportRegistrar(stopPVAClientRegister);
|
||||
}
|
||||
|
||||
@@ -125,5 +125,7 @@ static void stopPVAServerRegister(void)
|
||||
}
|
||||
}
|
||||
|
||||
epicsExportRegistrar(startPVAServerRegister);
|
||||
epicsExportRegistrar(stopPVAServerRegister);
|
||||
extern "C" {
|
||||
epicsExportRegistrar(startPVAServerRegister);
|
||||
epicsExportRegistrar(stopPVAServerRegister);
|
||||
}
|
||||
|
||||
@@ -81,6 +81,10 @@ PROD_HOST += rpcServiceExample
|
||||
rpcServiceExample_SRCS += rpcServiceExample.cpp
|
||||
rpcServiceExample_LIBS += pvAccess pvData pvMB Com
|
||||
|
||||
PROD_HOST += rpcClientExample
|
||||
rpcClientExample_SRCS += rpcClientExample.cpp
|
||||
rpcClientExample_LIBS += pvAccess pvData pvMB Com
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
@@ -43,7 +43,7 @@ std::string ChannelAccessIFTest::TEST_ARRAY_CHANNEL_NAME = "testArray1";
|
||||
int ChannelAccessIFTest::runAllTest() {
|
||||
|
||||
#ifdef ENABLE_STRESS_TESTS
|
||||
testPlan(157);
|
||||
testPlan(158);
|
||||
#else
|
||||
testPlan(152);
|
||||
#endif
|
||||
@@ -98,6 +98,7 @@ int ChannelAccessIFTest::runAllTest() {
|
||||
test_channelArrayTestNoConnection();
|
||||
|
||||
#ifdef ENABLE_STRESS_TESTS
|
||||
test_stressPutAndGetLargeArray();
|
||||
test_stressConnectDisconnect();
|
||||
test_stressConnectGetDisconnect();
|
||||
test_stressMonitorAndProcess();
|
||||
@@ -1990,6 +1991,85 @@ void ChannelAccessIFTest::test_channelArray_destroy() {
|
||||
}
|
||||
|
||||
|
||||
void ChannelAccessIFTest::test_stressPutAndGetLargeArray() {
|
||||
|
||||
testDiag("BEGIN TEST %s:", CURRENT_FUNCTION);
|
||||
|
||||
string request = "field(value)";
|
||||
size_t minSize = 1;
|
||||
size_t step = 25471;
|
||||
size_t maxSize = 8*1024*1024; // 8MB
|
||||
bool debug = false;
|
||||
|
||||
Channel::shared_pointer channel = syncCreateChannel(TEST_ARRAY_CHANNEL_NAME);
|
||||
if (!channel.get()) {
|
||||
testFail("%s: channel not created ", CURRENT_FUNCTION);
|
||||
return;
|
||||
}
|
||||
|
||||
SyncChannelPutRequesterImpl::shared_pointer putReq =
|
||||
syncCreateChannelPut(channel, request, debug);
|
||||
if (!putReq.get()) {
|
||||
testFail("%s: creating a channel put failed ", CURRENT_FUNCTION);
|
||||
return;
|
||||
}
|
||||
|
||||
PVDoubleArray::shared_pointer value = putReq->getPVStructure()->getSubField<PVDoubleArray>("value");
|
||||
if (!value.get()) {
|
||||
testFail("%s: getting double array value field failed ", CURRENT_FUNCTION);
|
||||
return;
|
||||
}
|
||||
|
||||
PVDoubleArray::svector newdata;
|
||||
|
||||
for (size_t len = minSize; len <= maxSize; len+=step) {
|
||||
|
||||
//testDiag("%s: array size %zd", CURRENT_FUNCTION, len);
|
||||
|
||||
// prepare data
|
||||
newdata.resize(len);
|
||||
for (size_t i = 0; i < len; i++)
|
||||
newdata[i] = i;
|
||||
|
||||
value->replace(freeze(newdata));
|
||||
putReq->getBitSet()->set(value->getFieldOffset());
|
||||
|
||||
bool succStatus = putReq->syncPut(false, getTimeoutSec());
|
||||
if (!succStatus) {
|
||||
testFail("%s: sync put failed at array size %zd", CURRENT_FUNCTION, len);
|
||||
return;
|
||||
}
|
||||
|
||||
succStatus = putReq->syncGet(getTimeoutSec());
|
||||
if (!succStatus) {
|
||||
testFail("%s: sync get failed at array size %zd", CURRENT_FUNCTION, len);
|
||||
return;
|
||||
}
|
||||
|
||||
// length check
|
||||
if (value->getLength() != len) {
|
||||
testFail("%s: length does not math %zd != %zd", CURRENT_FUNCTION, len, value->getLength());
|
||||
return;
|
||||
}
|
||||
|
||||
// value check
|
||||
PVDoubleArray::const_svector data(value->view());
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (i != data[i]) // NOTE: floating-point value comparison without delta
|
||||
testFail("%s: data slot %zd does not match %f != %f at array size %zd", CURRENT_FUNCTION, i, (double)i, data[i], len);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
channel->destroy();
|
||||
|
||||
testOk(true, "%s: stress test put-and-get large array successfull", CURRENT_FUNCTION);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ChannelAccessIFTest::test_stressConnectDisconnect() {
|
||||
|
||||
testDiag("BEGIN TEST %s:", CURRENT_FUNCTION);
|
||||
|
||||
@@ -141,6 +141,8 @@ class ChannelAccessIFTest {
|
||||
|
||||
void test_channelArrayTestNoConnection();
|
||||
|
||||
void test_stressPutAndGetLargeArray();
|
||||
|
||||
void test_stressConnectDisconnect();
|
||||
|
||||
void test_stressConnectGetDisconnect();
|
||||
|
||||
+1990
-1990
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* 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 <pv/pvData.h>
|
||||
#include <pv/rpcClient.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
|
||||
static StructureConstPtr requestStructure =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
add("a", pvString)->
|
||||
add("b", pvString)->
|
||||
createStructure();
|
||||
|
||||
#define TIMEOUT 3.0
|
||||
|
||||
int main()
|
||||
{
|
||||
PVStructure::shared_pointer request = getPVDataCreate()->createPVStructure(requestStructure);
|
||||
request->getSubField<PVString>("a")->put("3.14");
|
||||
request->getSubField<PVString>("b")->put("2.71");
|
||||
|
||||
RPCClient::shared_pointer client = RPCClientFactory::create("sum");
|
||||
|
||||
try
|
||||
{
|
||||
PVStructure::shared_pointer result = client->request(request, TIMEOUT);
|
||||
std::cout << *result << std::endl;
|
||||
} catch (RPCRequestException &e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -4,26 +4,16 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
*/
|
||||
|
||||
#include <pv/convert.h>
|
||||
#include <pv/pvData.h>
|
||||
#include <pv/rpcServer.h>
|
||||
|
||||
using namespace epics::pvData;
|
||||
using namespace epics::pvAccess;
|
||||
|
||||
|
||||
|
||||
static StructureConstPtr createResultField()
|
||||
{
|
||||
FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
|
||||
StringArray fieldNames;
|
||||
fieldNames.push_back("c");
|
||||
FieldConstPtrArray fields;
|
||||
fields.push_back(fieldCreate->createScalar(pvDouble));
|
||||
return fieldCreate->createStructure(fieldNames, fields);
|
||||
}
|
||||
|
||||
static StructureConstPtr resultStructure = createResultField();
|
||||
static StructureConstPtr resultStructure =
|
||||
getFieldCreate()->createFieldBuilder()->
|
||||
add("c", pvDouble)->
|
||||
createStructure();
|
||||
|
||||
class SumServiceImpl :
|
||||
public RPCService
|
||||
@@ -31,12 +21,16 @@ class SumServiceImpl :
|
||||
PVStructure::shared_pointer request(PVStructure::shared_pointer const & args)
|
||||
throw (RPCRequestException)
|
||||
{
|
||||
// TODO error handling
|
||||
double a = atof(args->getStringField("a")->get().c_str());
|
||||
double b = atof(args->getStringField("b")->get().c_str());
|
||||
PVString::shared_pointer fa = args->getSubField<PVString>("a");
|
||||
PVString::shared_pointer fb = args->getSubField<PVString>("b");
|
||||
if (!fa || !fb)
|
||||
throw RPCRequestException(Status::STATUSTYPE_ERROR, "'string a' and 'string b' fields required");
|
||||
|
||||
double a = atof(fa->get().c_str());
|
||||
double b = atof(fb->get().c_str());
|
||||
|
||||
PVStructure::shared_pointer result = getPVDataCreate()->createPVStructure(resultStructure);
|
||||
result->getDoubleField("c")->put(a+b);
|
||||
result->getSubField<PVDouble>("c")->put(a+b);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -318,7 +318,7 @@ void SimADC::run()
|
||||
|
||||
cycle();
|
||||
}
|
||||
printf("SimADC shutdown\n");
|
||||
//printf("SimADC shutdown\n");
|
||||
}
|
||||
|
||||
SimADC::smart_pointer_type createSimADC(const std::string& name)
|
||||
|
||||
@@ -77,8 +77,9 @@ class ChannelAccessIFRemoteTest: public ChannelAccessIFTest {
|
||||
~ChannelAccessIFRemoteTest() {
|
||||
m_serverContextAction.stop();
|
||||
ClientFactory::stop();
|
||||
structureChangedListeners.clear();
|
||||
structureStore.clear();
|
||||
|
||||
// shutdown SIGSEG problems
|
||||
epicsThreadSleep(2.0);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -199,16 +199,9 @@ public:
|
||||
epicsTimeStamp endTime;
|
||||
epicsTimeGetCurrent(&endTime);
|
||||
|
||||
|
||||
long seconds, nseconds;
|
||||
double duration;
|
||||
seconds = endTime.secPastEpoch - startTime.secPastEpoch;
|
||||
nseconds = endTime.nsec - startTime.nsec;
|
||||
|
||||
duration = seconds + nseconds/1000000000.0;
|
||||
|
||||
double duration = epicsTime(endTime) - epicsTime(startTime);
|
||||
double getPerSec = iterations*channels/duration;
|
||||
double gbit = getPerSec*arraySize*sizeof(double)*8/(1000*1000*1000); // * bits / giga; NO, it's really 1000 and not 102:
|
||||
double gbit = getPerSec*arraySize*sizeof(double)*8/(1000*1000*1000); // * bits / giga; NO, it's really 1000 and not 1024
|
||||
if (verbose)
|
||||
printf("%5.6f seconds, %.3f (x %d = %.3f) gets/s, data throughput %5.3f Gbits/s\n",
|
||||
duration, iterations/duration, channels, getPerSec, gbit);
|
||||
|
||||
@@ -2617,9 +2617,16 @@ void testServer(int timeToRun)
|
||||
unregisterChannelProviderFactory(factory);
|
||||
|
||||
structureChangedListeners.clear();
|
||||
structureStore.clear();
|
||||
|
||||
{
|
||||
Lock guard(structureStoreMutex);
|
||||
structureStore.clear();
|
||||
}
|
||||
ctx.reset();
|
||||
|
||||
unregisterChannelProviderFactory(factory);
|
||||
|
||||
|
||||
shutdownSimADCs();
|
||||
}
|
||||
|
||||
void testServerShutdown()
|
||||
@@ -2690,8 +2697,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
testServer(timeToRun);
|
||||
|
||||
shutdownSimADCs();
|
||||
|
||||
cout << "Done" << endl;
|
||||
|
||||
if (cleanupAndReport)
|
||||
|
||||
Reference in New Issue
Block a user