From 224a0f6d9baddb42997dd63a07d3197fa39c271a Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 11 Feb 2014 10:12:24 +0100 Subject: [PATCH 01/19] flow: Created branch 'feature/codec'. From ae73e7c2ed86c34893359be0db205d46a5843957 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 11 Feb 2014 11:17:14 +0100 Subject: [PATCH 02/19] codec implementation with lots of tests commited --- pvAccessApp/Makefile | 2 + pvAccessApp/remote/blockingTCPAcceptor.cpp | 16 +- pvAccessApp/remote/codec.cpp | 1568 ++++++++++ pvAccessApp/remote/codec.h | 807 +++++ testApp/remote/Makefile | 6 + testApp/remote/channelAccessIFTest.cpp | 12 +- testApp/remote/testChannelAccess.cpp | 1 + testApp/remote/testCodec.cpp | 3195 ++++++++++++++++++++ 8 files changed, 5599 insertions(+), 8 deletions(-) create mode 100644 pvAccessApp/remote/codec.cpp create mode 100644 pvAccessApp/remote/codec.h create mode 100644 testApp/remote/testCodec.cpp diff --git a/pvAccessApp/Makefile b/pvAccessApp/Makefile index 41fbdc5..fc2a966 100644 --- a/pvAccessApp/Makefile +++ b/pvAccessApp/Makefile @@ -45,6 +45,7 @@ INC += channelSearchManager.h INC += simpleChannelSearchManagerImpl.h INC += transportRegistry.h INC += serializationHelper.h +INC += codec.h LIBSRCS += blockingUDPTransport.cpp LIBSRCS += blockingUDPConnector.cpp LIBSRCS += beaconHandler.cpp @@ -57,6 +58,7 @@ LIBSRCS += abstractResponseHandler.cpp LIBSRCS += blockingTCPAcceptor.cpp LIBSRCS += transportRegistry.cpp LIBSRCS += serializationHelper.cpp +LIBSRCS += codec.cpp SRC_DIRS += $(PVACCESS)/remoteClient INC += clientContextImpl.h diff --git a/pvAccessApp/remote/blockingTCPAcceptor.cpp b/pvAccessApp/remote/blockingTCPAcceptor.cpp index becc4f1..caf7a2a 100644 --- a/pvAccessApp/remote/blockingTCPAcceptor.cpp +++ b/pvAccessApp/remote/blockingTCPAcceptor.cpp @@ -5,6 +5,7 @@ */ #include +#include "codec.h" #include #include @@ -180,17 +181,28 @@ namespace pvAccess { } // TODO tune buffer sizes?! + + // get TCP send buffer size + osiSocklen_t intLen = sizeof(int); + int _socketSendBufferSize; + retval = getsockopt(newClient, SOL_SOCKET, SO_SNDBUF, (char *)&_socketSendBufferSize, &intLen); + if(retval<0) { + epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); + LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer); + } + /** * Create transport, it registers itself to the registry. * Each transport should have its own response handler since it is not "shareable" */ std::auto_ptr responseHandler = _responseHandlerFactory->createResponseHandler(); - BlockingServerTCPTransport::shared_pointer transport = - BlockingServerTCPTransport::create( + BlockingServerTCPTransportCodec::shared_pointer transport = + BlockingServerTCPTransportCodec::create( _context, newClient, responseHandler, + _socketSendBufferSize, _receiveBufferSize); // validate connection diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp new file mode 100644 index 0000000..e85818d --- /dev/null +++ b/pvAccessApp/remote/codec.cpp @@ -0,0 +1,1568 @@ +/** +* 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. +*/ +#ifdef _WIN32 +#define NOMINMAX +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + + +#include + +using namespace epics::pvData; +using namespace epics::pvAccess; + + +namespace epics { + namespace pvAccess { + + const std::size_t AbstractCodec::MAX_MESSAGE_PROCESS = 100; + const std::size_t AbstractCodec::MAX_MESSAGE_SEND = 100; + const std::size_t AbstractCodec::MAX_ENSURE_SIZE = 1024; + const std::size_t AbstractCodec::MAX_ENSURE_DATA_SIZE = MAX_ENSURE_SIZE/2; + const std::size_t AbstractCodec::MAX_ENSURE_BUFFER_SIZE = MAX_ENSURE_SIZE; + const std::size_t AbstractCodec::MAX_ENSURE_DATA_BUFFER_SIZE = 1024; + + AbstractCodec::AbstractCodec( + ByteBuffer *receiveBuffer, + ByteBuffer *sendBuffer, + int32_t socketSendBufferSize, + bool blockingProcessQueue): + //PROTECTED + _readMode(NORMAL), _version(0), _flags(0), _command(0), _payloadSize(0), + _remoteTransportSocketReceiveBufferSize(MAX_TCP_RECV), _totalBytesSent(0), + _blockingProcessQueue(false), _senderThread(0), + _writeMode(PROCESS_SEND_QUEUE), + _writeOpReady(false),_lowLatency(false), + //PRIVATE + _storedPayloadSize(0), _storedPosition(0), _startPosition(0), + _maxSendPayloadSize(0), + _lastMessageStartPosition(0),_lastSegmentedMessageType(0), + _lastSegmentedMessageCommand(0), _nextMessagePayloadOffset(0), + _byteOrderFlag(0x80),_socketSendBufferSize(0) + { + if (receiveBuffer->getSize() < 2*MAX_ENSURE_SIZE) + throw std::invalid_argument( + "receiveBuffer.capacity() < 2*MAX_ENSURE_SIZE"); + + // require aligned buffer size + //(not condition, but simplifies alignment code) + + if (receiveBuffer->getSize() % PVA_ALIGNMENT != 0) + throw std::invalid_argument( + "receiveBuffer.capacity() % PVAConstants.PVA_ALIGNMENT != 0"); + + if (sendBuffer->getSize() < 2*MAX_ENSURE_SIZE) + throw std::invalid_argument("sendBuffer() < 2*MAX_ENSURE_SIZE"); + + // require aligned buffer size + //(not condition, but simplifies alignment code) + if (sendBuffer->getSize() % PVA_ALIGNMENT != 0) + throw std::invalid_argument( + "sendBuffer() % PVAConstants.PVA_ALIGNMENT != 0"); + + _socketBuffer.reset(receiveBuffer); + _sendBuffer.reset(sendBuffer); + + // initialize to be empty + _socketBuffer->setPosition(_socketBuffer->getLimit()); + _startPosition = _socketBuffer->getPosition(); + + // clear send + _sendBuffer->clear(); + + // start msg + control + _maxSendPayloadSize = + _sendBuffer->getSize() - 2*PVA_MESSAGE_HEADER_SIZE; + _socketSendBufferSize = socketSendBufferSize; + _blockingProcessQueue = blockingProcessQueue; + LOG(logLevelTrace, "AbstractCodec constructed (threadId: %u)", + epicsThreadGetIdSelf()); + } + + + void AbstractCodec::processRead() { + + LOG(logLevelTrace, "AbstractCodec::processRead: enter (threadId: %u)", + epicsThreadGetIdSelf()); + + switch (_readMode) + { + case NORMAL: + processReadNormal(); + break; + case SEGMENTED: + processReadSegmented(); + break; + case SPLIT: + throw std::logic_error("SPLIT NOT SUPPORTED"); + } + + } + + + void AbstractCodec::processHeader() { + + LOG(logLevelTrace, "AbstractCodec::processHeader enter (threadId: %u)", + epicsThreadGetIdSelf()); + + + // magic code + int8_t magicCode = _socketBuffer->getByte(); + + // version + _version = _socketBuffer->getByte(); + + // flags + _flags = _socketBuffer->getByte(); + + // command + _command = _socketBuffer->getByte(); + + // read payload size + _payloadSize = _socketBuffer->getInt(); + + // check magic code + if (magicCode != PVA_MAGIC) + { + LOG(logLevelError, + "Invalid header received from the client at %s:%d: %d," + " disconnecting...", + __FILE__, __LINE__, getLastReadBufferSocketAddress()); + invalidDataStreamHandler(); + throw invalid_data_stream_exception("invalid header received"); + } + + } + + + void AbstractCodec::processReadNormal() { + + LOG(logLevelTrace, + "AbstractCodec::processReadNormal enter (threadId: %u)", + epicsThreadGetIdSelf()); + + try + { + std::size_t messageProcessCount = 0; + while (messageProcessCount++ < MAX_MESSAGE_PROCESS) + { + // read as much as available, but at least for a header + // readFromSocket checks if reading from socket is really necessary + if (!readToBuffer(PVA_MESSAGE_HEADER_SIZE, false)) { + return; + } + + // read header fields + processHeader(); + bool isControl = ((_flags & 0x01) == 0x01); + if (isControl) { + processControlMessage(); + } + else + { + // segmented sanity check + bool notFirstSegment = (_flags & 0x20) != 0; + if (notFirstSegment) + { + LOG(logLevelWarn, + "Not-a-frst segmented message received in normal mode" + " from the client at %s:%d: %d, disconnecting...", + __FILE__, __LINE__, getLastReadBufferSocketAddress()); + invalidDataStreamHandler(); + throw invalid_data_stream_exception( + "not-a-first segmented message received in normal mode"); + } + + _storedPayloadSize = _payloadSize; + _storedPosition = _socketBuffer->getPosition(); + _storedLimit = _socketBuffer->getLimit(); + _socketBuffer->setLimit(std::min + (_storedPosition + _storedPayloadSize, _storedLimit)); + try + { + // handle response + processApplicationMessage(); + //TODO: MATEJ CHECK + throw simulate_finally_exception("go to finally block"); + } + catch(...) //finally + { + if (!isOpen()) + return; + + // can be closed by now + // isOpen() should be efficiently implemented + while (true) + //while (isOpen()) + { + // set position as whole message was read + //(in case code haven't done so) + std::size_t newPosition = + alignedValue( + _storedPosition + _storedPayloadSize, PVA_ALIGNMENT); + + // aligned buffer size ensures that there is enough space + //in buffer, + // however data might not be fully read + + // discard the rest of the packet + if (newPosition > _storedLimit) + { + // processApplicationMessage() did not read up + //quite some buffer + + // we only handle unused alignment bytes + int bytesNotRead = + newPosition - _socketBuffer->getPosition(); + + if (bytesNotRead < PVA_ALIGNMENT) + { + // make alignment bytes as real payload to enable SPLIT + // no end-of-socket or segmented scenario can happen + // due to aligned buffer size + _storedPayloadSize += bytesNotRead; + // reveal currently existing padding + _socketBuffer->setLimit(_storedLimit); + ensureData(bytesNotRead); + _storedPayloadSize -= bytesNotRead; + continue; + } + + // TODO we do not handle this for now (maybe never) + LOG(logLevelWarn, + "unprocessed read buffer from client at %s:%d: %d," + " disconnecting...", + __FILE__, __LINE__, getLastReadBufferSocketAddress()); + invalidDataStreamHandler(); + throw invalid_data_stream_exception( + "unprocessed read buffer"); + } + _socketBuffer->setLimit(_storedLimit); + _socketBuffer->setPosition(newPosition); + break; + } + } + } + } + + } + catch (invalid_data_stream_exception & ) + { + // noop, should be already handled (and logged) + } + catch (connection_closed_exception & ) + { + // noop, should be already handled (and logged) + } + } + + + void AbstractCodec::processReadSegmented() { + + LOG(logLevelTrace, + "AbstractCodec::processReadSegmented enter (threadId: %u)", + epicsThreadGetIdSelf()); + + while (true) + { + // read as much as available, but at least for a header + // readFromSocket checks if reading from socket is really necessary + readToBuffer(PVA_MESSAGE_HEADER_SIZE, true); + + // read header fields + processHeader(); + + bool isControl = ((_flags & 0x01) == 0x01); + if (isControl) + processControlMessage(); + else + { + // last segment bit set (means in-between segment or last segment) + // we expect this, no non-control messages between + //segmented message are supported + // NOTE: for now... it is easy to support non-semgented + //messages between segmented messages + bool notFirstSegment = (_flags & 0x20) != 0; + if (!notFirstSegment) + { + LOG(logLevelWarn, + "Not-a-first segmented message expected from the client at" + " %s:%d: %d, disconnecting...", + __FILE__, __LINE__, getLastReadBufferSocketAddress()); + invalidDataStreamHandler(); + throw new invalid_data_stream_exception( + "not-a-first segmented message expected"); + } + + _storedPayloadSize = _payloadSize; + + // return control to caller code + return; + } + } + + } + + + bool AbstractCodec::readToBuffer( + std::size_t requiredBytes, + bool persistent) { + + LOG(logLevelTrace, + "AbstractCodec::readToBuffer enter requiredBytes: %u," + " persistant: %d (threadId: %u)", + requiredBytes, persistent, epicsThreadGetIdSelf()); + + // do we already have requiredBytes available? + std::size_t remainingBytes = _socketBuffer->getRemaining(); + if (remainingBytes >= requiredBytes) { + return true; + } + + // assumption: remainingBytes < MAX_ENSURE_DATA_BUFFER_SIZE && + // requiredBytes < (socketBuffer.capacity() - PVA_ALIGNMENT) + + // + // copy unread part to the beginning of the buffer + // to make room for new data (as much as we can read) + // NOTE: requiredBytes is expected to be small (order of 10 bytes) + // + + // a new start position, we are careful to preserve alignment + _startPosition = + MAX_ENSURE_SIZE + _socketBuffer->getPosition() % PVA_ALIGNMENT; + + std::size_t endPosition = _startPosition + remainingBytes; + + for (std::size_t i = _startPosition; i < endPosition; i++) + _socketBuffer->putByte(i, _socketBuffer->getByte()); + + // update buffer to the new position + _socketBuffer->setLimit(_socketBuffer->getSize()); + _socketBuffer->setPosition(endPosition); + + // read at least requiredBytes bytes + std::size_t requiredPosition = _startPosition + requiredBytes; + while (_socketBuffer->getPosition() < requiredPosition) + { + int bytesRead = read(_socketBuffer.get()); + + LOG(logLevelTrace, + "AbstractCodec::readToBuffer READ BYTES: %d (threadId: %u)", + bytesRead, epicsThreadGetIdSelf()); + + if (bytesRead < 0) + { + close(); + throw connection_closed_exception("bytesRead < 0"); + } + // non-blocking IO support + else if (bytesRead == 0) + { + if (persistent) + readPollOne(); + else + { + // set pointers (aka flip) + _socketBuffer->setLimit(_socketBuffer->getPosition()); + _socketBuffer->setPosition(_startPosition); + + return false; + } + } + } + + // set pointers (aka flip) + _socketBuffer->setLimit(_socketBuffer->getPosition()); + _socketBuffer->setPosition(_startPosition); + + return true; + } + + + void AbstractCodec::ensureData(std::size_t size) { + + LOG(logLevelTrace, + "AbstractCodec::ensureData enter: size: %u (threadId: %u)", + size, epicsThreadGetIdSelf()); + + + // enough of data? + if (_socketBuffer->getRemaining() >= size) + return; + + // to large for buffer... + if (size > MAX_ENSURE_DATA_SIZE) {// half for SPLIT, half for SEGMENTED + std::ostringstream msg; + msg << "requested for buffer size " << size + << ", but maximum " << MAX_ENSURE_DATA_SIZE << " is allowed."; + LOG(logLevelWarn, + "%s at %s:%d,", msg.str().c_str(), __FILE__, __LINE__); + std::string s = msg.str(); + throw std::invalid_argument(s); + } + + try + { + + // subtract what was already processed + std::size_t pos = _socketBuffer->getPosition(); + _storedPayloadSize -= pos - _storedPosition; + + // SPLIT message case + // no more data and we have some payload left => read buffer + // NOTE: (storedPayloadSize >= size) does not work if size + //spans over multiple messages + if (_storedPayloadSize >= (_storedLimit-pos)) + { + // just read up remaining payload + // this will move current (getPosition(); + _storedLimit = _socketBuffer->getLimit(); + _socketBuffer->setLimit( + std::min( + _storedPosition + _storedPayloadSize, _storedLimit)); + + // check needed, if not enough data is available or + // we run into segmented message + ensureData(size); + } + // SEGMENTED message case + else + { + // TODO check flags + //if (flags && SEGMENTED_FLAGS_MASK == 0) + // throw IllegalStateException("segmented message expected, + //but current message flag does not indicate it"); + + + // copy remaining bytes of payload to safe area + //[0 to MAX_ENSURE_DATA_BUFFER_SIZE/2), if any + // remaining is relative to payload since buffer is + //bounded from outside + std::size_t remainingBytes = _socketBuffer->getRemaining(); + for (std::size_t i = 0; i < remainingBytes; i++) + _socketBuffer->putByte(i, _socketBuffer->getByte()); + + // restore limit (there might be some data already present + //and readToBuffer needs to know real limit) + _socketBuffer->setLimit(_storedLimit); + + // remember alignment offset of end of the message (to be restored) + std::size_t storedAlignmentOffset = + _socketBuffer->getPosition() % PVA_ALIGNMENT; + + // skip post-message alignment bytes + if (storedAlignmentOffset > 0) + { + std::size_t toSkip = PVA_ALIGNMENT - storedAlignmentOffset; + readToBuffer(toSkip, true); + std::size_t currentPos = _socketBuffer->getPosition(); + _socketBuffer->setPosition(currentPos + toSkip); + } + + // we expect segmented message, we expect header + // that (and maybe some control packets) needs to be "removed" + // so that we get combined payload + ReadMode storedMode = _readMode; _readMode = SEGMENTED; + processRead(); + _readMode = storedMode; + + // make sure we have all the data (maybe we run into SPLIT) + readToBuffer(size - remainingBytes + storedAlignmentOffset, true); + + // skip storedAlignmentOffset bytes (sender should padded start of + //segmented message) + // SPLIT cannot mess with this, since start of the message, + //i.e. current position, is always aligned + _socketBuffer->setPosition( + _socketBuffer->getPosition() + storedAlignmentOffset); + + // copy before position (i.e. start of the payload) + for (int32_t i = remainingBytes - 1, + j = _socketBuffer->getPosition() - 1; i >= 0; i--, j--) + _socketBuffer->putByte(j, _socketBuffer->getByte(i)); + + _startPosition = _socketBuffer->getPosition() - remainingBytes; + _socketBuffer->setPosition(_startPosition); + + _storedPayloadSize += remainingBytes - storedAlignmentOffset; + _storedPosition = _startPosition; + _storedLimit = _socketBuffer->getLimit(); + _socketBuffer->setLimit( + std::min( + _storedPosition + _storedPayloadSize, _storedLimit)); + + // sequential small segmented messages in the buffer + ensureData(size); + } + } + catch (io_exception &) { + try { + close(); + } catch (io_exception & ) { + // noop, best-effort close + } + throw connection_closed_exception( + "Failed to ensure data to read buffer."); + } + } + + + std::size_t AbstractCodec::alignedValue( + std::size_t value, + std::size_t alignment) { + + LOG(logLevelTrace, + "AbstractCodec::alignedValue enter: value: %u, alignment:%u" + " (threadId: %u)", + value, alignment, epicsThreadGetIdSelf()); + + std::size_t k = (alignment - 1); + return (value + k) & (~k); + } + + + void AbstractCodec::alignData(std::size_t alignment) { + + LOG(logLevelTrace, + "AbstractCodec::alignData enter: alignment:%u (threadId: %u)", + alignment, epicsThreadGetIdSelf()); + + std::size_t k = (alignment - 1); + std::size_t pos = _socketBuffer->getPosition(); + std::size_t newpos = (pos + k) & (~k); + if (pos == newpos) + return; + + std::size_t diff = _socketBuffer->getLimit() - newpos; + if (diff > 0) + { + _socketBuffer->setPosition(newpos); + return; + } + + ensureData(diff); + + // position has changed, recalculate + newpos = (_socketBuffer->getPosition() + k) & (~k); + _socketBuffer->setPosition(newpos); + } + + + void AbstractCodec::alignBuffer(std::size_t alignment) { + + LOG(logLevelTrace, "AbstractCodec::alignBuffer enter:" + " alignment:%u (threadId: %u)", + alignment, epicsThreadGetIdSelf()); + + std::size_t k = (alignment - 1); + std::size_t pos = _sendBuffer->getPosition(); + std::size_t newpos = (pos + k) & (~k); + if (pos == newpos) + return; + + // there is always enough of space + // since sendBuffer capacity % PVA_ALIGNMENT == 0 + _sendBuffer->setPosition(newpos); + } + + + void AbstractCodec::startMessage( + epics::pvData::int8 command, + std::size_t ensureCapacity) { + + LOG(logLevelTrace, + "AbstractCodec::startMessage enter: command:%x " + " ensureCapacity:%u (threadId: %u)", + command, ensureCapacity, epicsThreadGetIdSelf()); + + _lastMessageStartPosition = + std::numeric_limits::max(); // TODO revise this + ensureBuffer( + PVA_MESSAGE_HEADER_SIZE + ensureCapacity + _nextMessagePayloadOffset); + _lastMessageStartPosition = _sendBuffer->getPosition(); + _sendBuffer->putByte(PVA_MAGIC); + _sendBuffer->putByte(PVA_VERSION); + _sendBuffer->putByte( + (_lastSegmentedMessageType | _byteOrderFlag)); // data + endian + _sendBuffer->putByte(command); // command + _sendBuffer->putInt(0); // temporary zero payload + + // apply offset + if (_nextMessagePayloadOffset > 0) + _sendBuffer->setPosition( + _sendBuffer->getPosition() + _nextMessagePayloadOffset); + } + + + void AbstractCodec::putControlMessage( + epics::pvData::int8 command, + epics::pvData::int32 data) { + + LOG(logLevelTrace, + "AbstractCodec::putControlMessage enter: command:%x " + "data:%d (threadId: %u)", + command, data, epicsThreadGetIdSelf()); + + _lastMessageStartPosition = + std::numeric_limits::max(); // TODO revise this + ensureBuffer(PVA_MESSAGE_HEADER_SIZE); + _sendBuffer->putByte(PVA_MAGIC); + _sendBuffer->putByte(PVA_VERSION); + _sendBuffer->putByte((0x01 | _byteOrderFlag)); // control + endian + _sendBuffer->putByte(command); // command + _sendBuffer->putInt(data); // data + } + + + void AbstractCodec::endMessage() { + + LOG(logLevelTrace, "AbstractCodec::endMessage enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + endMessage(false); + } + + + void AbstractCodec::endMessage(bool hasMoreSegments) { + + LOG(logLevelTrace, + "AbstractCodec::endMessage enter: hasMoreSegments:%d (threadId: %u)", + hasMoreSegments, epicsThreadGetIdSelf()); + + if (_lastMessageStartPosition != std::numeric_limits::max()) + { + std::size_t lastPayloadBytePosition = _sendBuffer->getPosition(); + + // align + alignBuffer(PVA_ALIGNMENT); + + // set paylaod size (non-aligned) + std::size_t payloadSize = + lastPayloadBytePosition - + _lastMessageStartPosition - PVA_MESSAGE_HEADER_SIZE; + + _sendBuffer->putInt(_lastMessageStartPosition + 4, payloadSize); + + // set segmented bit + if (hasMoreSegments) { + // first segment + if (_lastSegmentedMessageType == 0) + { + std::size_t flagsPosition = _lastMessageStartPosition + 2; + epics::pvData::int8 type = _sendBuffer->getByte(flagsPosition); + // set first segment bit + _sendBuffer->putByte(flagsPosition, (type | 0x10)); + // first + last segment bit == in-between segment + _lastSegmentedMessageType = type | 0x30; + _lastSegmentedMessageCommand = + _sendBuffer->getByte(flagsPosition + 1); + } + _nextMessagePayloadOffset = lastPayloadBytePosition % PVA_ALIGNMENT; + } + else + { + // last segment + if (_lastSegmentedMessageType != + std::numeric_limits::max()) + { + std::size_t flagsPosition = _lastMessageStartPosition + 2; + // set last segment bit (by clearing first segment bit) + _sendBuffer->putByte(flagsPosition, + (_lastSegmentedMessageType & 0xEF)); + _lastSegmentedMessageType = 0; + } + _nextMessagePayloadOffset = 0; + } + + // TODO + /* + // manage markers + final int position = sendBuffer.position(); + final int bytesLeft = sendBuffer.remaining(); + if (position >= nextMarkerPosition && bytesLeft >= + PVAConstants.PVA_MESSAGE_HEADER_SIZE) + { + sendBuffer.put(PVAConstants.PVA_MAGIC); + sendBuffer.put(PVAConstants.PVA_VERSION); + sendBuffer.put((byte)(0x01 | byteOrderFlag)); // control data + sendBuffer.put((byte)0); // marker + sendBuffer.putInt((int)(totalBytesSent + position + + PVAConstants.PVA_MESSAGE_HEADER_SIZE)); + nextMarkerPosition = position + markerPeriodBytes; + } + */ + _lastMessageStartPosition = std::numeric_limits::max(); + } + } + + void AbstractCodec::ensureBuffer(std::size_t size) { + + LOG(logLevelTrace, + "AbstractCodec::ensureBuffer enter: size:%u (threadId: %u)", + size, epicsThreadGetIdSelf()); + + if (_sendBuffer->getRemaining() >= size) + return; + + // too large for buffer... + if (_maxSendPayloadSize < size) { + std::ostringstream msg; + msg << "requested for buffer size " << + size << ", but only " << _maxSendPayloadSize << " available."; + std::string s = msg.str(); + LOG(logLevelWarn, + "%s at %s:%d,", msg.str().c_str(), __FILE__, __LINE__); + throw std::invalid_argument(s); + } + + while (_sendBuffer->getRemaining() < size) + flush(false); + } + + + void AbstractCodec::flushSerializeBuffer() { + + LOG(logLevelTrace, + "AbstractCodec::flushSerializeBuffer enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + flush(false); + } + + + void AbstractCodec::flush(bool lastMessageCompleted) { + + LOG(logLevelTrace, + "AbstractCodec::flush enter: lastMessageCompleted:%d (threadId: %u)", + lastMessageCompleted, epicsThreadGetIdSelf()); + + // automatic end + endMessage(!lastMessageCompleted); + + _sendBuffer->flip(); + + try { + send(_sendBuffer.get()); + } catch (io_exception &) { + try { + if (isOpen()) + close(); + } catch (io_exception &) { + // noop, best-effort close + } + throw connection_closed_exception("Failed to send buffer."); + } + + _sendBuffer->clear(); + + _lastMessageStartPosition = std::numeric_limits::max(); + + // start with last header + if (!lastMessageCompleted && _lastSegmentedMessageType != 0) + startMessage(_lastSegmentedMessageCommand, 0); + } + + + void AbstractCodec::processWrite() { + + LOG(logLevelTrace, + "AbstractCodec::processWrite enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + // TODO catch ConnectionClosedException, InvalidStreamException? + switch (_writeMode) + { + case PROCESS_SEND_QUEUE: + processSendQueue(); + break; + case WAIT_FOR_READY_SIGNAL: + _writeOpReady = true; + break; + } + } + + + void AbstractCodec::send(ByteBuffer *buffer) + { + + LOG(logLevelTrace, "AbstractCodec::send enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + + // On Windows, limiting the buffer size is important to prevent + // poor throughput performances when transferring large amount of + // data. See Microsoft KB article KB823764. + // We do it also for other systems just to be safe. + std::size_t maxBytesToSend = + std::min( + _socketSendBufferSize, _remoteTransportSocketReceiveBufferSize) / 2; + + std::size_t limit = buffer->getLimit(); + std::size_t bytesToSend = limit - buffer->getPosition(); + + // limit sending + if (bytesToSend > maxBytesToSend) + { + bytesToSend = maxBytesToSend; + buffer->setLimit(buffer->getPosition() + bytesToSend); + } + + int tries = 0; + while (buffer->getRemaining() > 0) + { + + //int p = buffer.position(); + int bytesSent = write(buffer); + + if (IS_LOGGABLE(logLevelTrace)) { + hexDump(std::string("AbstractCodec::send WRITE"), + (const int8 *)buffer->getArray(), + buffer->getPosition(), buffer->getRemaining()); + } + + if (bytesSent < 0) + { + // connection lost + close(); + throw connection_closed_exception("bytesSent < 0"); + } + else if (bytesSent == 0) + { + sendBufferFull(tries++); + continue; + } + + _totalBytesSent += bytesSent; + + // readjust limit + if (bytesToSend == maxBytesToSend) + { + bytesToSend = limit - buffer->getPosition(); + + if(bytesToSend > maxBytesToSend) + bytesToSend = maxBytesToSend; + + buffer->setLimit(buffer->getPosition() + bytesToSend); + } + tries = 0; + } + } + + + void AbstractCodec::processSendQueue() + { + + LOG(logLevelTrace, + "AbstractCodec::processSendQueue enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + try + { + std::size_t senderProcessed = 0; + while (senderProcessed++ < MAX_MESSAGE_SEND) + { + TransportSender::shared_pointer sender = _sendQueue.take(-1); + if (sender.get() == 0) + { + // flush + if (_sendBuffer->getPosition() > 0) + flush(true); + + sendCompleted(); // do not schedule sending + + if (_blockingProcessQueue) { + if (terminated()) // termination + break; + sender = _sendQueue.take(0); + // termination (we want to process even if shutdown) + if (sender.get() == 0) + break; + } + else + return; + } + + processSender(sender); + } + } + //TODO MATEJ CHECK + //InterruptedException ie + catch (...) { + // noop, allowed and expected in blocking + } + + // flush + if (_sendBuffer->getPosition() > 0) + flush(true); + } + + + void AbstractCodec::clearSendQueue() + { + LOG(logLevelTrace, + "AbstractCodec::clearSendQueue enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + _sendQueue.clean(); + } + + + void AbstractCodec::enqueueSendRequest( + TransportSender::shared_pointer const & sender) { + + LOG(logLevelTrace, + "AbstractCodec::enqueueSendRequest enter: sender is set:%d" + " (threadId: %u)", + (sender.get() != 0), epicsThreadGetIdSelf()); + + _sendQueue.put(sender); + scheduleSend(); + } + + + void AbstractCodec::setSenderThread() + { + LOG(logLevelTrace, + "AbstractCodec::setSenderThread enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + _senderThread = epicsThreadGetIdSelf(); + } + + + void AbstractCodec::processSender( + TransportSender::shared_pointer const & sender) + { + + LOG(logLevelTrace, + "AbstractCodec::processSender enter: sender is set:%d (threadId: %u)", + (sender.get() != 0), epicsThreadGetIdSelf); + + ScopedLock lock(sender); + + try { + _lastMessageStartPosition = _sendBuffer->getPosition(); + + sender->send(_sendBuffer.get(), this); + + // automatic end (to set payload size) + endMessage(false); + } + catch (std::exception &e ) { + + std::ostringstream msg; + msg << "an exception caught while processing a send message: " + << e.what(); + LOG(logLevelWarn, "%s at %s:%d", + msg.str().c_str(), __FILE__, __LINE__); + + try { + close(); + } catch (io_exception & ) { + // noop + } + + throw connection_closed_exception(msg.str()); + } + } + + + void AbstractCodec::enqueueSendRequest( + TransportSender::shared_pointer const & sender, + std::size_t requiredBufferSize) { + + LOG(logLevelTrace, + "AbstractCodec::enqueueSendRequest enter: sender is set:%d " + "requiredBufferSize:%u (threadId: %u)", + (sender.get() != 0), requiredBufferSize, epicsThreadGetIdSelf); + + if (_senderThread == epicsThreadGetIdSelf() && + _sendQueue.empty() && + _sendBuffer->getRemaining() >= requiredBufferSize) + { + processSender(sender); + if (_sendBuffer->getPosition() > 0) + { + if (_lowLatency) + flush(true); + else + scheduleSend(); + } + } + else + enqueueSendRequest(sender); + } + + + void AbstractCodec::setRecipient(osiSockAddr const & sendTo) { + + LOG(logLevelTrace, + "AbstractCodec::setRecipient enter: (threadId: %u)", + epicsThreadGetIdSelf); + + _sendTo = sendTo; + } + + + void AbstractCodec::setByteOrder(int byteOrder) + { + + LOG(logLevelTrace, + "AbstractCodec::setByteOrder enter: byteOrder:%x (threadId: %u)", + byteOrder, epicsThreadGetIdSelf()); + + _socketBuffer->setEndianess(byteOrder); + // TODO sync + _sendBuffer->setEndianess(byteOrder); + _byteOrderFlag = EPICS_ENDIAN_BIG == byteOrder ? 0x80 : 0x00; + } + + + + // + // + // BlockingAbstractCodec + // + // + // + + void BlockingAbstractCodec::readPollOne() { + + LOG(logLevelTrace, + "BlockingAbstractCodec::readPollOne enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + throw std::logic_error("should not be called for blocking IO"); + } + + + void BlockingAbstractCodec::writePollOne() { + + LOG(logLevelTrace, + "BlockingAbstractCodec::writePollOne enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + throw std::logic_error("should not be called for blocking IO"); + } + + + void BlockingAbstractCodec::close() { + + LOG(logLevelTrace, + "BlockingAbstractCodec::close enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + + if (_isOpen.getAndSet(false)) + { + // always close in the same thread, same way, etc. + // wakeup processSendQueue + LOG(logLevelTrace, + "BlockingAbstractCodec::close _sendQueue.waaaaakeup: " + " (threadId: %u)", + epicsThreadGetIdSelf()); + + _sendQueue.wakeup(); + } + else { + LOG(logLevelTrace, + "BlockingAbstractCodec::close NOT WAKING UP _sendQueue: " + " (threadId: %u)", + epicsThreadGetIdSelf()); + } + } + + + bool BlockingAbstractCodec::terminated() { + + LOG(logLevelTrace, + "BlockingAbstractCodec::terminated enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + //TODO OPEN QUESTION TO MATEJ + return !isOpen(); + } + + + bool BlockingAbstractCodec::isOpen() { + + LOG(logLevelTrace, "BlockingAbstractCodec::isOpen enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + return _isOpen.get(); + } + + + void BlockingAbstractCodec::start() { + + LOG(logLevelTrace, "BlockingAbstractCodec::start enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + _readThread = epicsThreadCreate( + "BlockingAbstractCodec-readThread", + epicsThreadPriorityMedium, + epicsThreadGetStackSize( + epicsThreadStackMedium), + BlockingAbstractCodec::receiveThread, + this); + + _sendThread = epicsThreadCreate( + "BlockingAbstractCodec-_sendThread", + epicsThreadPriorityMedium, + epicsThreadGetStackSize( + epicsThreadStackMedium), + BlockingAbstractCodec::sendThread, + this); + + LOG(logLevelTrace, + "BlockingAbstractCodec::start exit WITH readThread: %u," + " sendThread:%u (threadId: %u)", + _readThread, _sendThread, epicsThreadGetIdSelf()); + + } + + + void BlockingAbstractCodec::receiveThread(void *param) + { + + LOG(logLevelTrace, + "BlockingAbstractCodec::receiveThread enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + + BlockingAbstractCodec *bac = static_cast(param); + + while (bac->isOpen()) + { + try { + bac->processRead(); + } catch (io_exception &e) { + LOG(logLevelWarn, + "an exception caught while in receiveThread at %s:%d: %s", + __FILE__, __LINE__, e.what()); + } + } + + LOG(logLevelTrace, "BlockingAbstractCodec::receiveThread" + " EXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIT: (threadId: %u)", + epicsThreadGetIdSelf()); + + } + + + void BlockingAbstractCodec::sendThread(void *param) + { + + LOG(logLevelTrace, + "BlockingAbstractCodec::sendThread enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + BlockingAbstractCodec *bac = static_cast(param); + + bac->setSenderThread(); + + while (bac->isOpen()) + { + try { + bac->processWrite(); + } catch (io_exception &e) { + LOG(logLevelWarn, + "an exception caught while in sendThread at %s:%d: %s", + __FILE__, __LINE__, e.what()); + } + } + + LOG(logLevelTrace, + "BlockingAbstractCodec::sendThread EXIIIIIIIIIIIIIIT" + " while(bac->isOpen): (threadId: %u)", + epicsThreadGetIdSelf()); + + + // wait read thread to die + //TODO epics join thread + //readThread.join(); // TODO timeout + //bac->_shutdownEvent.signal(); + + // call internal destroy + LOG(logLevelTrace, "XXXXXXXXXXXXXXXXXXXXXXXXXXXX" + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (threadId: %u)", + epicsThreadGetIdSelf()); + //bac->internalDestroy(); + LOG(logLevelTrace, "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" + "YYYYYYYYYYYYYYYYYYYYYYYYYYYY (threadId: %u)", + epicsThreadGetIdSelf()); + + LOG(logLevelTrace, + "BlockingAbstractCodec::sendThread EXIIIIT (threadId: %u)", + epicsThreadGetIdSelf()); + + } + + + void BlockingAbstractCodec::sendBufferFull(int tries) { + + LOG(logLevelTrace, + "BlockingAbstractCodec::sendBufferFull enter: tries: %d " + "(threadId: %u)", + tries, epicsThreadGetIdSelf()); + + // TODO constants + epicsThreadSleep(std::max(tries * 0.1, 1)); + } + + + // + // + // BlockingSocketAbstractCodec + // + // + // + + + BlockingSocketAbstractCodec::BlockingSocketAbstractCodec( + SOCKET channel, + int32_t sendBufferSize, + int32_t receiveBufferSize): + BlockingAbstractCodec( + new ByteBuffer((std::max((std::size_t)( + MAX_TCP_RECV + MAX_ENSURE_DATA_BUFFER_SIZE), receiveBufferSize) + + (PVA_ALIGNMENT - 1)) & (~(PVA_ALIGNMENT - 1))), + new ByteBuffer((std::max((std::size_t)( MAX_TCP_RECV + + MAX_ENSURE_DATA_BUFFER_SIZE), receiveBufferSize) + (PVA_ALIGNMENT - 1)) + & (~(PVA_ALIGNMENT - 1))), sendBufferSize), + _channel(channel) + { + + // get remote address + osiSocklen_t saSize = sizeof(sockaddr); + int retval = getpeername(_channel, &(_socketAddress.sa), &saSize); + if(unlikely(retval<0)) { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + LOG(logLevelError, + "Error fetching socket remote address: %s", + errStr); + } + + // set receive timeout so that we do not have problems at + //shutdown (recvfrom would block) + struct timeval timeout; + memset(&timeout, 0, sizeof(struct timeval)); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + if (unlikely(::setsockopt (_channel, SOL_SOCKET, SO_RCVTIMEO, + (char*)&timeout, sizeof(timeout)) < 0)) + { + char errStr[64]; + epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); + LOG(logLevelError, + "Failed to set SO_RCVTIMEO for TDP socket %s: %s.", + inetAddressToString(_socketAddress).c_str(), errStr); + } + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec constructed (threadId: %u)", + epicsThreadGetIdSelf()); + } + + + void BlockingSocketAbstractCodec::internalDestroy() { + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::internalDestroy enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + if(_channel != INVALID_SOCKET) { + epicsSocketDestroy(_channel); + _channel = INVALID_SOCKET; + } + + } + + + void BlockingSocketAbstractCodec::invalidDataStreamHandler() { + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::invalidDataStreamHandler enter:" + " (threadId: %u)", + epicsThreadGetIdSelf()); + + close(); + } + + + int BlockingSocketAbstractCodec::write( + epics::pvData::ByteBuffer *src) { + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::write enter: position:%u, " + "remaining:%u (threadId: %u)", + src->getPosition(), src->getRemaining(), epicsThreadGetIdSelf()); + + std::size_t remaining; + while((remaining=src->getRemaining()) > 0) { + + int bytesSent = ::send(_channel, + &src->getArray()[src->getPosition()], + remaining, 0); + + if(unlikely(bytesSent<0)) { + + int socketError = SOCKERRNO; + + // spurious EINTR check + if (socketError==SOCK_EINTR) + continue; + } + + if (bytesSent > 0) { + src->setPosition(src->getPosition() + bytesSent); + } + + return bytesSent; + + } + + //TODO check what to return + return -1; + } + + + std::size_t BlockingSocketAbstractCodec::getSocketReceiveBufferSize() + const { + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::getSocketReceiveBufferSize" + " enter (threadId: %u)", epicsThreadGetIdSelf()); + + osiSocklen_t intLen = sizeof(int); + char strBuffer[64]; + int socketRecvBufferSize; + int retval = getsockopt(_channel, SOL_SOCKET, SO_RCVBUF, + (char *)&socketRecvBufferSize, &intLen); + + if(retval<0) { + epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); + //LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer); + } + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::getSocketReceiveBufferSize" + " returning:%u (threadId: %u)", socketRecvBufferSize, + epicsThreadGetIdSelf()); + + return socketRecvBufferSize; + } + + + int BlockingSocketAbstractCodec::read(epics::pvData::ByteBuffer* dst) { + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::read enter: " + "read bytes:%u (threadId: %u)", + dst->getRemaining(), epicsThreadGetIdSelf()); + + std::size_t remaining; + while((remaining=dst->getRemaining()) > 0) { + + // read + std::size_t pos = dst->getPosition(); + + int bytesRead = recv(_channel, + (char*)(dst->getArray()+pos), remaining, 0); + + if (IS_LOGGABLE(logLevelTrace)) { + hexDump(std::string("READ"), + (const int8 *)(dst->getArray()+pos), bytesRead); + } + + if(unlikely(bytesRead<=0)) { + + if (bytesRead<0) + { + int socketError = SOCKERRNO; + + // interrupted or timeout + if (socketError == EINTR || + socketError == EAGAIN || + socketError == EWOULDBLOCK) + continue; + } + + return -1; // 0 means connection loss for blocking transport, notify codec by returning -1 + } + dst->setPosition(dst->getPosition() + bytesRead); + return bytesRead; + } + + //TODO check what to return + return -1; + } + + + BlockingServerTCPTransportCodec::BlockingServerTCPTransportCodec( + Context::shared_pointer const & context, + SOCKET channel, + std::auto_ptr& responseHandler, + int32_t sendBufferSize, + int32_t receiveBufferSize) : + BlockingTCPTransportCodec(context, channel, responseHandler, + sendBufferSize, receiveBufferSize, PVA_DEFAULT_PRIORITY), + _lastChannelSID(0) + { + + // NOTE: priority not yet known, default priority is used to + //register/unregister + // TODO implement priorities in Reactor... not that user will + // change it.. still getPriority() must return "registered" priority! + + start(); + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec constructed (threadId: %u)", + epicsThreadGetIdSelf()); + + } + + + BlockingServerTCPTransportCodec::~BlockingServerTCPTransportCodec() { + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec DESTRUCTED (threadId: %u)", + epicsThreadGetIdSelf()); + } + + + pvAccessID BlockingServerTCPTransportCodec::preallocateChannelSID() { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::preallocateChannelSID enter:" + " (threadId: %u)", + epicsThreadGetIdSelf()); + + Lock lock(_channelsMutex); + // search first free (theoretically possible loop of death) + pvAccessID sid = ++_lastChannelSID; + while(_channels.find(sid)!=_channels.end()) + sid = ++_lastChannelSID; + return sid; + } + + + void BlockingServerTCPTransportCodec::registerChannel( + pvAccessID sid, + ServerChannel::shared_pointer const & channel) { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::registerChannel enter: sid:%d " + " (threadId: %u)", + channel->getSID(), epicsThreadGetIdSelf()); + + Lock lock(_channelsMutex); + _channels[sid] = channel; + + } + + + void BlockingServerTCPTransportCodec::unregisterChannel(pvAccessID sid) { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::unregisterChannel enter:" + " sid:%d (threadId: %u)", + sid, epicsThreadGetIdSelf()); + + Lock lock(_channelsMutex); + _channels.erase(sid); + } + + + ServerChannel::shared_pointer + BlockingServerTCPTransportCodec::getChannel(pvAccessID sid) { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::getChannel enter:" + " sid:%d (threadId: %u)", + sid, epicsThreadGetIdSelf()); + + Lock lock(_channelsMutex); + + std::map::iterator it = + _channels.find(sid); + + if(it!=_channels.end()) return it->second; + + return ServerChannel::shared_pointer(); + } + + + int BlockingServerTCPTransportCodec::getChannelCount() { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::getChannelCount enter: " + "(threadId: %u)", + epicsThreadGetIdSelf()); + + Lock lock(_channelsMutex); + return static_cast(_channels.size()); + } + + + void BlockingServerTCPTransportCodec::send(ByteBuffer* buffer, + TransportSendControl* control) { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::send enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + // + // set byte order control message + // + + ensureBuffer(PVA_MESSAGE_HEADER_SIZE); + buffer->putByte(PVA_MAGIC); + buffer->putByte(PVA_VERSION); + buffer->putByte( + 0x01 | ((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) + ? 0x80 : 0x00)); // control + big endian + buffer->putByte(2); // set byte order + buffer->putInt(0); + + + // + // send verification message + // + control->startMessage(CMD_CONNECTION_VALIDATION, 2*sizeof(int32)); + + // receive buffer size + buffer->putInt(static_cast(getReceiveBufferSize())); + + // socket receive buffer size + buffer->putInt(static_cast(getSocketReceiveBufferSize())); + + // send immediately + control->flush(true); + } + } +} diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h new file mode 100644 index 0000000..f885d88 --- /dev/null +++ b/pvAccessApp/remote/codec.h @@ -0,0 +1,807 @@ +/** +* 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 CODEC_H_ +#define CODEC_H_ + +#include +#include +#include + +#ifdef epicsExportSharedSymbols +# define abstractCodecEpicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef abstractCodecEpicsExportSharedSymbols +# define epicsExportSharedSymbols +# undef abstractCodecEpicsExportSharedSymbols +#endif + +#include +#include +#include +#include +#include +#include + +namespace epics { + namespace pvAccess { + + + template + class AtomicValue + { + public: + AtomicValue(): _value(0) {}; + + T getAndSet(T value) + { + mutex.lock(); + T tmp = _value; _value = value; + mutex.unlock(); + return tmp; + } + + T get() { mutex.lock(); T tmp = _value; mutex.unlock(); return tmp; } + + private: + T _value; + epics::pvData::Mutex mutex; + }; + + + template + class queue { + public: + + queue(void) { } + //TODO + /*queue(queue const &T) = delete; + queue(queue &&T) = delete; + queue& operator=(const queue &T) = delete; + */ + ~queue(void) + { + LOG(logLevelTrace, + "queue::~queue DESTROY (threadId: %u)", epicsThreadGetIdSelf()); + } + + + bool empty(void) + { + LOG(logLevelTrace, + "queue::empty enter: (threadId: %u)", epicsThreadGetIdSelf()); + epics::pvData::Lock lock(_queueMutex); + return _queue.empty(); + } + + void clean() + { + LOG(logLevelTrace, "queue::clean enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + epics::pvData::Lock lock(_queueMutex); + _queue.clear(); + } + + + void wakeup() + { + + LOG(logLevelTrace, "queue::wakeup enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + if (!_wakeup.getAndSet(true)) + { + LOG(logLevelTrace, + "queue::wakeup signaling on _queueEvent: (threadId: %u)", + epicsThreadGetIdSelf()); + _queueEvent.signal(); + } + } + + + void put(T const & elem) + { + LOG(logLevelTrace, + "queue::put enter (threadId: %u)", epicsThreadGetIdSelf()); + + { + epics::pvData::Lock lock(_queueMutex); + _queue.push_front(elem); + } + + _queueEvent.signal(); + } + + + T take(int timeOut) + { + + LOG(logLevelTrace, + "queue::take enter timeOut:%d (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + + while (true) + { + + bool isEmpty = empty(); + + if (isEmpty) + { + + if (timeOut < 0) { + epics::pvAccess::LOG(logLevelTrace, + "queue::take exit timeOut:%d (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + + return T(); + } + + while (isEmpty) + { + + if (timeOut == 0) { + + LOG(logLevelTrace, + "queue::take going to wait timeOut:%d (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + + _queueEvent.wait(); + } + else { + + LOG(logLevelTrace, + "queue::take going to wait timeOut:%d (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + + _queueEvent.wait(timeOut); + } + + LOG(logLevelTrace, + "queue::take waking up timeOut:%d (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + + isEmpty = empty(); + if (isEmpty) + { + if (timeOut > 0) { // TODO spurious wakeup, but not critical + LOG(logLevelTrace, + "queue::take exit after being woken up timeOut:%d" + " (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + return T(); + } + else // if (timeout == 0) cannot be negative + { + if (_wakeup.getAndSet(false)) { + + LOG(logLevelTrace, + "queue::take exit after being woken up timeOut:%d" + " (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + + return T(); + } + } + } + } + } + else + { + + LOG(logLevelTrace, + "queue::take obtaining lock for front element timeOut:%d" + " (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + + epics::pvData::Lock lock(_queueMutex); + T sender = _queue.front(); + _queue.pop_front(); + + LOG(logLevelTrace, + "queue::take exit with sender timeOut:%d (threadId: %u)", + timeOut, epicsThreadGetIdSelf()); + + return sender; + } + } + } + + private: + + std::deque _queue; + epics::pvData::Event _queueEvent; + epics::pvData::Mutex _queueMutex; + AtomicValue _wakeup; + epics::pvData::Mutex _stdMutex; + }; + + + class simulate_finally_exception: public std::runtime_error { + public: + explicit simulate_finally_exception( + const std::string &s): std::runtime_error(s) {} + }; + + class io_exception: public std::runtime_error { + public: + explicit io_exception(const std::string &s): std::runtime_error(s) {} + }; + + + class invalid_data_stream_exception: public std::runtime_error { + public: + explicit invalid_data_stream_exception( + const std::string &s): std::runtime_error(s) {} + }; + + + class connection_closed_exception: public std::runtime_error { + public: + explicit connection_closed_exception(const std::string &s): std::runtime_error(s) {} + }; + + + enum ReadMode { NORMAL, SPLIT, SEGMENTED }; + + enum WriteMode { PROCESS_SEND_QUEUE, WAIT_FOR_READY_SIGNAL }; + + + class AbstractCodec : + public TransportSendControl, + public Transport + { + public: + + static const std::size_t MAX_MESSAGE_PROCESS; + static const std::size_t MAX_MESSAGE_SEND; + static const std::size_t MAX_ENSURE_SIZE; + static const std::size_t MAX_ENSURE_DATA_SIZE; + static const std::size_t MAX_ENSURE_BUFFER_SIZE; + static const std::size_t MAX_ENSURE_DATA_BUFFER_SIZE; + + AbstractCodec( + epics::pvData::ByteBuffer *receiveBuffer, + epics::pvData::ByteBuffer *sendBuffer, + int32_t socketSendBufferSize, + bool blockingProcessQueue); + + virtual void processControlMessage() = 0; + virtual void processApplicationMessage() = 0; + virtual osiSockAddr getLastReadBufferSocketAddress() = 0; + virtual void invalidDataStreamHandler() = 0; + virtual void readPollOne()=0; + virtual void writePollOne() = 0; + virtual void scheduleSend() = 0; + virtual void sendCompleted() = 0; + virtual bool terminated() = 0; + virtual int write(epics::pvData::ByteBuffer* src) = 0; + virtual int read(epics::pvData::ByteBuffer* dst) = 0; + virtual bool isOpen() = 0; + virtual void close() = 0; + + + virtual ~AbstractCodec() + { + LOG(logLevelTrace, + "AbstractCodec::~AbstractCodec DESTROY (threadId: %u)", + epicsThreadGetIdSelf()); + } + + void alignBuffer(std::size_t alignment); + void ensureData(std::size_t size); + void alignData(std::size_t alignment); + void startMessage( + epics::pvData::int8 command, + std::size_t ensureCapacity); + void putControlMessage( + epics::pvData::int8 command, + epics::pvData::int32 data); + void endMessage(); + void ensureBuffer(std::size_t size); + void flushSerializeBuffer(); + void flush(bool lastMessageCompleted); + 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); + void setSenderThread(); + void setRecipient(osiSockAddr const & sendTo); + void setByteOrder(int byteOrder); + + static std::size_t alignedValue(std::size_t value, std::size_t alignment); + + protected: + + virtual void sendBufferFull(int tries) = 0; + void send(epics::pvData::ByteBuffer *buffer); + + + ReadMode _readMode; + int8_t _version; + int8_t _flags; + int8_t _command; + int32_t _payloadSize; + epics::pvData::int32 _remoteTransportSocketReceiveBufferSize; + int64_t _totalBytesSent; + bool _blockingProcessQueue; + //TODO initialize union + osiSockAddr _sendTo; + epicsThreadId _senderThread; + WriteMode _writeMode; + bool _writeOpReady; + bool _lowLatency; + + std::auto_ptr _socketBuffer; + std::auto_ptr _sendBuffer; + + epics::pvAccess::queue _sendQueue; + + private: + + void processHeader(); + void processReadNormal(); + void processReadSegmented(); + bool readToBuffer(std::size_t requiredBytes, bool persistent); + void endMessage(bool hasMoreSegments); + void processSender( + epics::pvAccess::TransportSender::shared_pointer const & sender); + + std::size_t _storedPayloadSize; + std::size_t _storedPosition; + std::size_t _storedLimit; + std::size_t _startPosition; + + std::size_t _maxSendPayloadSize; + std::size_t _lastMessageStartPosition; + std::size_t _lastSegmentedMessageType; + int8_t _lastSegmentedMessageCommand; + std::size_t _nextMessagePayloadOffset; + + epics::pvData::int8 _byteOrderFlag; + int32_t _socketSendBufferSize; + }; + + + class BlockingAbstractCodec: public AbstractCodec { + + public: + + POINTER_DEFINITIONS(BlockingAbstractCodec); + + BlockingAbstractCodec( + epics::pvData::ByteBuffer *receiveBuffer, + epics::pvData::ByteBuffer *sendBuffer, + int32_t socketSendBufferSize): + AbstractCodec(receiveBuffer, sendBuffer, socketSendBufferSize, true), + _readThread(0), _sendThread(0) { _isOpen.getAndSet(true);} + + void readPollOne(); + void writePollOne(); + void scheduleSend() {} + void sendCompleted() {} + void close(); + bool terminated(); + bool isOpen(); + void start(); + + static void receiveThread(void* param); + static void sendThread(void* param); + + protected: + void sendBufferFull(int tries); + virtual void internalDestroy() = 0; + + private: + AtomicValue _isOpen; + volatile epicsThreadId _readThread; + volatile epicsThreadId _sendThread; + epics::pvData::Event _shutdownEvent; + }; + + + class BlockingSocketAbstractCodec: public BlockingAbstractCodec { + + public: + + BlockingSocketAbstractCodec( + SOCKET channel, + int32_t sendBufferSize, + int32_t receiveBufferSize); + + int read(epics::pvData::ByteBuffer* dst); + int write(epics::pvData::ByteBuffer* src); + osiSockAddr getLastReadBufferSocketAddress() { return _socketAddress;} + void invalidDataStreamHandler(); + std::size_t getSocketReceiveBufferSize() const; + + protected: + + void internalDestroy(); + + private: + SOCKET _channel; + osiSockAddr _socketAddress; + }; + + + class BlockingTCPTransportCodec : + public BlockingSocketAbstractCodec, + public std::tr1::enable_shared_from_this { + + public: + + epics::pvData::String getType() const { + return epics::pvData::String("TCP"); + } + + + void internalDestroy() { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::internalDestroy() enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + BlockingSocketAbstractCodec::internalDestroy(); + Transport::shared_pointer thisSharedPtr = this->shared_from_this(); + _context->getTransportRegistry()->remove(thisSharedPtr); + } + + + void changedTransport() {} + + + void processControlMessage() { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::processControlMessage()" + "enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + if (_command == 2) + { + // check 7-th bit + setByteOrder(_flags < 0 ? EPICS_ENDIAN_BIG : EPICS_ENDIAN_LITTLE); + } + } + + + void processApplicationMessage() { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::processApplicationMessage() enter:" + " (threadId: %u)", + epicsThreadGetIdSelf()); + + _responseHandler->handleResponse(&_socketAddress, shared_from_this(), + _version, _command, _payloadSize, _socketBuffer.get()); + } + + + const osiSockAddr* getRemoteAddress() const { + return &_socketAddress; + } + + + epics::pvData::int8 getRevision() const { + return PVA_PROTOCOL_REVISION; + } + + + std::size_t getReceiveBufferSize() const { + return _socketBuffer->getSize(); + } + + + epics::pvData::int16 getPriority() const { + return _priority; + } + + + void setRemoteRevision(epics::pvData::int8 revision) { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::setRemoteRevision() enter:" + " revision: %d (threadId: %u)", + revision, epicsThreadGetIdSelf()); + + _remoteTransportRevision = revision; + } + + + void setRemoteTransportReceiveBufferSize( + std::size_t remoteTransportReceiveBufferSize) { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::setRemoteTransportReceiveBufferSize()" + " enter: remoteTransportReceiveBufferSize:%u (threadId: %u)", + remoteTransportReceiveBufferSize, epicsThreadGetIdSelf()); + + _remoteTransportReceiveBufferSize = remoteTransportReceiveBufferSize; + } + + + void setRemoteTransportSocketReceiveBufferSize( + std::size_t socketReceiveBufferSize) { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::" + "setRemoteTransportSocketReceiveBufferSize()" + "enter: socketReceiveBufferSize:%u (threadId: %u)", + socketReceiveBufferSize, epicsThreadGetIdSelf()); + + _remoteTransportSocketReceiveBufferSize = socketReceiveBufferSize; + } + + + std::tr1::shared_ptr + cachedDeserialize(epics::pvData::ByteBuffer* buffer) + { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::cachedDeserialize() enter:" + " (threadId: %u)", + epicsThreadGetIdSelf()); + + return _incomingIR.deserialize(buffer, this); + } + + + void cachedSerialize( + const std::tr1::shared_ptr& field, + epics::pvData::ByteBuffer* buffer) + { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::cachedSerialize() enter:" + " (threadId: %u)", + epicsThreadGetIdSelf()); + + _outgoingIR.serialize(field, buffer, this); + } + + + bool directSerialize( + epics::pvData::ByteBuffer *existingBuffer, + const char* toSerialize, + std::size_t elementCount, std::size_t elementSize) + { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::directSerialize() enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + return false; + } + + + bool directDeserialize(epics::pvData::ByteBuffer *existingBuffer, + char* deserializeTo, + std::size_t elementCount, std::size_t elementSize) { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::directDeserialize() enter:" + " (threadId: %u)", + epicsThreadGetIdSelf()); + + return false; + } + + + void flushSendQueue() { }; + + + bool isClosed() { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::isClosed() enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + return !isOpen(); + } + + + void activate() { + + LOG(logLevelTrace, + "BlockingTCPTransportCodec::activate() enter: (threadId: %u)", + epicsThreadGetIdSelf()); + + Transport::shared_pointer thisSharedPtr = shared_from_this(); + _context->getTransportRegistry()->put(thisSharedPtr); + } + + protected: + + BlockingTCPTransportCodec( + Context::shared_pointer const & context, + SOCKET channel, + std::auto_ptr& responseHandler, + int32_t sendBufferSize, + int32_t receiveBufferSize, + epics::pvData::int16 priority + ): + BlockingSocketAbstractCodec(channel, sendBufferSize, receiveBufferSize), + _context(context), _responseHandler(responseHandler), + _verified(false), _remoteTransportReceiveBufferSize(MAX_TCP_RECV), + _remoteTransportRevision(0), _priority(priority) + { + LOG(logLevelTrace, + "BlockingTCPTransportCodec constructed: (threadId: %u)", + epicsThreadGetIdSelf()); + } + + + private: + + Context::shared_pointer _context; + std::auto_ptr _responseHandler; + bool _verified; + size_t _remoteTransportReceiveBufferSize; + epics::pvData::int8 _remoteTransportRevision; + epics::pvData::int16 _priority; + + osiSockAddr _socketAddress; + epics::pvData::Mutex _verifiedMutex; + epics::pvData::Event _verifiedEvent; + IntrospectionRegistry _incomingIR; + IntrospectionRegistry _outgoingIR; + + }; + + + class BlockingServerTCPTransportCodec : + public BlockingTCPTransportCodec, + public ChannelHostingTransport, + public TransportSender { + + public: + POINTER_DEFINITIONS(BlockingServerTCPTransportCodec); + + BlockingServerTCPTransportCodec( + Context::shared_pointer const & context, + SOCKET channel, + std::auto_ptr& responseHandler, + int32_t sendBufferSize, + int32_t receiveBufferSize ); + + static shared_pointer create( + Context::shared_pointer const & context, + SOCKET channel, + std::auto_ptr& responseHandler, + int sendBufferSize, + int receiveBufferSize) + { + shared_pointer thisPointer( + new BlockingServerTCPTransportCodec( + context, channel, responseHandler, + sendBufferSize, receiveBufferSize) + ); + thisPointer->activate(); + return thisPointer; + } + + public: + + bool acquire(std::tr1::shared_ptr const & client) + { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::acquire() enter:" + " client is set: %d (threadId: %u)", + (client.get() != 0), epicsThreadGetIdSelf()); + + return false; + } + + void release(pvAccessID /*clientId*/) {} + + pvAccessID preallocateChannelSID(); + + void depreallocateChannelSID(pvAccessID /*sid*/) { + // noop + } + + void registerChannel( + pvAccessID sid, + ServerChannel::shared_pointer const & channel); + + void unregisterChannel(pvAccessID sid); + + ServerChannel::shared_pointer getChannel(pvAccessID sid); + + int getChannelCount(); + + epics::pvData::PVField::shared_pointer getSecurityToken() { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::getSecurityToken() enter:" + " (threadId: %u)", + epicsThreadGetIdSelf()); + + return epics::pvData::PVField::shared_pointer(); + } + + void lock() { + // noop + } + + void unlock() { + // noop + } + + void acquire() { + // noop, since does not make sence on itself + } + + void release() { + // noop, since does not make sence on itself + } + + bool verify(epics::pvData::int32 timeoutMs) { + + LOG(logLevelTrace, + "BlockingServerTCPTransportCodec::verify() enter: " + "timeoutMs:%d (threadId: %u)", + timeoutMs, epicsThreadGetIdSelf()); + + TransportSender::shared_pointer transportSender = + std::tr1::dynamic_pointer_cast(shared_from_this()); + enqueueSendRequest(transportSender); + verified(); + return true; + } + + void verified() { + } + + void aliveNotification() { + // noop on server-side + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control); + + virtual ~BlockingServerTCPTransportCodec(); + + private: + + /** + * Last SID cache. + */ + pvAccessID _lastChannelSID; + + /** + * Channel table (SID -> channel mapping). + */ + std::map _channels; + + epics::pvData::Mutex _channelsMutex; + + }; + } +} + +#endif /* CODEC_H_ */ diff --git a/testApp/remote/Makefile b/testApp/remote/Makefile index 9308543..d0fd94b 100644 --- a/testApp/remote/Makefile +++ b/testApp/remote/Makefile @@ -51,6 +51,12 @@ testChannelAccess_SRCS = testChannelAccess channelAccessIFTest testChannelAccess_LIBS += pvData pvAccess pvMB Com TESTS += testChannelAccess +TESTPROD_HOST += testCodec +testCodec_SRCS = testCodec +testCodec_LIBS += pvData pvAccess pvMB Com +TESTS += testCodec + + PROD_HOST += pvget pvget_SRCS += pvget.cpp pvget_LIBS += pvData pvAccess pvMB Com diff --git a/testApp/remote/channelAccessIFTest.cpp b/testApp/remote/channelAccessIFTest.cpp index cbee7f3..cdf5f69 100755 --- a/testApp/remote/channelAccessIFTest.cpp +++ b/testApp/remote/channelAccessIFTest.cpp @@ -48,7 +48,7 @@ int ChannelAccessIFTest::runAllTest() { testPlan(152); #endif - test_implementation(); +/* test_implementation(); test_providerName(); test_createEmptyChannel(); @@ -56,10 +56,10 @@ int ChannelAccessIFTest::runAllTest() { test_createChannel(); test_recreateChannelOnDestroyedProvider(); test_findEmptyChannel(); - test_findChannel(); + test_findChannel();*/ test_channel(); - test_channelGetWithInvalidChannelAndRequester(); +/* test_channelGetWithInvalidChannelAndRequester(); test_channelGetNoProcess(); test_channelGetIntProcess(); test_channelGetTestNoConnection(); @@ -95,7 +95,7 @@ int ChannelAccessIFTest::runAllTest() { test_channelArray(); test_channelArray_destroy(); - test_channelArrayTestNoConnection(); + test_channelArrayTestNoConnection();*/ #ifdef ENABLE_STRESS_TESTS test_stressConnectDisconnect(); @@ -415,7 +415,7 @@ void ChannelAccessIFTest::test_channel() { testOk(channel->getConnectionState() == Channel::DESTROYED , "%s: channel connection state DESTROYED ", CURRENT_FUNCTION); - testDiag("%s: destroying the channel yet again", CURRENT_FUNCTION); +/* testDiag("%s: destroying the channel yet again", CURRENT_FUNCTION); channel->destroy(); succStatus = channelReq->waitUntilStateChange(getTimeoutSec()); @@ -432,7 +432,7 @@ void ChannelAccessIFTest::test_channel() { testOk(!channel->isConnected(), "%s: yet again destroyed channel should not be connected ", CURRENT_FUNCTION); testOk(channel->getConnectionState() == Channel::DESTROYED , "%s: yet again destroyed channel connection state DESTROYED ", CURRENT_FUNCTION); - +*/ } diff --git a/testApp/remote/testChannelAccess.cpp b/testApp/remote/testChannelAccess.cpp index 535e1df..d6778c6 100755 --- a/testApp/remote/testChannelAccess.cpp +++ b/testApp/remote/testChannelAccess.cpp @@ -88,6 +88,7 @@ class ChannelAccessIFRemoteTest: public ChannelAccessIFTest { MAIN(testChannelProvider) { + SET_LOG_LEVEL(logLevelTrace); ChannelAccessIFRemoteTest caRemoteTest; return caRemoteTest.runAllTest(); } diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp new file mode 100644 index 0000000..716ca42 --- /dev/null +++ b/testApp/remote/testCodec.cpp @@ -0,0 +1,3195 @@ +/* +* testCodec.cpp +*/ + +#ifdef _WIN32 +#define NOMINMAX +#endif + + +#include +#include +#include +#include + +#include +#include + +using namespace epics::pvData; + +namespace epics { + + namespace pvAccess { + + class PVAMessage { + + public: + + int8_t _version; + int8_t _flags; + int8_t _command; + int32_t _payloadSize; + std::tr1::shared_ptr _payload; + + PVAMessage(int8_t version, + int8_t flags, + int8_t command, + int32_t payloadSize) { + _version = version; + _flags = flags; + _command = command; + _payloadSize = payloadSize; + } + + //memberwise copy constructor/assigment operator + //provided by the compiler + }; + + + class ReadPollOneCallback { + public: + virtual void readPollOne() = 0; + }; + + + class WritePollOneCallback { + public: + virtual void writePollOne() = 0 ; + }; + + + class TestCodec: public AbstractCodec { + + public: + + TestCodec( + std::size_t receiveBufferSize, + std::size_t sendBufferSize, + bool blocking = false): + _closedCount(0), + _invalidDataStreamCount(0), + _scheduleSendCount(0), + _sendCompletedCount(0), + _sendBufferFullCount(0), + _readPollOneCount(0), + _writePollOneCount(0), + _throwExceptionOnSend(false), + _readPayload(false), + _disconnected(false), + _forcePayloadRead(-1), + _readBuffer(new ByteBuffer(receiveBufferSize)), + _writeBuffer(sendBufferSize), + AbstractCodec( + new ByteBuffer(receiveBufferSize), + new ByteBuffer(sendBufferSize), + sendBufferSize/10, + blocking ) { + } + + + void reset() + { + _closedCount = 0; + _invalidDataStreamCount = 0; + _scheduleSendCount = 0; + _sendCompletedCount = 0; + _sendBufferFullCount = 0; + _readPollOneCount = 0; + _writePollOneCount = 0; + _readBuffer->clear(); + _writeBuffer.clear(); + _receivedAppMessages.clear(); + _receivedControlMessages.clear(); + } + + + int read(ByteBuffer *buffer) { + + if (_disconnected) + return -1; + + std::size_t startPos = _readBuffer->getPosition(); + //buffer.put(readBuffer); + //while (buffer.hasRemaining() && readBuffer.hasRemaining()) + // buffer.put(readBuffer.get()); + + std::size_t bufferRemaining = buffer->getRemaining(); + std::size_t readBufferRemaining = + _readBuffer->getRemaining(); + + if (bufferRemaining >= readBufferRemaining) { + + while(_readBuffer->getRemaining() > 0) { + buffer->putByte(_readBuffer->getByte()); + } + + } + else + { + // TODO this could be optimized + for (std::size_t i = 0; i < bufferRemaining; i++) { + buffer->putByte(_readBuffer->getByte()); + } + } + return _readBuffer->getPosition() - startPos; + } + + + int write(ByteBuffer *buffer) { + if (_disconnected) + return -1; // TODO: not by the JavaDoc API spec + + if (_throwExceptionOnSend) + throw io_exception("text IO exception"); + + // we could write remaining int8_ts, but for + //test this is enought + if (buffer->getRemaining() > _writeBuffer.getRemaining()) + return 0; + + std::size_t startPos = buffer->getPosition(); + + while(buffer->getRemaining() > 0) { + _writeBuffer.putByte(buffer->getByte()); + } + + return buffer->getPosition() - startPos; + } + + + void transferToReadBuffer() + { + flushSerializeBuffer(); + _writeBuffer.flip(); + + _readBuffer->clear(); + + while(_writeBuffer.getRemaining() > 0) { + _readBuffer->putByte(_writeBuffer.getByte()); + } + + _readBuffer->flip(); + + _writeBuffer.clear(); + } + + + void addToReadBuffer() + { + flushSerializeBuffer(); + _writeBuffer.flip(); + + while(_writeBuffer.getRemaining() > 0) { + _readBuffer->putByte(_writeBuffer.getByte()); + } + + _readBuffer->flip(); + + _writeBuffer.clear(); + } + + + void processControlMessage() { + // alignment check + if (_socketBuffer->getPosition() % PVA_ALIGNMENT != 0) + throw std::logic_error("message not aligned"); + + _receivedControlMessages.push_back( + PVAMessage(_version, _flags, _command, _payloadSize)); + } + + + void processApplicationMessage() { + // alignment check + if (_socketBuffer->getPosition() % PVA_ALIGNMENT != 0) + throw std::logic_error("message not aligned"); + + PVAMessage caMessage(_version, _flags, + _command, _payloadSize); + + if (_readPayload && _payloadSize > 0) + { + // no fragmentation supported by this implementation + std::size_t toRead = + _forcePayloadRead >= 0 + ? _forcePayloadRead : _payloadSize; + + caMessage._payload.reset(new ByteBuffer(toRead)); + while (toRead > 0) + { + std::size_t partitalRead = + std::min(toRead, MAX_ENSURE_DATA_SIZE); + ensureData(partitalRead); + std::size_t pos = caMessage._payload->getPosition(); + + + while(_socketBuffer->getRemaining() > 0) { + caMessage._payload->putByte(_socketBuffer->getByte()); + } + + std::size_t read = + caMessage._payload->getPosition() - pos; + + toRead -= read; + } + } + _receivedAppMessages.push_back(caMessage); + } + + + void readPollOne() { + _readPollOneCount++; + if (_readPollOneCallback.get() != 0) + _readPollOneCallback->readPollOne(); + } + + + void writePollOne() { + _writePollOneCount++; + if (_writePollOneCallback.get() != 0) + _writePollOneCallback->writePollOne(); + } + + + void endBlockedProcessSendQueue() { + _blockingProcessQueue = false; + _sendQueue.wakeup(); + } + + + void close() { _closedCount++; } + + bool isOpen() { return _closedCount == 0; } + + ReadMode getReadMode() { return _readMode; } + + WriteMode getWriteMode() { return _writeMode;} + + std::auto_ptr & getSendBuffer() + { + return _sendBuffer; + } + + osiSockAddr getLastReadBufferSocketAddress() + { + osiSockAddr tmp = {0}; + return tmp; + } + + void invalidDataStreamHandler() { _invalidDataStreamCount++; } + + void scheduleSend() { _scheduleSendCount++; } + + void sendCompleted() { _sendCompletedCount++; } + + bool terminated() { return false; } + + void cachedSerialize( + const std::tr1::shared_ptr& field, + ByteBuffer* buffer) {field->serialize(buffer, this); } + + bool acquire( + std::tr1::shared_ptr const & client) + { + return false; + } + + bool directSerialize( + ByteBuffer *existingBuffer, + const char* toSerialize, + std::size_t elementCount, + std::size_t elementSize) {return false; } + + bool directDeserialize( + ByteBuffer *existingBuffer, + char* deserializeTo, + std::size_t elementCount, + std::size_t elementSize) { return false; } + + std::tr1::shared_ptr + cachedDeserialize(ByteBuffer* buffer) + { + return std::tr1::shared_ptr(); + } + + void release(pvAccessID clientId) {} + + epics::pvData::String getType() const + { + return epics::pvData::String("TCP"); + } + + const osiSockAddr* getRemoteAddress() const { return 0; } + + epics::pvData::int8 getRevision() const + { + return PVA_PROTOCOL_REVISION; + } + + std::size_t getReceiveBufferSize() const { return 16384; } + + epics::pvData::int16 getPriority() const { return 0; } + + std::size_t getSocketReceiveBufferSize() const + { + return 16384; + } + + void setRemoteRevision(epics::pvData::int8 revision) {} + + void setRemoteTransportSocketReceiveBufferSize( + std::size_t socketReceiveBufferSize) {} + + void setRemoteTransportReceiveBufferSize( + std::size_t remoteTransportReceiveBufferSize) {} + + void changedTransport() {} + + void flushSendQueue() { }; + + bool verify(epics::pvData::int32 timeoutMs) { return true;} + + void verified() {} + + void aliveNotification() {} + + bool isClosed() { return false; } + + + std::size_t _closedCount; + std::size_t _invalidDataStreamCount; + std::size_t _scheduleSendCount; + std::size_t _sendCompletedCount; + std::size_t _sendBufferFullCount; + std::size_t _readPollOneCount; + std::size_t _writePollOneCount; + bool _throwExceptionOnSend; + bool _readPayload; + bool _disconnected; + int _forcePayloadRead; + + std::auto_ptr _readBuffer; + epics::pvData::ByteBuffer _writeBuffer; + + std::vector _receivedAppMessages; + std::vector _receivedControlMessages; + + std::auto_ptr _readPollOneCallback; + std::auto_ptr _writePollOneCallback; + + + protected: + + void sendBufferFull(int tries) { + _sendBufferFullCount++; + _writeOpReady = false; + _writeMode = WAIT_FOR_READY_SIGNAL; + this->writePollOne(); + _writeMode = PROCESS_SEND_QUEUE; + } + }; + + + class CodecTest { + + public: + + int runAllTest() { + testPlan(5885); + testHeaderProcess(); + testInvalidHeaderMagic(); + testInvalidHeaderSegmentedInNormal(); + testInvalidHeaderPayloadNotRead(); + testHeaderSplitRead(); + testNonEmptyPayload(); + testNormalAlignment(); + testSplitAlignment(); + testSegmentedMessage(); + //testSegmentedInvalidInBetweenFlagsMessage(); + testSegmentedMessageAlignment(); + testSegmentedSplitMessage(); + testStartMessage(); + testStartMessageNonEmptyPayload(); + testStartMessageNormalAlignment(); + testStartMessageSegmentedMessage(); + testStartMessageSegmentedMessageAlignment(); + testReadNormalConnectionLoss(); + testSegmentedSplitConnectionLoss(); + testSendConnectionLoss(); + testEnqueueSendRequest(); + testEnqueueSendDirectRequest(); + testSendException(); + testSendHugeMessagePartes(); + testRecipient(); + testClearSendQueue(); + testInvalidArguments(); + testDefaultModes(); + testEnqueueSendRequestExceptionThrown(); + testBlockingProcessQueueTest(); + return testDone(); + } + + virtual ~CodecTest() {} + + protected: + + static const std::size_t DEFAULT_BUFFER_SIZE = 10240; + + private: + + void testHeaderProcess() { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x01); + codec._readBuffer->put((int8_t)0x23); + codec._readBuffer->putInt(0x456789AB); + codec._readBuffer->flip(); + + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 1, + "%s: codec._receivedControlMessages.size() == 1 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + + PVAMessage header = codec._receivedControlMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(header._flags == 0x01, + "%s: header._flags == 0x01", CURRENT_FUNCTION); + testOk(header._command == 0x23, + "%s: header._command == 0x23", CURRENT_FUNCTION); + testOk(header._payloadSize == 0x456789AB, + "%s: header._payloadSize == 0x456789AB", CURRENT_FUNCTION); + + codec.reset(); + + // two at the time, app and control + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x00); + codec._readBuffer->put((int8_t)0x20); + codec._readBuffer->putInt(0x00000000); + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x81); + codec._readBuffer->put((int8_t)0xEE); + codec._readBuffer->putInt(0xDDCCBBAA); + codec._readBuffer->flip(); + + codec.processRead(); + + testOk(0 == codec._invalidDataStreamCount, + "%s: 0 == codec._invalidDataStreamCount", + CURRENT_FUNCTION); + testOk(0 == codec._closedCount, + "%s: 0 == codec._closedCount", CURRENT_FUNCTION); + testOk(1 == codec._receivedControlMessages.size(), + "%s: 1 == codec._receivedControlMessages.size()", + CURRENT_FUNCTION); + testOk(1 == codec._receivedAppMessages.size(), + "%s: 1 == codec._receivedAppMessages.size()", + CURRENT_FUNCTION); + + + // app, no payload + header = codec._receivedAppMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x00, + "%s: header._flags == 0x00", CURRENT_FUNCTION); + testOk(header._command == 0x20, + "%s: header._command == 0x20", CURRENT_FUNCTION); + testOk(header._payloadSize == 0x00000000, + "%s: header._payloadSize == 0x00000000", CURRENT_FUNCTION); + + // control + header = codec._receivedControlMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x81, + "%s: header._flags == 0x81", CURRENT_FUNCTION); + testOk(header._command == 0xEE, + "%s: header._command == 0xEE", CURRENT_FUNCTION); + testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + "%s: header._payloadSize == 0xDDCCBBAA", + CURRENT_FUNCTION); + } + + + void testInvalidHeaderMagic() + { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readBuffer->put((int8_t)00); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x01); + codec._readBuffer->put((int8_t)0x23); + codec._readBuffer->putInt(0x456789AB); + codec._readBuffer->flip(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 1, + "%s: codec._invalidDataStreamCount == 1", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + } + + + void testInvalidHeaderSegmentedInNormal() + { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + int8_t invalidFlagsValues[] = + {(int8_t)0x20, (int8_t)(0x30+0x80)}; + + std::size_t size=sizeof(invalidFlagsValues)/sizeof(int8_t); + + for (std::size_t i = 0; i < size; i++) + { + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put(invalidFlagsValues[i]); + codec._readBuffer->put((int8_t)0x23); + codec._readBuffer->putInt(0); + codec._readBuffer->flip(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 1, + "%s: codec._invalidDataStreamCount == 1", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + } + } + + + void testInvalidHeaderPayloadNotRead() + { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x80); + codec._readBuffer->put((int8_t)0x23); + codec._readBuffer->putInt(0x456789AB); + codec._readBuffer->flip(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 1, + "%s: codec._invalidDataStreamCount == 1", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + } + + + void testHeaderSplitRead() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x01); + codec._readBuffer->flip(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + + codec._readBuffer->clear(); + + codec._readBuffer->put((int8_t)0x23); + codec._readBuffer->putInt(0x456789AB); + codec._readBuffer->flip(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 1, + "%s: codec._receivedControlMessages.size() == 1 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + + + // app, no payload + PVAMessage header = codec._receivedControlMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x01, + "%s: header._flags == 0x01", CURRENT_FUNCTION); + testOk(header._command == 0x23, + "%s: header._command == 0x23", CURRENT_FUNCTION); + testOk(header._payloadSize == 0x456789AB, + "%s: header._payloadSize == 0x456789AB", CURRENT_FUNCTION); + } + + + void testNonEmptyPayload() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + // no misalignment + codec._readPayload = true; + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x80); + codec._readBuffer->put((int8_t)0x23); + codec._readBuffer->putInt(PVA_ALIGNMENT); + for (int i = 0; i < PVA_ALIGNMENT; i++) + codec._readBuffer->put((int8_t)i); + codec._readBuffer->flip(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + // app, no payload + PVAMessage header = codec._receivedAppMessages[0]; + + testOk(header._payload.get() != 0, + "%s: header._payload.get() != 0", CURRENT_FUNCTION); + + header._payload->flip(); + + testOk( + (std::size_t)PVA_ALIGNMENT == header._payload->getLimit(), + "%s: PVA_ALIGNMENT == header._payload->getLimit()", + CURRENT_FUNCTION); + + } + + + void testNormalAlignment() + { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x80); + codec._readBuffer->put((int8_t)0x23); + int32_t payloadSize1 = PVA_ALIGNMENT+1; + codec._readBuffer->putInt(payloadSize1); + + for (int32_t i = 0; i < payloadSize1; i++) + codec._readBuffer->put((int8_t)i); + // align + std::size_t aligned = + AbstractCodec::alignedValue(payloadSize1, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize1; i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x80); + codec._readBuffer->put((int8_t)0x45); + + int32_t payloadSize2 = 2*PVA_ALIGNMENT-1; + codec._readBuffer->putInt(payloadSize2); + + for (int32_t i = 0; i < payloadSize2; i++) { + codec._readBuffer->put((int8_t)i); + } + + aligned = + AbstractCodec::alignedValue(payloadSize2, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize2; i < aligned; i++) { + codec._readBuffer->put((int8_t)0xFF); + } + + codec._readBuffer->flip(); + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 2, + "%s: codec._receivedAppMessages.size() == 2", + CURRENT_FUNCTION); + + PVAMessage msg = codec._receivedAppMessages[0]; + + testOk(msg._payloadSize == payloadSize1, + "%s: msg._payloadSize == payloadSize1", CURRENT_FUNCTION); + + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + + msg._payload->flip(); + + testOk((std::size_t)payloadSize1 == msg._payload->getLimit(), + "%s: payloadSize1, msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) { + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + } + + msg = codec._receivedAppMessages[1]; + + testOk(msg._payloadSize == payloadSize2, + "%s: msg._payloadSize == payloadSize2", CURRENT_FUNCTION); + + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk((std::size_t)payloadSize2 == msg._payload->getLimit(), + "%s: payloadSize2 == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) { + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + } + } + + + class ReadPollOneCallbackForTestSplitAlignment: + public ReadPollOneCallback { + + public: + + ReadPollOneCallbackForTestSplitAlignment( + TestCodec & codec, + int32_t payloadSize1, + int32_t payloadSize2): + _codec(codec), _payloadSize1(payloadSize1), + _payloadSize2(payloadSize2) {} + + + void readPollOne() { + + if (_codec._readPollOneCount == 1) + { + _codec._readBuffer->clear(); + for (int32_t i = _payloadSize1-2; + i < _payloadSize1; i++) { + _codec._readBuffer->put((int8_t)i); + } + + // align + std::size_t aligned = + AbstractCodec::alignedValue( + _payloadSize1, PVA_ALIGNMENT); + + for (std::size_t i = _payloadSize1; i < aligned; i++) + _codec._readBuffer->put((int8_t)0xFF); + + + _codec._readBuffer->put(PVA_MAGIC); + _codec._readBuffer->put(PVA_VERSION); + _codec._readBuffer->put((int8_t)0x80); + _codec._readBuffer->put((int8_t)0x45); + _codec._readBuffer->putInt(_payloadSize2); + + for (int32_t i = 0; i < _payloadSize2; i++) { + _codec._readBuffer->put((int8_t)i); + } + + _codec._readBuffer->flip(); + } + else if (_codec._readPollOneCount == 2) + { + _codec._readBuffer->clear(); + + std::size_t aligned = + AbstractCodec::alignedValue( + _payloadSize2, PVA_ALIGNMENT); + + for (std::size_t i = _payloadSize2; i < aligned; i++) { + _codec._readBuffer->put((int8_t)0xFF); + } + + _codec._readBuffer->flip(); + } + + else + throw std::logic_error("should not happen"); + } + + private: + TestCodec &_codec; + int8_t _payloadSize1; + int8_t _payloadSize2; + }; + + + void testSplitAlignment() + { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + // "<=" used instead of "==" to suppress compiler warning + if (PVA_ALIGNMENT <= 1) + return; + + codec._readPayload = true; + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x80); + codec._readBuffer->put((int8_t)0x23); + + int32_t payloadSize1 = PVA_ALIGNMENT+1; + codec._readBuffer->putInt(payloadSize1); + + for (int32_t i = 0; i < payloadSize1-2; i++) { + codec._readBuffer->put((int8_t)i); + } + + int32_t payloadSize2 = 2*PVA_ALIGNMENT-1; + + std::auto_ptr + readPollOneCallback( + new ReadPollOneCallbackForTestSplitAlignment + (codec, payloadSize1, payloadSize2)); + + codec._readPollOneCallback = readPollOneCallback; + + codec._readBuffer->flip(); + codec.processRead(); + + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 2, + "%s: codec._receivedAppMessages.size() == 2", + CURRENT_FUNCTION); + testOk(codec._readPollOneCount == 2, + "%s: codec._readPollOneCount == 2", CURRENT_FUNCTION); + + PVAMessage msg = codec._receivedAppMessages[0]; + + testOk(msg._payloadSize == payloadSize1, + "%s: msg._payloadSize == payloadSize1", CURRENT_FUNCTION); + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk(payloadSize1 = msg._payload->getLimit(), + "%s: payloadSize1 = msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) { + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + } + + msg = codec._receivedAppMessages[1]; + + testOk(msg._payloadSize == payloadSize2, + "%s: msg._payloadSize == payloadSize2", CURRENT_FUNCTION); + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk((std::size_t)payloadSize2 == msg._payload->getLimit(), + "%s: payloadSize2 == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) { + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + } + } + + + void testSegmentedMessage() + { + + // no misalignment + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + // 1st + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x90); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize1 = PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize1); + + int32_t c = 0; + for (int32_t i = 0; i < payloadSize1; i++) + codec._readBuffer->put((int8_t)(c++)); + + // 2nd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xB0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize2 = 2*PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize2); + + for (int32_t i = 0; i < payloadSize2; i++) + codec._readBuffer->put((int8_t)(c++)); + + // control in between + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x81); + codec._readBuffer->put((int8_t)0xEE); + codec._readBuffer->putInt(0xDDCCBBAA); + + // 3rd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xB0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize3 = PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize3); + + for (int32_t i = 0; i < payloadSize3; i++) + codec._readBuffer->put((int8_t)(c++)); + + // 4t (last) + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xA0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize4 = 2*PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize4); + + for (int32_t i = 0; i < payloadSize4; i++) + codec._readBuffer->put((int8_t)(c++)); + + codec._readBuffer->flip(); + + int32_t payloadSizeSum = + payloadSize1+payloadSize2+payloadSize3+payloadSize4; + + codec._forcePayloadRead = payloadSizeSum; + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 1, + "%s: codec._receivedControlMessages.size() == 1 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + testOk(codec._readPollOneCount == 0, + "%s: codec._readPollOneCount == 0", CURRENT_FUNCTION); + + + PVAMessage msg = codec._receivedAppMessages[0]; + + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk( + (std::size_t)payloadSizeSum == msg._payload->getLimit(), + "%s: payloadSizeSum == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) { + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getint8_t()", + CURRENT_FUNCTION); + } + + + msg = codec._receivedControlMessages[0]; + + testOk(msg._version == PVA_VERSION, + "%s: msg._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(msg._flags == 0x81, + "%s: msg._flags == 0x81", CURRENT_FUNCTION); + testOk(msg._command == 0xEE, + "%s: msg._command == 0xEE", CURRENT_FUNCTION); + testOk((std::size_t)msg._payloadSize == 0xDDCCBBAA, + "%s: msg._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); + } + + + void testSegmentedInvalidInBetweenFlagsMessage() + { + // no misalignment + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + // 1st + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x90); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize1 = PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize1); + + int32_t c = 0; + for (int32_t i = 0; i < payloadSize1; i++) + codec._readBuffer->put((int8_t)(c++)); + + // 2nd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + // invalid flag, should be 0xB0 + codec._readBuffer->put((int8_t)0x90); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize2 = 2*PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize2); + + for (int32_t i = 0; i < payloadSize2; i++) + codec._readBuffer->put((int8_t)(c++)); + + // control in between + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x81); + codec._readBuffer->put((int8_t)0xEE); + codec._readBuffer->putInt(0xDDCCBBAA); + + // 3rd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xB0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize3 = PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize3); + + for (int32_t i = 0; i < payloadSize3; i++) + codec._readBuffer->put((int8_t)(c++)); + + // 4t (last) + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xA0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize4 = 2*PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize4); + + for (int32_t i = 0; i < payloadSize4; i++) + codec._readBuffer->put((int8_t)(c++)); + + codec._readBuffer->flip(); + + int32_t payloadSizeSum = + payloadSize1+payloadSize2+payloadSize3+payloadSize4; + codec._forcePayloadRead = payloadSizeSum; + + try { + codec.processRead(); + testFail( + "%s: invalid_data_stream_exception, but not reported", + CURRENT_FUNCTION); + } catch(invalid_data_stream_exception &) { + testOk(true, "%s: invalid_data_stream_exception reported", + CURRENT_FUNCTION); + } + + testOk(codec._invalidDataStreamCount == 1, + "%s: codec._invalidDataStreamCount == 1", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + } + + + void testSegmentedMessageAlignment() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + // 1st + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x90); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize1 = PVA_ALIGNMENT+1; + codec._readBuffer->putInt(payloadSize1); + + int32_t c = 0; + for (int32_t i = 0; i < payloadSize1; i++) + codec._readBuffer->put((int8_t)(c++)); + + std::size_t aligned = + AbstractCodec::alignedValue(payloadSize1, PVA_ALIGNMENT); + for (std::size_t i = payloadSize1; i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + + // 2nd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xB0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize2 = 2*PVA_ALIGNMENT-1; + int32_t payloadSize2Real = + payloadSize2 + payloadSize1 % PVA_ALIGNMENT; + + codec._readBuffer->putInt(payloadSize2Real); + + // pre-message padding + for (int32_t i = 0; i < payloadSize1 % PVA_ALIGNMENT; i++) + codec._readBuffer->put((int8_t)0xEE); + + for (int32_t i = 0; i < payloadSize2; i++) + codec._readBuffer->put((int8_t)(c++)); + + aligned = + AbstractCodec::alignedValue( + payloadSize2Real, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize2Real; i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + // 3rd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xB0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize3 = PVA_ALIGNMENT+2; + int32_t payloadSize3Real = + payloadSize3 + payloadSize2Real % PVA_ALIGNMENT; + codec._readBuffer->putInt(payloadSize3Real); + + // pre-message padding required + for (int32_t i = 0; + i < payloadSize2Real % PVA_ALIGNMENT; i++) + codec._readBuffer->put((int8_t)0xEE); + + for (int32_t i = 0; i < payloadSize3; i++) + codec._readBuffer->put((int8_t)(c++)); + + aligned = + AbstractCodec::alignedValue( + payloadSize3Real, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize3Real; i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + // 4t (last) + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xA0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize4 = 2*PVA_ALIGNMENT+3; + int32_t payloadSize4Real = + payloadSize4 + payloadSize3Real % PVA_ALIGNMENT; + + codec._readBuffer->putInt(payloadSize4Real); + + // pre-message padding required + for (int32_t i = 0; + i < payloadSize3Real % PVA_ALIGNMENT; i++) + codec._readBuffer->put((int8_t)0xEE); + + for (int32_t i = 0; i < payloadSize4; i++) + codec._readBuffer->put((int8_t)(c++)); + + aligned = + AbstractCodec::alignedValue( + payloadSize4Real, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize4Real; i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + codec._readBuffer->flip(); + + int32_t payloadSizeSum = + payloadSize1+payloadSize2+payloadSize3+payloadSize4; + + codec._forcePayloadRead = payloadSizeSum; + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + testOk(codec._readPollOneCount == 0, + "%s: codec._readPollOneCount == 0", CURRENT_FUNCTION); + + PVAMessage msg = codec._receivedAppMessages[0]; + + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk( + (std::size_t)payloadSizeSum == msg._payload->getLimit(), + "%s: payloadSizeSum == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + } + + + class ReadPollOneCallbackForTestSegmentedSplitMessage: + public ReadPollOneCallback { + + public: + + ReadPollOneCallbackForTestSegmentedSplitMessage( + TestCodec & codec, + int32_t realReadBufferEnd): + _codec(codec), _realReadBufferEnd(realReadBufferEnd) {} + + + void readPollOne() { + if (_codec._readPollOneCount == 1) + { + _codec._readBuffer->setLimit(_realReadBufferEnd); + } + else + throw std::logic_error("should not happen"); + } + + private: + TestCodec &_codec; + std::size_t _realReadBufferEnd; + }; + + + void testSegmentedSplitMessage() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + for (int32_t firstMessagePayloadSize = 1; // cannot be zero + firstMessagePayloadSize <= 3*PVA_ALIGNMENT; + firstMessagePayloadSize++) + { + for (int32_t secondMessagePayloadSize = 0; + secondMessagePayloadSize <= 2*PVA_ALIGNMENT; + secondMessagePayloadSize++) + { + // cannot be zero + for (int32_t thirdMessagePayloadSize = 1; + thirdMessagePayloadSize <= 2*PVA_ALIGNMENT; + thirdMessagePayloadSize++) + { + std::size_t splitAt = 1; + while (true) + { + TestCodec codec(DEFAULT_BUFFER_SIZE, + DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + // 1st + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x90); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize1 = firstMessagePayloadSize; + codec._readBuffer->putInt(payloadSize1); + + int32_t c = 0; + for (int32_t i = 0; i < payloadSize1; i++) + codec._readBuffer->put((int8_t)(c++)); + + std::size_t aligned = + AbstractCodec::alignedValue( + payloadSize1, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize1; i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + // 2nd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xB0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize2 = secondMessagePayloadSize; + int payloadSize2Real = + payloadSize2 + payloadSize1 % PVA_ALIGNMENT; + + codec._readBuffer->putInt(payloadSize2Real); + + // pre-message padding + for (int32_t i = 0; + i < payloadSize1 % PVA_ALIGNMENT; i++) + codec._readBuffer->put((int8_t)0xEE); + + for (int32_t i = 0; i < payloadSize2; i++) + codec._readBuffer->put((int8_t)(c++)); + + aligned = + AbstractCodec::alignedValue( + payloadSize2Real, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize2Real; + i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + // 3rd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xA0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize3 = thirdMessagePayloadSize; + int32_t payloadSize3Real = + payloadSize3 + payloadSize2Real % PVA_ALIGNMENT; + + codec._readBuffer->putInt(payloadSize3Real); + + // pre-message padding required + for (int32_t i = 0; + i < payloadSize2Real % PVA_ALIGNMENT; i++) + codec._readBuffer->put((int8_t)0xEE); + + for (int32_t i = 0; i < payloadSize3; i++) + codec._readBuffer->put((int8_t)(c++)); + + aligned = + AbstractCodec::alignedValue( + payloadSize3Real, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize3Real; + i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + codec._readBuffer->flip(); + + std::size_t realReadBufferEnd = + codec._readBuffer->getLimit(); + + if (splitAt++ == realReadBufferEnd) + break; + + codec._readBuffer->setLimit(splitAt); + + std::auto_ptr + readPollOneCallback( + new ReadPollOneCallbackForTestSegmentedSplitMessage + (codec, realReadBufferEnd)); + + codec._readPollOneCallback = readPollOneCallback; + + + int32_t payloadSizeSum = + payloadSize1+payloadSize2+payloadSize3; + + codec._forcePayloadRead = payloadSizeSum; + + codec.processRead(); + + while (codec._invalidDataStreamCount == 0 && + codec._readBuffer->getPosition() != + realReadBufferEnd) + { + codec._readPollOneCount++; + codec._readPollOneCallback->readPollOne(); + codec.processRead(); + } + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + + if (splitAt == realReadBufferEnd) { + testOk(0 == codec._readPollOneCount, + "%s: 0 == codec._readPollOneCount", + CURRENT_FUNCTION); + } + else { + testOk(1 == codec._readPollOneCount, + "%s: 1 == codec._readPollOneCount", + CURRENT_FUNCTION); + } + + PVAMessage msg = codec._receivedAppMessages[0]; + + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk((std::size_t)payloadSizeSum == + msg._payload->getLimit(), + "%s: payloadSizeSum == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) { + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + } + } + } + } + } + } + + + void testStartMessage() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec.putControlMessage((int8_t)0x23, 0x456789AB); + + codec.transferToReadBuffer(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 1, + "%s: codec._receivedControlMessages.size() == 1 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + + + PVAMessage header = codec._receivedControlMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x81, + "%s: header._flags == 0x81", CURRENT_FUNCTION); + testOk(header._command == 0x23, + "%s: header._command == 0x23", CURRENT_FUNCTION); + testOk(header._payloadSize == 0x456789AB, + "%s: header._payloadSize == 0x456789AB", CURRENT_FUNCTION); + + codec.reset(); + + // two at the time, app and control + codec.setByteOrder(EPICS_ENDIAN_LITTLE); + codec.startMessage((int8_t)0x20, 0x00000000); + codec.endMessage(); + + codec.setByteOrder(EPICS_ENDIAN_BIG); + codec.putControlMessage((int8_t)0xEE, 0xDDCCBBAA); + + codec.transferToReadBuffer(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 1, + "%s: codec._receivedControlMessages.size() == 1 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + // app, no payload + header = codec._receivedAppMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x00, + "%s: header._flags == 0x00", CURRENT_FUNCTION); + testOk(header._command == 0x20, + "%s: header._command == 0x20", CURRENT_FUNCTION); + testOk(header._payloadSize == 0x00000000, + "%s: header._payloadSize == 0x00000000", CURRENT_FUNCTION); + + // control + header = codec._receivedControlMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x81, + "%s: header._flags == 0x81", CURRENT_FUNCTION); + testOk(header._command == 0xEE, + "%s: header._command == 0xEE", CURRENT_FUNCTION); + testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); + } + + + void testStartMessageNonEmptyPayload() + { + // no misalignment + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + codec.startMessage((int8_t)0x23, 0); + + codec.ensureBuffer((std::size_t)PVA_ALIGNMENT); + for (int32_t i = 0; i < PVA_ALIGNMENT; i++) + codec.getSendBuffer()->put((int8_t)i); + + codec.endMessage(); + + codec.transferToReadBuffer(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + // app, no payload + PVAMessage header = codec._receivedAppMessages[0]; + + testOk(header._payload.get() != 0, + "%s: header._payload.get() != 0", CURRENT_FUNCTION); + + header._payload->flip(); + + testOk((std::size_t)PVA_ALIGNMENT == + header._payload->getLimit(), + "%s: PVA_ALIGNMENT == header._payload->getLimit()", + CURRENT_FUNCTION); + } + + + void testStartMessageNormalAlignment() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + codec.startMessage((int8_t)0x23, 0); + int32_t payloadSize1 = PVA_ALIGNMENT+1; + codec.ensureBuffer(payloadSize1); + + for (int32_t i = 0; i < payloadSize1; i++) + codec.getSendBuffer()->put((int8_t)i); + + codec.endMessage(); + + codec.startMessage((int8_t)0x45, 0); + int32_t payloadSize2 = 2*PVA_ALIGNMENT-1; + codec.ensureBuffer(payloadSize2); + + for (int32_t i = 0; i < payloadSize2; i++) + codec.getSendBuffer()->put((int8_t)i); + + codec.endMessage(); + + codec.transferToReadBuffer(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 2, + "%s: codec._receivedAppMessages.size() == 2", + CURRENT_FUNCTION); + + PVAMessage msg = codec._receivedAppMessages[0]; + + testOk(msg._payloadSize == payloadSize1, + "%s: msg._payloadSize == payloadSize1", CURRENT_FUNCTION); + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk((std::size_t)payloadSize1 == + msg._payload->getLimit(), + "%s: payloadSize1 == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + + msg = codec._receivedAppMessages[1]; + + testOk(msg._payloadSize == payloadSize2, + "%s: msg._payloadSize == payloadSize2", CURRENT_FUNCTION); + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk((std::size_t)payloadSize2 == + msg._payload->getLimit(), + "%s: payloadSize2 == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + } + + + void testStartMessageSegmentedMessage() + { + // no misalignment + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + codec.startMessage((int8_t)0x01, 0); + + int32_t c = 0; + + int32_t payloadSize1 = PVA_ALIGNMENT; + for (int32_t i = 0; i < payloadSize1; i++) + codec.getSendBuffer()->put((int8_t)(c++)); + + codec.flush(false); + + int32_t payloadSize2 = 2*PVA_ALIGNMENT; + for (int32_t i = 0; i < payloadSize2; i++) + codec.getSendBuffer()->put((int8_t)(c++)); + + codec.flush(false); + + int32_t payloadSize3 = PVA_ALIGNMENT; + for (int32_t i = 0; i < payloadSize3; i++) + codec.getSendBuffer()->put((int8_t)(c++)); + + codec.flush(false); + + int32_t payloadSize4 = 2*PVA_ALIGNMENT; + for (int32_t i = 0; i < payloadSize4; i++) + codec.getSendBuffer()->put((int8_t)(c++)); + + codec.endMessage(); + + codec.transferToReadBuffer(); + + int32_t payloadSizeSum = + payloadSize1+payloadSize2+payloadSize3+payloadSize4; + + codec._forcePayloadRead = payloadSizeSum; + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + testOk(codec._readPollOneCount == 0, + "%s: codec._readPollOneCount == 0", CURRENT_FUNCTION); + + PVAMessage msg = codec._receivedAppMessages[0]; + + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk((std::size_t)payloadSizeSum == + msg._payload->getLimit(), + "%s: payloadSizeSum == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) + testOk((int8_t)i == msg._payload->getByte(), + "%s: (int8_t)i == msg._payload->getByte()", + CURRENT_FUNCTION); + } + + + void testStartMessageSegmentedMessageAlignment() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + for (int32_t firstMessagePayloadSize = 1; // cannot be zero + firstMessagePayloadSize <= 3*PVA_ALIGNMENT; + firstMessagePayloadSize++) + { + for (int32_t secondMessagePayloadSize = 0; + secondMessagePayloadSize <= 2*PVA_ALIGNMENT; + secondMessagePayloadSize++) + { + // cannot be zero + for (int32_t thirdMessagePayloadSize = 1; + thirdMessagePayloadSize <= 2*PVA_ALIGNMENT; + thirdMessagePayloadSize++) + { + // cannot be zero + for (int32_t fourthMessagePayloadSize = 1; + fourthMessagePayloadSize <= 2*PVA_ALIGNMENT; + fourthMessagePayloadSize++) + { + TestCodec codec(DEFAULT_BUFFER_SIZE, + DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + codec.startMessage((int8_t)0x01, 0); + + int32_t c = 0; + + int32_t payloadSize1 = firstMessagePayloadSize; + for (int32_t i = 0; i < payloadSize1; i++) + codec.getSendBuffer()->put((int8_t)(c++)); + + codec.flush(false); + + int32_t payloadSize2 = secondMessagePayloadSize; + for (int32_t i = 0; i < payloadSize2; i++) + codec.getSendBuffer()->put((int8_t)(c++)); + + codec.flush(false); + + int32_t payloadSize3 = thirdMessagePayloadSize; + for (int32_t i = 0; i < payloadSize3; i++) + codec.getSendBuffer()->put((int8_t)(c++)); + + codec.flush(false); + + int32_t payloadSize4 = fourthMessagePayloadSize; + for (int32_t i = 0; i < payloadSize4; i++) + codec.getSendBuffer()->put((int8_t)(c++)); + + codec.endMessage(); + + codec.transferToReadBuffer(); + + int32_t payloadSizeSum = + payloadSize1+payloadSize2+payloadSize3 + +payloadSize4; + + codec._forcePayloadRead = payloadSizeSum; + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + testOk(codec._readPollOneCount == 0, + "%s: codec._readPollOneCount == 0", + CURRENT_FUNCTION); + + PVAMessage msg = codec._receivedAppMessages[0]; + + testOk(msg._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + msg._payload->flip(); + + testOk((std::size_t)payloadSizeSum == + msg._payload->getLimit(), + "%s: payloadSizeSum == msg._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < msg._payloadSize; i++) { + if ((int8_t)i != msg._payload->getByte()) { + testFail( + "%s: (int8_t)%d == msg._payload->getByte()", + CURRENT_FUNCTION, (int8_t)i); + } + } + } + } + } + } + } + + + void testReadNormalConnectionLoss() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + codec._disconnected = true; + + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x01); + codec._readBuffer->put((int8_t)0x23); + codec._readBuffer->putInt(0x456789AB); + + codec._readBuffer->flip(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 1, + "%s: codec._closedCount == 1", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + } + + + class ReadPollOneCallbackForTestSegmentedSplitConnectionLoss: + public ReadPollOneCallback { + + public: + + ReadPollOneCallbackForTestSegmentedSplitConnectionLoss( + TestCodec & codec): _codec(codec) {} + + + void readPollOne() { + if (_codec._readPollOneCount == 1) + { + _codec._disconnected = true; + } + else + throw std::logic_error("should not happen"); + } + + private: + TestCodec &_codec; + }; + + + void testSegmentedSplitConnectionLoss() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + + for (int32_t firstMessagePayloadSize = 1; // cannot be zero + firstMessagePayloadSize <= 3*PVA_ALIGNMENT; + firstMessagePayloadSize++) + { + for (int32_t secondMessagePayloadSize = 0; + secondMessagePayloadSize <= 2*PVA_ALIGNMENT; + secondMessagePayloadSize++) + { + // cannot be zero + for (int32_t thirdMessagePayloadSize = 1; + thirdMessagePayloadSize <= 2*PVA_ALIGNMENT; + thirdMessagePayloadSize++) + { + std::size_t splitAt = 1; + + while (true) + { + TestCodec codec(DEFAULT_BUFFER_SIZE, + DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + + // 1st + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0x90); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize1 = firstMessagePayloadSize; + codec._readBuffer->putInt(payloadSize1); + + int32_t c = 0; + for (int32_t i = 0; i < payloadSize1; i++) + codec._readBuffer->put((int8_t)(c++)); + + std::size_t aligned = + AbstractCodec::alignedValue( + payloadSize1, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize1; i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + // 2nd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xB0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize2 = secondMessagePayloadSize; + int32_t payloadSize2Real = + payloadSize2 + payloadSize1 % PVA_ALIGNMENT; + + codec._readBuffer->putInt(payloadSize2Real); + + // pre-message padding + for (int32_t i = 0; + i < payloadSize1 % PVA_ALIGNMENT; i++) + codec._readBuffer->put((int8_t)0xEE); + + for (int32_t i = 0; i < payloadSize2; i++) + codec._readBuffer->put((int8_t)(c++)); + + aligned = + AbstractCodec::alignedValue( + payloadSize2Real, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize2Real; + i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + // 3rd + codec._readBuffer->put(PVA_MAGIC); + codec._readBuffer->put(PVA_VERSION); + codec._readBuffer->put((int8_t)0xA0); + codec._readBuffer->put((int8_t)0x01); + + int32_t payloadSize3 = thirdMessagePayloadSize; + int32_t payloadSize3Real = + payloadSize3 + payloadSize2Real % PVA_ALIGNMENT; + + codec._readBuffer->putInt(payloadSize3Real); + + // pre-message padding required + for (int32_t i = 0; + i < payloadSize2Real % PVA_ALIGNMENT; i++) + codec._readBuffer->put((int8_t)0xEE); + + for (int32_t i = 0; i < payloadSize3; i++) + codec._readBuffer->put((int8_t)(c++)); + + aligned = + AbstractCodec::alignedValue( + payloadSize3Real, PVA_ALIGNMENT); + + for (std::size_t i = payloadSize3Real; + i < aligned; i++) + codec._readBuffer->put((int8_t)0xFF); + + codec._readBuffer->flip(); + + std::size_t realReadBufferEnd = + codec._readBuffer->getLimit(); + + if (splitAt++ == realReadBufferEnd-1) + break; + + codec._readBuffer->setLimit(splitAt); + + std::auto_ptr + readPollOneCallback( new + ReadPollOneCallbackForTestSegmentedSplitConnectionLoss + (codec)); + + + codec._readPollOneCallback = readPollOneCallback; + + int32_t payloadSizeSum = + payloadSize1+payloadSize2+payloadSize3; + + codec._forcePayloadRead = payloadSizeSum; + + codec.processRead(); + + while (codec._closedCount == 0 && + codec._invalidDataStreamCount == 0 && + codec._readBuffer->getPosition() != + realReadBufferEnd) + { + codec._readPollOneCount++; + codec._readPollOneCallback->readPollOne(); + codec.processRead(); + } + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 1, + "%s: codec._closedCount == 1", CURRENT_FUNCTION); + } + } + } + } + } + + + void testSendConnectionLoss() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + codec._disconnected = true; + + codec.putControlMessage((int8_t)0x23, 0x456789AB); + + try + { + codec.transferToReadBuffer(); + testFail("%s: connection lost, but not reported", + CURRENT_FUNCTION); + } + catch (connection_closed_exception & ) { + testOk(true, "%s: connection closed exception expected", + CURRENT_FUNCTION); + } + + testOk(codec._closedCount == 1, + "%s: codec._closedCount == 1", CURRENT_FUNCTION); + } + + + class TransportSenderForTestEnqueueSendRequest: + public TransportSender { + public: + + TransportSenderForTestEnqueueSendRequest( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.startMessage((int8_t)0x20, 0x00000000); + _codec.endMessage(); + } + + private: + TestCodec &_codec; + }; + + + class TransportSender2ForTestEnqueueSendRequest: + public TransportSender { + public: + + TransportSender2ForTestEnqueueSendRequest( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.putControlMessage((int8_t)0xEE, 0xDDCCBBAA); + } + + private: + TestCodec &_codec; + }; + + + void testEnqueueSendRequest() + { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + std::tr1::shared_ptr sender = + std::tr1::shared_ptr( + new TransportSenderForTestEnqueueSendRequest(codec)); + + std::tr1::shared_ptr sender2 = + std::tr1::shared_ptr( + new TransportSender2ForTestEnqueueSendRequest(codec)); + + // process + codec.enqueueSendRequest(sender); + codec.enqueueSendRequest(sender2); + codec.processSendQueue(); + + codec.transferToReadBuffer(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 1, + "%s: codec._receivedControlMessages.size() == 1 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + + // app, no payload + PVAMessage header = codec._receivedAppMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x80, + "%s: header._flags == 0x80", CURRENT_FUNCTION); + testOk(header._command == 0x20, + "%s: header._command == 0x20", CURRENT_FUNCTION); + testOk(header._payloadSize == 0x00000000, + "%s: header._payloadSize == 0x00000000", CURRENT_FUNCTION); + + // control + header = codec._receivedControlMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x81, + "%s: header._flags == 0x81", CURRENT_FUNCTION); + testOk(header._command == 0xEE, + "%s: header._command == 0xEE", CURRENT_FUNCTION); + testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); + } + + + class TransportSenderForTestEnqueueSendDirectRequest: + public TransportSender { + public: + + TransportSenderForTestEnqueueSendDirectRequest( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.startMessage((int8_t)0x20, 0x00000000); + _codec.endMessage(); + } + + private: + TestCodec &_codec; + }; + + + class TransportSender2ForTestEnqueueSendDirectRequest: + public TransportSender { + public: + + TransportSender2ForTestEnqueueSendDirectRequest( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.putControlMessage((int8_t)0xEE, + 0xDDCCBBAA); + } + + private: + TestCodec &_codec; + }; + + + void testEnqueueSendDirectRequest() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + std::tr1::shared_ptr sender = + std::tr1::shared_ptr( + new TransportSenderForTestEnqueueSendDirectRequest(codec)); + std::tr1::shared_ptr sender2 = + std::tr1::shared_ptr( + new TransportSender2ForTestEnqueueSendDirectRequest + (codec)); + + + // thread not right + codec.enqueueSendRequest(sender, PVA_MESSAGE_HEADER_SIZE); + + + testOk(1 == codec._scheduleSendCount, + "%s: 1 == codec._scheduleSendCount", CURRENT_FUNCTION); + testOk(0 == codec._receivedControlMessages.size(), + "%s: 0 == codec._receivedControlMessages.size()", + CURRENT_FUNCTION); + testOk(0 == codec._receivedAppMessages.size(), + "%s: 0 == codec._receivedAppMessages.size()", + CURRENT_FUNCTION); + + codec.setSenderThread(); + + // not empty queue + codec.enqueueSendRequest(sender2, PVA_MESSAGE_HEADER_SIZE); + + testOk(2 == codec._scheduleSendCount, + "%s: 2 == codec._scheduleSendCount", CURRENT_FUNCTION); + testOk(0 == codec._receivedControlMessages.size(), + "%s: 0 == codec._receivedControlMessages.size()", + CURRENT_FUNCTION); + testOk(0 == codec._receivedAppMessages.size(), + "%s: 0 == codec._receivedAppMessages.size()", + CURRENT_FUNCTION); + + // send will be triggered after last + //was processed + testOk(0 == codec._sendCompletedCount, + "%s: 0 == codec._sendCompletedCount", CURRENT_FUNCTION); + + codec.processSendQueue(); + + testOk(1 == codec._sendCompletedCount, + "%s: 1 == codec._sendCompletedCount", CURRENT_FUNCTION); + + codec.transferToReadBuffer(); + + codec.processRead(); + + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 1, + "%s: codec._receivedControlMessages.size() == 1 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + // app, no payload + PVAMessage header = codec._receivedAppMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x80, + "%s: header._flags == 0x80", CURRENT_FUNCTION); + testOk(header._command == 0x20, + "%s: header._command == 0x20", CURRENT_FUNCTION); + testOk(header._payloadSize == 0x00000000, + "%s: header._payloadSize == 0x00000000", CURRENT_FUNCTION); + + + // control + header = codec._receivedControlMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x81, + "%s: header._flags == 0x81", CURRENT_FUNCTION); + testOk(header._command == 0xEE, + "%s: header._command == 0xEE", CURRENT_FUNCTION); + testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); + + + testOk(0 == codec.getSendBuffer()->getPosition(), + "%s: 0 == codec.getSendBuffer()->getPosition()", + CURRENT_FUNCTION); + + // now queue is empty and thread is right + codec.enqueueSendRequest(sender2, PVA_MESSAGE_HEADER_SIZE); + + testOk((std::size_t)PVA_MESSAGE_HEADER_SIZE == + codec.getSendBuffer()->getPosition(), + "%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(); + + testOk(2 == codec._sendCompletedCount, + "%s: 2 == codec._sendCompletedCount", CURRENT_FUNCTION); + + + codec.transferToReadBuffer(); + codec.processRead(); + + testOk(codec._receivedControlMessages.size() == 2, + "%s: codec._receivedControlMessages.size() == 2 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + header = codec._receivedControlMessages[1]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x81, + "%s: header._flags == 0x81", CURRENT_FUNCTION); + testOk(header._command == 0xEE, + "%s: header._command == 0xEE", CURRENT_FUNCTION); + testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); + } + + + + class TransportSenderForTestSendPerPartes: + public TransportSender { + public: + + TransportSenderForTestSendPerPartes( + TestCodec & codec, std::size_t bytesToSend): + _codec(codec), _bytesToSent(bytesToSend) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.startMessage((int8_t)0x12, _bytesToSent); + + for (std::size_t i = 0; i < _bytesToSent; i++) + _codec.getSendBuffer()->put((int8_t)i); + + _codec.endMessage(); + } + + private: + TestCodec &_codec; + std::size_t _bytesToSent; + }; + + + void testSendPerPartes() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + std::size_t bytesToSent = + DEFAULT_BUFFER_SIZE - 2*PVA_MESSAGE_HEADER_SIZE; + + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + std::tr1::shared_ptr sender = + std::tr1::shared_ptr( + new TransportSenderForTestSendPerPartes( + codec, bytesToSent)); + + codec._readPayload = true; + + // process + codec.enqueueSendRequest(sender); + codec.processSendQueue(); + + codec.transferToReadBuffer(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + + // app + PVAMessage header = codec._receivedAppMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == 0x80, + "%s: header._flags == 0x80", CURRENT_FUNCTION); + testOk(header._command == 0x12, + "%s: header._command == 0x12", CURRENT_FUNCTION); + testOk((std::size_t)header._payloadSize == bytesToSent, + "%s: header._payloadSize == bytesToSent", + CURRENT_FUNCTION); + + + testOk(header._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + header._payload->flip(); + + testOk(bytesToSent == header._payload->getLimit(), + "%s: bytesToSent == header._payload->getLimit()", + CURRENT_FUNCTION); + + for (int32_t i = 0; i < header._payloadSize; i++) { + if ((int8_t)i != header._payload->getByte()) { + testFail("%s: (int8_t)%d == header._payload->getByte()", + CURRENT_FUNCTION, i); + } + } + } + + + class TransportSenderForTestSendException: + public TransportSender { + public: + + TransportSenderForTestSendException( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.putControlMessage((int8_t)0x01, 0x00112233); + } + + private: + TestCodec &_codec; + }; + + + void testSendException() + { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + std::tr1::shared_ptr sender = + std::tr1::shared_ptr( + new TransportSenderForTestSendException(codec)); + + codec._throwExceptionOnSend = true; + + // process + codec.enqueueSendRequest(sender); + + try + { + codec.processSendQueue(); + testFail("%s: ConnectionClosedException expected", + CURRENT_FUNCTION); + } catch (connection_closed_exception &) { + // OK + } + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 1, + "%s: codec._closedCount == 1", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + } + + + class TransportSenderForTestSendHugeMessagePartes: + public TransportSender { + public: + + TransportSenderForTestSendHugeMessagePartes( + TestCodec & codec, std::size_t bytesToSend): + _codec(codec), _bytesToSent(bytesToSend) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.startMessage((int8_t)0x12, 0); + std::size_t toSend = _bytesToSent; + int32_t c = 0; + while (toSend > 0) + { + std::size_t sendNow = std::min(toSend, + AbstractCodec::MAX_ENSURE_BUFFER_SIZE); + + _codec.ensureBuffer(sendNow); + for (std::size_t i = 0; i < sendNow; i++) + _codec.getSendBuffer()->put((int8_t)(c++)); + toSend -= sendNow; + } + _codec.endMessage(); + } + + private: + TestCodec &_codec; + std::size_t _bytesToSent; + }; + + + class WritePollOneCallbackForTestSendHugeMessagePartes: + public WritePollOneCallback { + public: + + WritePollOneCallbackForTestSendHugeMessagePartes( + TestCodec &codec): _codec(codec) {} + + void writePollOne() { + _codec.processWrite(); // this should return immediately + + // now we fake reading + _codec._writeBuffer.flip(); + + // in this test we made sure readBuffer is big enough + while(_codec._writeBuffer.getRemaining() > 0) + { + _codec._readBuffer->putByte( + _codec._writeBuffer.getByte()); + } + + _codec._writeBuffer.clear(); + } + private: + TestCodec & _codec; + }; + + + void testSendHugeMessagePartes() + { + + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + std::size_t bytesToSent = 10*DEFAULT_BUFFER_SIZE+1; + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + codec._readPayload = true; + codec._readBuffer.reset( + new ByteBuffer(11*DEFAULT_BUFFER_SIZE)); + + std::auto_ptr + writePollOneCallback( + new WritePollOneCallbackForTestSendHugeMessagePartes + (codec)); + + std::tr1::shared_ptr sender = + std::tr1::shared_ptr( + new TransportSenderForTestSendHugeMessagePartes( + codec, bytesToSent)); + + codec._writePollOneCallback = writePollOneCallback; + + + // process + codec.enqueueSendRequest(sender); + codec.processSendQueue(); + + codec.addToReadBuffer(); + + codec._forcePayloadRead = bytesToSent; + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 0, + "%s: codec._closedCount == 0", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 1, + "%s: codec._receivedAppMessages.size() == 1", + CURRENT_FUNCTION); + + // app + PVAMessage header = codec._receivedAppMessages[0]; + + testOk(header._version == PVA_VERSION, + "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); + testOk(header._flags == (int8_t)(0x80 | 0x10), + "%s: header._flags == (int8_t)(0x80 | 0x10)", + CURRENT_FUNCTION); + testOk(header._command == 0x12, + "%s: header._command == 0x12", CURRENT_FUNCTION); + + testOk(header._payload.get() != 0, + "%s: msg._payload.get() != 0", CURRENT_FUNCTION); + + header._payload->flip(); + + testOk(bytesToSent == header._payload->getLimit(), + "%s: bytesToSent == header._payload->getLimit()", + CURRENT_FUNCTION); + + + for (int32_t i = 0; i < header._payloadSize; i++) { + if ((int8_t)i != header._payload->getByte()) { + testFail("%s: (int8_t)%d == header._payload->getByte()", + CURRENT_FUNCTION, i); + } + } + + } + + + void testRecipient() + { + // nothing to test, depends on implementation + testSkip(1, " testRecipient()"); + } + + + class TransportSenderForTestClearSendQueue: + public TransportSender { + public: + + TransportSenderForTestClearSendQueue( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.startMessage((int8_t)0x20, 0x00000000); + _codec.endMessage(); + } + + private: + TestCodec &_codec; + }; + + + class TransportSender2ForTestClearSendQueue: + public TransportSender { + public: + + TransportSender2ForTestClearSendQueue( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.putControlMessage((int8_t)0xEE, 0xDDCCBBAA); + } + + private: + TestCodec &_codec; + }; + + + void testClearSendQueue() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + std::tr1::shared_ptr sender = + std::tr1::shared_ptr( + new TransportSenderForTestClearSendQueue(codec)); + + std::tr1::shared_ptr sender2 = + std::tr1::shared_ptr( + 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); + + try + { + // too small + TestCodec codec(1,DEFAULT_BUFFER_SIZE); + testFail("%s: too small buffer accepted", + CURRENT_FUNCTION); + } catch (std::exception &) { + // OK + } + + try + { + // too small + TestCodec codec(DEFAULT_BUFFER_SIZE,1); + testFail("%s: too small buffer accepted", + CURRENT_FUNCTION); + } catch (std::exception &) { + // OK + } + + if (PVA_ALIGNMENT > 1) + { + try + { + // non aligned + TestCodec codec(2*AbstractCodec::MAX_ENSURE_SIZE+1, + DEFAULT_BUFFER_SIZE); + + testFail("%s: non-aligned buffer size accepted", + CURRENT_FUNCTION); + + } catch (std::exception &) { + // OK + } + + try + { + // non aligned + TestCodec codec(DEFAULT_BUFFER_SIZE, + 2*AbstractCodec::MAX_ENSURE_SIZE+1); + + testFail("%s: non-aligned buffer size accepted", + CURRENT_FUNCTION); + + } catch (std::exception &) { + // OK + } + } + + TestCodec codec(DEFAULT_BUFFER_SIZE, + DEFAULT_BUFFER_SIZE); + + try + { + codec.ensureBuffer(DEFAULT_BUFFER_SIZE+1); + testFail("%s: too big size accepted", + CURRENT_FUNCTION); + } catch (std::exception &) { + // OK + } + + try + { + codec.ensureData(AbstractCodec::MAX_ENSURE_DATA_SIZE+1); + testFail("%s: too big size accepted", CURRENT_FUNCTION); + } catch (std::exception &) { + // OK + } + } + + + void testDefaultModes() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + testOk(NORMAL== codec.getReadMode(), + "%s: NORMAL== codec.getReadMode()", CURRENT_FUNCTION); + testOk(PROCESS_SEND_QUEUE == codec.getWriteMode(), + "%s: PROCESS_SEND_QUEUE == codec.getWriteMode()", + CURRENT_FUNCTION); + } + + + class TransportSenderForTestEnqueueSendRequestExceptionThrown: + public TransportSender { + public: + + TransportSenderForTestEnqueueSendRequestExceptionThrown( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + throw connection_closed_exception( + "expected test exception"); + } + + private: + TestCodec &_codec; + }; + + + class TransportSender2ForTestEnqueueSendRequestExceptionThrown: + public TransportSender { + public: + + TransportSender2ForTestEnqueueSendRequestExceptionThrown( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.putControlMessage((int8_t)0xEE, 0xDDCCBBAA); + } + + private: + TestCodec &_codec; + }; + + + void testEnqueueSendRequestExceptionThrown() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + TestCodec codec(DEFAULT_BUFFER_SIZE,DEFAULT_BUFFER_SIZE); + + std::tr1::shared_ptr sender = + std::tr1::shared_ptr( + new + TransportSenderForTestEnqueueSendRequestExceptionThrown + (codec)); + + std::tr1::shared_ptr sender2 = + std::tr1::shared_ptr(new + TransportSender2ForTestEnqueueSendRequestExceptionThrown( + codec)); + + + // process + codec.enqueueSendRequest(sender); + codec.enqueueSendRequest(sender2); + + try + { + codec.processSendQueue(); + testFail("%s: ConnectionClosedException expected", + CURRENT_FUNCTION); + + } catch (connection_closed_exception &) { + // OK + } + + codec.transferToReadBuffer(); + + codec.processRead(); + + testOk(codec._invalidDataStreamCount == 0, + "%s: codec._invalidDataStreamCount == 0", + CURRENT_FUNCTION); + testOk(codec._closedCount == 1, + "%s: codec._closedCount == 1", CURRENT_FUNCTION); + testOk(codec._receivedControlMessages.size() == 0, + "%s: codec._receivedControlMessages.size() == 0 ", + CURRENT_FUNCTION); + testOk(codec._receivedAppMessages.size() == 0, + "%s: codec._receivedAppMessages.size() == 0", + CURRENT_FUNCTION); + } + + + class TransportSenderForTestBlockingProcessQueueTest: + public TransportSender { + public: + + TransportSenderForTestBlockingProcessQueueTest( + TestCodec & codec): _codec(codec) {} + + void unlock() { + } + + void lock() { + } + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control) + { + _codec.putControlMessage((int8_t)0x01, 0x00112233); + } + + private: + TestCodec &_codec; + }; + + + class ValueHolder { + public: + ValueHolder( + TestCodec &testCodec, + AtomicValue &processTreadExited): + _testCodec(testCodec), + _processTreadExited(processTreadExited) {} + + TestCodec &_testCodec; + AtomicValue & _processTreadExited; + }; + + + void testBlockingProcessQueueTest() + { + testDiag("BEGIN TEST %s:", CURRENT_FUNCTION); + + TestCodec codec(DEFAULT_BUFFER_SIZE, + DEFAULT_BUFFER_SIZE, true); + + _processTreadExited.getAndSet(false); + std::tr1::shared_ptr sender = + std::tr1::shared_ptr( + new TransportSenderForTestBlockingProcessQueueTest(codec)); + + ValueHolder valueHolder(codec, _processTreadExited); + + epicsThreadCreate( + "testBlockingProcessQueueTest-processThread", + epicsThreadPriorityMedium, + epicsThreadGetStackSize( + epicsThreadStackMedium), + CodecTest::blockingProcessQueueThread, + &valueHolder); + + epicsThreadSleep(3); + + testOk(_processTreadExited.get() == false, + "%s: _processTreadExited.get() == false", + CURRENT_FUNCTION); + + // let's put something into it + + codec.enqueueSendRequest(sender); + + epicsThreadSleep(1); + + testOk((std::size_t)PVA_MESSAGE_HEADER_SIZE == + codec._writeBuffer.getPosition(), + "%s: PVA_MESSAGE_HEADER_SIZE == " + "codec._writeBuffer.getPosition()", + CURRENT_FUNCTION); + + codec.endBlockedProcessSendQueue(); + + epicsThreadSleep(1); + + testOk(_processTreadExited.get() == true, + "%s: _processTreadExited.get() == true", CURRENT_FUNCTION); + } + + private: + + void static blockingProcessQueueThread(void *param) { + ValueHolder *valueHolder = static_cast(param); + // this should block + valueHolder->_testCodec.processSendQueue(); + valueHolder->_processTreadExited.getAndSet(true); + } + + AtomicValue _processTreadExited; + }; + } +} + + +using namespace epics::pvAccess; + +MAIN(testCodec) +{ + CodecTest codecTest; + return codecTest.runAllTest(); +} From 5f0f7b9fde5b1b86386607c14533b52f98321e92 Mon Sep 17 00:00:00 2001 From: damjankumar Date: Thu, 13 Feb 2014 15:35:49 +0100 Subject: [PATCH 03/19] testing push --- testApp/remote/testCodec.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 716ca42..814b384 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -6,6 +6,7 @@ #define NOMINMAX #endif +//testing #include #include From 5ab2ead581178a30c9e8b567455fba3ba6ce09e3 Mon Sep 17 00:00:00 2001 From: damjankumar Date: Thu, 13 Feb 2014 15:52:02 +0100 Subject: [PATCH 04/19] internalDestroy problem solved + memory leak fixed in abstract codec --- pvAccessApp/remote/codec.cpp | 52 +++++++++++++++++++++----- pvAccessApp/remote/codec.h | 23 +++++++----- testApp/remote/channelAccessIFTest.cpp | 8 ++-- testApp/remote/testChannelAccess.cpp | 2 +- testApp/remote/testCodec.cpp | 35 ++++++++--------- 5 files changed, 78 insertions(+), 42 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index e85818d..80a5a9c 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -39,8 +39,8 @@ namespace epics { const std::size_t AbstractCodec::MAX_ENSURE_DATA_BUFFER_SIZE = 1024; AbstractCodec::AbstractCodec( - ByteBuffer *receiveBuffer, - ByteBuffer *sendBuffer, + std::tr1::shared_ptr receiveBuffer, + std::tr1::shared_ptr sendBuffer, int32_t socketSendBufferSize, bool blockingProcessQueue): //PROTECTED @@ -49,6 +49,8 @@ namespace epics { _blockingProcessQueue(false), _senderThread(0), _writeMode(PROCESS_SEND_QUEUE), _writeOpReady(false),_lowLatency(false), + _socketBuffer(receiveBuffer), + _sendBuffer(sendBuffer), //PRIVATE _storedPayloadSize(0), _storedPosition(0), _startPosition(0), _maxSendPayloadSize(0), @@ -76,9 +78,6 @@ namespace epics { throw std::invalid_argument( "sendBuffer() % PVAConstants.PVA_ALIGNMENT != 0"); - _socketBuffer.reset(receiveBuffer); - _sendBuffer.reset(sendBuffer); - // initialize to be empty _socketBuffer->setPosition(_socketBuffer->getLimit()); _startPosition = _socketBuffer->getPosition(); @@ -369,6 +368,11 @@ namespace epics { if (bytesRead < 0) { + + LOG(logLevelTrace, + "AbstractCodec::before close (threadId: %u)", + epicsThreadGetIdSelf()); + close(); throw connection_closed_exception("bytesRead < 0"); } @@ -1153,6 +1157,7 @@ namespace epics { BlockingAbstractCodec *bac = static_cast(param); + Transport::shared_pointer ptr (bac->shared_from_this()); while (bac->isOpen()) { @@ -1181,6 +1186,8 @@ namespace epics { BlockingAbstractCodec *bac = static_cast(param); + Transport::shared_pointer ptr (bac->shared_from_this()); + bac->setSenderThread(); while (bac->isOpen()) @@ -1209,7 +1216,7 @@ namespace epics { LOG(logLevelTrace, "XXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (threadId: %u)", epicsThreadGetIdSelf()); - //bac->internalDestroy(); + bac->internalDestroy(); LOG(logLevelTrace, "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" "YYYYYYYYYYYYYYYYYYYYYYYYYYYY (threadId: %u)", epicsThreadGetIdSelf()); @@ -1246,12 +1253,12 @@ namespace epics { int32_t sendBufferSize, int32_t receiveBufferSize): BlockingAbstractCodec( - new ByteBuffer((std::max((std::size_t)( + std::tr1::shared_ptr(new ByteBuffer((std::max((std::size_t)( MAX_TCP_RECV + MAX_ENSURE_DATA_BUFFER_SIZE), receiveBufferSize) + - (PVA_ALIGNMENT - 1)) & (~(PVA_ALIGNMENT - 1))), - new ByteBuffer((std::max((std::size_t)( MAX_TCP_RECV + + (PVA_ALIGNMENT - 1)) & (~(PVA_ALIGNMENT - 1)))), + std::tr1::shared_ptr(new ByteBuffer((std::max((std::size_t)( MAX_TCP_RECV + MAX_ENSURE_DATA_BUFFER_SIZE), receiveBufferSize) + (PVA_ALIGNMENT - 1)) - & (~(PVA_ALIGNMENT - 1))), sendBufferSize), + & (~(PVA_ALIGNMENT - 1)))), sendBufferSize), _channel(channel) { @@ -1325,10 +1332,22 @@ namespace epics { std::size_t remaining; while((remaining=src->getRemaining()) > 0) { + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::write before send" + " (threadId: %u)", + epicsThreadGetIdSelf()); + + int bytesSent = ::send(_channel, &src->getArray()[src->getPosition()], remaining, 0); + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::write afer send, read:%d" + " (threadId: %u)", + bytesSent, epicsThreadGetIdSelf()); + + if(unlikely(bytesSent<0)) { int socketError = SOCKERRNO; @@ -1390,9 +1409,21 @@ namespace epics { // read std::size_t pos = dst->getPosition(); + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::read before recv" + " (threadId: %u)", + epicsThreadGetIdSelf()); + int bytesRead = recv(_channel, (char*)(dst->getArray()+pos), remaining, 0); + + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::read after recv, read: %d", + bytesRead," (threadId: %u)", + epicsThreadGetIdSelf()); + if (IS_LOGGABLE(logLevelTrace)) { hexDump(std::string("READ"), @@ -1414,6 +1445,7 @@ namespace epics { return -1; // 0 means connection loss for blocking transport, notify codec by returning -1 } + dst->setPosition(dst->getPosition() + bytesRead); return bytesRead; } diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index f885d88..58339b5 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -280,8 +280,8 @@ namespace epics { static const std::size_t MAX_ENSURE_DATA_BUFFER_SIZE; AbstractCodec( - epics::pvData::ByteBuffer *receiveBuffer, - epics::pvData::ByteBuffer *sendBuffer, + std::tr1::shared_ptr receiveBuffer, + std::tr1::shared_ptr sendBuffer, int32_t socketSendBufferSize, bool blockingProcessQueue); @@ -354,8 +354,8 @@ namespace epics { bool _writeOpReady; bool _lowLatency; - std::auto_ptr _socketBuffer; - std::auto_ptr _sendBuffer; + std::tr1::shared_ptr _socketBuffer; + std::tr1::shared_ptr _sendBuffer; epics::pvAccess::queue _sendQueue; @@ -385,15 +385,17 @@ namespace epics { }; - class BlockingAbstractCodec: public AbstractCodec { + class BlockingAbstractCodec: + public AbstractCodec, + public std::tr1::enable_shared_from_this { public: POINTER_DEFINITIONS(BlockingAbstractCodec); - BlockingAbstractCodec( - epics::pvData::ByteBuffer *receiveBuffer, - epics::pvData::ByteBuffer *sendBuffer, + BlockingAbstractCodec( + std::tr1::shared_ptr receiveBuffer, + std::tr1::shared_ptr sendBuffer, int32_t socketSendBufferSize): AbstractCodec(receiveBuffer, sendBuffer, socketSendBufferSize, true), _readThread(0), _sendThread(0) { _isOpen.getAndSet(true);} @@ -448,8 +450,9 @@ namespace epics { class BlockingTCPTransportCodec : - public BlockingSocketAbstractCodec, - public std::tr1::enable_shared_from_this { + public BlockingSocketAbstractCodec + + { public: diff --git a/testApp/remote/channelAccessIFTest.cpp b/testApp/remote/channelAccessIFTest.cpp index cdf5f69..8f8b51b 100755 --- a/testApp/remote/channelAccessIFTest.cpp +++ b/testApp/remote/channelAccessIFTest.cpp @@ -48,7 +48,7 @@ int ChannelAccessIFTest::runAllTest() { testPlan(152); #endif -/* test_implementation(); + test_implementation(); test_providerName(); test_createEmptyChannel(); @@ -56,10 +56,10 @@ int ChannelAccessIFTest::runAllTest() { test_createChannel(); test_recreateChannelOnDestroyedProvider(); test_findEmptyChannel(); - test_findChannel();*/ + test_findChannel(); test_channel(); -/* test_channelGetWithInvalidChannelAndRequester(); + test_channelGetWithInvalidChannelAndRequester(); test_channelGetNoProcess(); test_channelGetIntProcess(); test_channelGetTestNoConnection(); @@ -95,7 +95,7 @@ int ChannelAccessIFTest::runAllTest() { test_channelArray(); test_channelArray_destroy(); - test_channelArrayTestNoConnection();*/ + test_channelArrayTestNoConnection(); #ifdef ENABLE_STRESS_TESTS test_stressConnectDisconnect(); diff --git a/testApp/remote/testChannelAccess.cpp b/testApp/remote/testChannelAccess.cpp index d6778c6..0f1021d 100755 --- a/testApp/remote/testChannelAccess.cpp +++ b/testApp/remote/testChannelAccess.cpp @@ -88,7 +88,7 @@ class ChannelAccessIFRemoteTest: public ChannelAccessIFTest { MAIN(testChannelProvider) { - SET_LOG_LEVEL(logLevelTrace); + SET_LOG_LEVEL(logLevelError); ChannelAccessIFRemoteTest caRemoteTest; return caRemoteTest.runAllTest(); } diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 814b384..602ee82 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -6,7 +6,6 @@ #define NOMINMAX #endif -//testing #include #include @@ -25,13 +24,7 @@ namespace epics { class PVAMessage { public: - - int8_t _version; - int8_t _flags; - int8_t _command; - int32_t _payloadSize; - std::tr1::shared_ptr _payload; - + PVAMessage(int8_t version, int8_t flags, int8_t command, @@ -42,6 +35,12 @@ namespace epics { _payloadSize = payloadSize; } + int8_t _version; + int8_t _flags; + int8_t _command; + int32_t _payloadSize; + std::tr1::shared_ptr _payload; + //memberwise copy constructor/assigment operator //provided by the compiler }; @@ -67,7 +66,12 @@ namespace epics { std::size_t receiveBufferSize, std::size_t sendBufferSize, bool blocking = false): - _closedCount(0), + AbstractCodec( + std::tr1::shared_ptr(new ByteBuffer(receiveBufferSize)), + std::tr1::shared_ptr(new ByteBuffer(sendBufferSize)), + sendBufferSize/10, + blocking ), + _closedCount(0), _invalidDataStreamCount(0), _scheduleSendCount(0), _sendCompletedCount(0), @@ -79,12 +83,8 @@ namespace epics { _disconnected(false), _forcePayloadRead(-1), _readBuffer(new ByteBuffer(receiveBufferSize)), - _writeBuffer(sendBufferSize), - AbstractCodec( - new ByteBuffer(receiveBufferSize), - new ByteBuffer(sendBufferSize), - sendBufferSize/10, - blocking ) { + _writeBuffer(sendBufferSize) + { } @@ -253,6 +253,7 @@ namespace epics { void endBlockedProcessSendQueue() { + //TODO not thread safe _blockingProcessQueue = false; _sendQueue.wakeup(); } @@ -266,14 +267,14 @@ namespace epics { WriteMode getWriteMode() { return _writeMode;} - std::auto_ptr & getSendBuffer() + std::tr1::shared_ptr getSendBuffer() { return _sendBuffer; } osiSockAddr getLastReadBufferSocketAddress() { - osiSockAddr tmp = {0}; + osiSockAddr tmp = {{0}}; return tmp; } From a9c6ed6af88f7c97122c766c12123faf062af31e Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 13 Feb 2014 22:46:19 +0100 Subject: [PATCH 05/19] codec: minor code fixes, codec endian test checks --- pvAccessApp/remote/codec.cpp | 131 +++++++++++++++++++---------------- pvAccessApp/remote/codec.h | 17 ++--- pvAccessCPP.files | 4 ++ pvAccessCPP.includes | 4 +- testApp/remote/testCodec.cpp | 83 +++++++++++----------- 5 files changed, 127 insertions(+), 112 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 80a5a9c..6165f82 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -39,8 +39,8 @@ namespace epics { const std::size_t AbstractCodec::MAX_ENSURE_DATA_BUFFER_SIZE = 1024; AbstractCodec::AbstractCodec( - std::tr1::shared_ptr receiveBuffer, - std::tr1::shared_ptr sendBuffer, + std::tr1::shared_ptr const & receiveBuffer, + std::tr1::shared_ptr const & sendBuffer, int32_t socketSendBufferSize, bool blockingProcessQueue): //PROTECTED @@ -56,7 +56,8 @@ namespace epics { _maxSendPayloadSize(0), _lastMessageStartPosition(0),_lastSegmentedMessageType(0), _lastSegmentedMessageCommand(0), _nextMessagePayloadOffset(0), - _byteOrderFlag(0x80),_socketSendBufferSize(0) + _byteOrderFlag(EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00), + _socketSendBufferSize(0) { if (receiveBuffer->getSize() < 2*MAX_ENSURE_SIZE) throw std::invalid_argument( @@ -109,7 +110,7 @@ namespace epics { processReadSegmented(); break; case SPLIT: - throw std::logic_error("SPLIT NOT SUPPORTED"); + throw std::logic_error("ReadMode == SPLIT not supported"); } } @@ -197,65 +198,16 @@ namespace epics { { // handle response processApplicationMessage(); - //TODO: MATEJ CHECK - throw simulate_finally_exception("go to finally block"); + + postProcessApplicationMessage(); } catch(...) //finally { - if (!isOpen()) - return; + if (!isOpen()) + return; - // can be closed by now - // isOpen() should be efficiently implemented - while (true) - //while (isOpen()) - { - // set position as whole message was read - //(in case code haven't done so) - std::size_t newPosition = - alignedValue( - _storedPosition + _storedPayloadSize, PVA_ALIGNMENT); - - // aligned buffer size ensures that there is enough space - //in buffer, - // however data might not be fully read - - // discard the rest of the packet - if (newPosition > _storedLimit) - { - // processApplicationMessage() did not read up - //quite some buffer - - // we only handle unused alignment bytes - int bytesNotRead = - newPosition - _socketBuffer->getPosition(); - - if (bytesNotRead < PVA_ALIGNMENT) - { - // make alignment bytes as real payload to enable SPLIT - // no end-of-socket or segmented scenario can happen - // due to aligned buffer size - _storedPayloadSize += bytesNotRead; - // reveal currently existing padding - _socketBuffer->setLimit(_storedLimit); - ensureData(bytesNotRead); - _storedPayloadSize -= bytesNotRead; - continue; - } - - // TODO we do not handle this for now (maybe never) - LOG(logLevelWarn, - "unprocessed read buffer from client at %s:%d: %d," - " disconnecting...", - __FILE__, __LINE__, getLastReadBufferSocketAddress()); - invalidDataStreamHandler(); - throw invalid_data_stream_exception( - "unprocessed read buffer"); - } - _socketBuffer->setLimit(_storedLimit); - _socketBuffer->setPosition(newPosition); - break; - } + postProcessApplicationMessage(); + throw; } } } @@ -271,6 +223,63 @@ namespace epics { } } + void AbstractCodec::postProcessApplicationMessage() + { + if (!isOpen()) + return; + + // can be closed by now + // isOpen() should be efficiently implemented + while (true) + //while (isOpen()) + { + // set position as whole message was read + //(in case code haven't done so) + std::size_t newPosition = + alignedValue( + _storedPosition + _storedPayloadSize, PVA_ALIGNMENT); + + // aligned buffer size ensures that there is enough space + //in buffer, + // however data might not be fully read + + // discard the rest of the packet + if (newPosition > _storedLimit) + { + // processApplicationMessage() did not read up + //quite some buffer + + // we only handle unused alignment bytes + int bytesNotRead = + newPosition - _socketBuffer->getPosition(); + + if (bytesNotRead < PVA_ALIGNMENT) + { + // make alignment bytes as real payload to enable SPLIT + // no end-of-socket or segmented scenario can happen + // due to aligned buffer size + _storedPayloadSize += bytesNotRead; + // reveal currently existing padding + _socketBuffer->setLimit(_storedLimit); + ensureData(bytesNotRead); + _storedPayloadSize -= bytesNotRead; + continue; + } + + // TODO we do not handle this for now (maybe never) + LOG(logLevelWarn, + "unprocessed read buffer from client at %s:%d: %d," + " disconnecting...", + __FILE__, __LINE__, getLastReadBufferSocketAddress()); + invalidDataStreamHandler(); + throw invalid_data_stream_exception( + "unprocessed read buffer"); + } + _socketBuffer->setLimit(_storedLimit); + _socketBuffer->setPosition(newPosition); + break; + } + } void AbstractCodec::processReadSegmented() { @@ -1280,6 +1289,7 @@ namespace epics { timeout.tv_sec = 1; timeout.tv_usec = 0; + // TODO remove this and implement use epicsSocketSystemCallInterruptMechanismQuery if (unlikely(::setsockopt (_channel, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0)) { @@ -1450,8 +1460,7 @@ namespace epics { return bytesRead; } - //TODO check what to return - return -1; + return 0; } diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index 58339b5..7b75d62 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -45,7 +45,7 @@ namespace epics { namespace pvAccess { - + // TODO replace mutex with atomic (CAS) operations template class AtomicValue { @@ -280,8 +280,8 @@ namespace epics { static const std::size_t MAX_ENSURE_DATA_BUFFER_SIZE; AbstractCodec( - std::tr1::shared_ptr receiveBuffer, - std::tr1::shared_ptr sendBuffer, + std::tr1::shared_ptr const & receiveBuffer, + std::tr1::shared_ptr const & sendBuffer, int32_t socketSendBufferSize, bool blockingProcessQueue); @@ -343,7 +343,7 @@ namespace epics { int8_t _version; int8_t _flags; int8_t _command; - int32_t _payloadSize; + int32_t _payloadSize; // TODO why not size_t? epics::pvData::int32 _remoteTransportSocketReceiveBufferSize; int64_t _totalBytesSent; bool _blockingProcessQueue; @@ -357,13 +357,14 @@ namespace epics { std::tr1::shared_ptr _socketBuffer; std::tr1::shared_ptr _sendBuffer; - epics::pvAccess::queue _sendQueue; + epics::pvAccess::queue _sendQueue; private: void processHeader(); void processReadNormal(); - void processReadSegmented(); + void postProcessApplicationMessage(); + void processReadSegmented(); bool readToBuffer(std::size_t requiredBytes, bool persistent); void endMessage(bool hasMoreSegments); void processSender( @@ -394,8 +395,8 @@ namespace epics { POINTER_DEFINITIONS(BlockingAbstractCodec); BlockingAbstractCodec( - std::tr1::shared_ptr receiveBuffer, - std::tr1::shared_ptr sendBuffer, + std::tr1::shared_ptr const & receiveBuffer, + std::tr1::shared_ptr const & sendBuffer, int32_t socketSendBufferSize): AbstractCodec(receiveBuffer, sendBuffer, socketSendBufferSize, true), _readThread(0), _sendThread(0) { _isOpen.getAndSet(true);} diff --git a/pvAccessCPP.files b/pvAccessCPP.files index 6fbd1d9..82a19d7 100644 --- a/pvAccessCPP.files +++ b/pvAccessCPP.files @@ -40,6 +40,8 @@ pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp pvAccessApp/remote/simpleChannelSearchManagerImpl.h pvAccessApp/remote/transportRegistry.cpp pvAccessApp/remote/transportRegistry.h +pvAccessApp/remote/codec.cpp +pvAccessApp/remote/codec.h pvAccessApp/remoteClient/clientContextImpl.cpp pvAccessApp/remoteClient/clientContextImpl.h pvAccessApp/remoteClient/clientContextImpl.h.orig @@ -101,6 +103,8 @@ testApp/remote/testNTImage.cpp testApp/remote/testRemoteClientImpl.cpp testApp/remote/testServer.cpp testApp/remote/testServerContext.cpp +testApp/remote/testChannelAccess.cpp +testApp/remote/testCodec.cpp testApp/utils/testAtomicBoolean.cpp testApp/utils/configurationTest.cpp testApp/utils/testHexDump.cpp diff --git a/pvAccessCPP.includes b/pvAccessCPP.includes index 471a178..d8138cf 100644 --- a/pvAccessCPP.includes +++ b/pvAccessCPP.includes @@ -7,4 +7,6 @@ /home/msekoranja/epicsV4/pvAccessCPP/pvAccessApp/rpcService /home/msekoranja/epicsV4/pvAccessCPP/pvAccessApp/server /home/msekoranja/epicsV4/pvAccessCPP/pvAccessApp/utils -/home/msekoranja/epicsV4/pvAccessCPP/testApp/remote \ No newline at end of file +/home/msekoranja/epicsV4/pvAccessCPP/testApp/remote +/home/msekoranja/epicsV4/pvAccessCPP/testApp/client +/home/msekoranja/epicsV4/pvAccessCPP/testApp/utils diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 602ee82..41e9adb 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -397,7 +397,7 @@ namespace epics { public: int runAllTest() { - testPlan(5885); + testPlan(5884); testHeaderProcess(); testInvalidHeaderMagic(); testInvalidHeaderSegmentedInNormal(); @@ -518,9 +518,9 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x00, + testOk(header._flags == (int8_t)0x00, "%s: header._flags == 0x00", CURRENT_FUNCTION); - testOk(header._command == 0x20, + testOk(header._command == (int8_t)0x20, "%s: header._command == 0x20", CURRENT_FUNCTION); testOk(header._payloadSize == 0x00000000, "%s: header._payloadSize == 0x00000000", CURRENT_FUNCTION); @@ -530,11 +530,11 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x81, + testOk(header._flags == (int8_t)0x81, "%s: header._flags == 0x81", CURRENT_FUNCTION); - testOk(header._command == 0xEE, + testOk(header._command == (int8_t)0xEE, "%s: header._command == 0xEE", CURRENT_FUNCTION); - testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + testOk(header._payloadSize == (int32_t)0xDDCCBBAA, "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); } @@ -687,9 +687,9 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x01, + testOk(header._flags == (int8_t)0x01, "%s: header._flags == 0x01", CURRENT_FUNCTION); - testOk(header._command == 0x23, + testOk(header._command == (int8_t)0x23, "%s: header._command == 0x23", CURRENT_FUNCTION); testOk(header._payloadSize == 0x456789AB, "%s: header._payloadSize == 0x456789AB", CURRENT_FUNCTION); @@ -1118,11 +1118,11 @@ namespace epics { testOk(msg._version == PVA_VERSION, "%s: msg._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(msg._flags == 0x81, + testOk(msg._flags == (int8_t)0x81, "%s: msg._flags == 0x81", CURRENT_FUNCTION); - testOk(msg._command == 0xEE, + testOk(msg._command == (int8_t)0xEE, "%s: msg._command == 0xEE", CURRENT_FUNCTION); - testOk((std::size_t)msg._payloadSize == 0xDDCCBBAA, + testOk(msg._payloadSize == (int32_t)0xDDCCBBAA, "%s: msg._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); } @@ -1606,9 +1606,9 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x81, - "%s: header._flags == 0x81", CURRENT_FUNCTION); - testOk(header._command == 0x23, + testOk(header._flags == (int8_t)((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00) | 0x01), + "%s: header._flags == 0x(0|8)1", CURRENT_FUNCTION); + testOk(header._command == (int8_t)0x23, "%s: header._command == 0x23", CURRENT_FUNCTION); testOk(header._payloadSize == 0x456789AB, "%s: header._payloadSize == 0x456789AB", CURRENT_FUNCTION); @@ -1644,9 +1644,9 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x00, + testOk(header._flags == (int8_t)0x00, "%s: header._flags == 0x00", CURRENT_FUNCTION); - testOk(header._command == 0x20, + testOk(header._command == (int8_t)0x20, "%s: header._command == 0x20", CURRENT_FUNCTION); testOk(header._payloadSize == 0x00000000, "%s: header._payloadSize == 0x00000000", CURRENT_FUNCTION); @@ -1656,11 +1656,11 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x81, + testOk(header._flags == (int8_t)0x81, "%s: header._flags == 0x81", CURRENT_FUNCTION); - testOk(header._command == 0xEE, + testOk(header._command == (int8_t)0xEE, "%s: header._command == 0xEE", CURRENT_FUNCTION); - testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + testOk(header._payloadSize == (int32_t)0xDDCCBBAA, "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); } @@ -2297,9 +2297,9 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x80, - "%s: header._flags == 0x80", CURRENT_FUNCTION); - testOk(header._command == 0x20, + testOk(header._flags == (int8_t)((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00) | 0x00), + "%s: header._flags == 0x(0|8)0", CURRENT_FUNCTION); + testOk(header._command == (int8_t)0x20, "%s: header._command == 0x20", CURRENT_FUNCTION); testOk(header._payloadSize == 0x00000000, "%s: header._payloadSize == 0x00000000", CURRENT_FUNCTION); @@ -2309,11 +2309,11 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x81, - "%s: header._flags == 0x81", CURRENT_FUNCTION); - testOk(header._command == 0xEE, + testOk(header._flags == (int8_t)((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00) | 0x01), + "%s: header._flags == 0x(0|8)1", CURRENT_FUNCTION); + testOk(header._command == (int8_t)0xEE, "%s: header._command == 0xEE", CURRENT_FUNCTION); - testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + testOk(header._payloadSize == (int32_t)0xDDCCBBAA, "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); } @@ -2441,8 +2441,8 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x80, - "%s: header._flags == 0x80", CURRENT_FUNCTION); + testOk(header._flags == (int8_t)((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00) | 0x00), + "%s: header._flags == 0x(0|8)0", CURRENT_FUNCTION); testOk(header._command == 0x20, "%s: header._command == 0x20", CURRENT_FUNCTION); testOk(header._payloadSize == 0x00000000, @@ -2454,11 +2454,11 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x81, - "%s: header._flags == 0x81", CURRENT_FUNCTION); - testOk(header._command == 0xEE, + testOk(header._flags == (int8_t)((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00) | 0x01), + "%s: header._flags == 0x(0|8)1", CURRENT_FUNCTION); + testOk(header._command == (int8_t)0xEE, "%s: header._command == 0xEE", CURRENT_FUNCTION); - testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + testOk(header._payloadSize == (int32_t)0xDDCCBBAA, "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); @@ -2499,11 +2499,11 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x81, - "%s: header._flags == 0x81", CURRENT_FUNCTION); - testOk(header._command == 0xEE, + testOk(header._flags == (int8_t)((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00) | 0x01), + "%s: header._flags == 0x(0|8)1", CURRENT_FUNCTION); + testOk(header._command == (int8_t)0xEE, "%s: header._command == 0xEE", CURRENT_FUNCTION); - testOk((std::size_t)header._payloadSize == 0xDDCCBBAA, + testOk(header._payloadSize == (int32_t)0xDDCCBBAA, "%s: header._payloadSize == 0xDDCCBBAA", CURRENT_FUNCTION); } @@ -2582,11 +2582,11 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == 0x80, + testOk(header._flags == (int8_t)0x80, "%s: header._flags == 0x80", CURRENT_FUNCTION); - testOk(header._command == 0x12, + testOk(header._command == (int8_t)0x12, "%s: header._command == 0x12", CURRENT_FUNCTION); - testOk((std::size_t)header._payloadSize == bytesToSent, + testOk(header._payloadSize == (int32_t)bytesToSent, "%s: header._payloadSize == bytesToSent", CURRENT_FUNCTION); @@ -2789,8 +2789,8 @@ namespace epics { testOk(header._version == PVA_VERSION, "%s: header._version == PVA_VERSION", CURRENT_FUNCTION); - testOk(header._flags == (int8_t)(0x80 | 0x10), - "%s: header._flags == (int8_t)(0x80 | 0x10)", + testOk(header._flags == (int8_t)((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00) | 0x10), + "%s: header._flags == (int8_t)(0x(0|8)0 | 0x10)", CURRENT_FUNCTION); testOk(header._command == 0x12, "%s: header._command == 0x12", CURRENT_FUNCTION); @@ -2818,7 +2818,6 @@ namespace epics { void testRecipient() { // nothing to test, depends on implementation - testSkip(1, " testRecipient()"); } From 2e184d62e81f25576d7abde59adda5a04b99da05 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Thu, 13 Feb 2014 23:55:19 +0100 Subject: [PATCH 06/19] fixed transprort weak_ptr --- pvAccessApp/remote/codec.cpp | 9 +++------ pvAccessApp/remote/codec.h | 29 +++++++++++++++-------------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 6165f82..547072a 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -1128,6 +1128,7 @@ namespace epics { } + // NOTE: must not be called from constructor (e.g. needs shared_from_this()) void BlockingAbstractCodec::start() { LOG(logLevelTrace, "BlockingAbstractCodec::start enter: (threadId: %u)", @@ -1164,9 +1165,8 @@ namespace epics { "BlockingAbstractCodec::receiveThread enter: (threadId: %u)", epicsThreadGetIdSelf()); - BlockingAbstractCodec *bac = static_cast(param); - Transport::shared_pointer ptr (bac->shared_from_this()); + Transport::shared_pointer ptr = bac->shared_from_this(); while (bac->isOpen()) { @@ -1194,8 +1194,7 @@ namespace epics { epicsThreadGetIdSelf()); BlockingAbstractCodec *bac = static_cast(param); - - Transport::shared_pointer ptr (bac->shared_from_this()); + Transport::shared_pointer ptr = bac->shared_from_this(); bac->setSenderThread(); @@ -1480,8 +1479,6 @@ namespace epics { // TODO implement priorities in Reactor... not that user will // change it.. still getPriority() must return "registered" priority! - start(); - LOG(logLevelTrace, "BlockingServerTCPTransportCodec constructed (threadId: %u)", epicsThreadGetIdSelf()); diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index 7b75d62..58f2c3c 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -236,12 +236,6 @@ namespace epics { }; - class simulate_finally_exception: public std::runtime_error { - public: - explicit simulate_finally_exception( - const std::string &s): std::runtime_error(s) {} - }; - class io_exception: public std::runtime_error { public: explicit io_exception(const std::string &s): std::runtime_error(s) {} @@ -388,7 +382,8 @@ namespace epics { class BlockingAbstractCodec: public AbstractCodec, - public std::tr1::enable_shared_from_this { + public std::tr1::enable_shared_from_this + { public: @@ -425,7 +420,9 @@ namespace epics { }; - class BlockingSocketAbstractCodec: public BlockingAbstractCodec { + class BlockingSocketAbstractCodec: + public BlockingAbstractCodec + { public: @@ -588,9 +585,9 @@ namespace epics { bool directSerialize( - epics::pvData::ByteBuffer *existingBuffer, - const char* toSerialize, - std::size_t elementCount, std::size_t elementSize) + epics::pvData::ByteBuffer * /*existingBuffer*/, + const char* /*toSerialize*/, + std::size_t /*elementCount*/, std::size_t /*elementSize*/) { LOG(logLevelTrace, @@ -601,9 +598,9 @@ namespace epics { } - bool directDeserialize(epics::pvData::ByteBuffer *existingBuffer, - char* deserializeTo, - std::size_t elementCount, std::size_t elementSize) { + bool directDeserialize(epics::pvData::ByteBuffer * /*existingBuffer*/, + char* /*deserializeTo*/, + std::size_t /*elementCount*/, std::size_t /*elementSize*/) { LOG(logLevelTrace, "BlockingTCPTransportCodec::directDeserialize() enter:" @@ -635,6 +632,8 @@ namespace epics { Transport::shared_pointer thisSharedPtr = shared_from_this(); _context->getTransportRegistry()->put(thisSharedPtr); + + start(); } protected: @@ -684,6 +683,7 @@ namespace epics { public: POINTER_DEFINITIONS(BlockingServerTCPTransportCodec); + protected: BlockingServerTCPTransportCodec( Context::shared_pointer const & context, SOCKET channel, @@ -691,6 +691,7 @@ namespace epics { int32_t sendBufferSize, int32_t receiveBufferSize ); + public: static shared_pointer create( Context::shared_pointer const & context, SOCKET channel, From 13cf6f0d6a129f9ac79f023a2243030100572f26 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Fri, 14 Feb 2014 21:55:27 +0100 Subject: [PATCH 07/19] codec: fixed bugs in codec, now entire test passes --- pvAccessApp/remote/codec.cpp | 21 ++++++++++----------- pvAccessApp/remote/codec.h | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 547072a..87abd57 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -194,19 +194,24 @@ namespace epics { _storedLimit = _socketBuffer->getLimit(); _socketBuffer->setLimit(std::min (_storedPosition + _storedPayloadSize, _storedLimit)); + bool postProcess = true; try { // handle response processApplicationMessage(); - + postProcess = false; postProcessApplicationMessage(); } catch(...) //finally { - if (!isOpen()) - return; - - postProcessApplicationMessage(); + if (postProcess) + { + if (!isOpen()) + return; + + postProcessApplicationMessage(); + } + throw; } } @@ -891,7 +896,6 @@ namespace epics { "AbstractCodec::processSendQueue enter: (threadId: %u)", epicsThreadGetIdSelf()); - try { std::size_t senderProcessed = 0; while (senderProcessed++ < MAX_MESSAGE_SEND) @@ -920,11 +924,6 @@ namespace epics { processSender(sender); } } - //TODO MATEJ CHECK - //InterruptedException ie - catch (...) { - // noop, allowed and expected in blocking - } // flush if (_sendBuffer->getPosition() > 0) diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index 58f2c3c..ba4a777 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -126,7 +126,7 @@ namespace epics { { epics::pvData::Lock lock(_queueMutex); - _queue.push_front(elem); + _queue.push_back(elem); } _queueEvent.signal(); From eb1237253549fb877d89a662903cbba727de924d Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Fri, 14 Feb 2014 22:06:43 +0100 Subject: [PATCH 08/19] tests: testPlan count adj --- testApp/remote/channelAccessIFTest.cpp | 3 +-- testApp/remote/testCodec.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/testApp/remote/channelAccessIFTest.cpp b/testApp/remote/channelAccessIFTest.cpp index 8f8b51b..3c65f89 100755 --- a/testApp/remote/channelAccessIFTest.cpp +++ b/testApp/remote/channelAccessIFTest.cpp @@ -415,7 +415,7 @@ void ChannelAccessIFTest::test_channel() { testOk(channel->getConnectionState() == Channel::DESTROYED , "%s: channel connection state DESTROYED ", CURRENT_FUNCTION); -/* testDiag("%s: destroying the channel yet again", CURRENT_FUNCTION); + testDiag("%s: destroying the channel yet again", CURRENT_FUNCTION); channel->destroy(); succStatus = channelReq->waitUntilStateChange(getTimeoutSec()); @@ -432,7 +432,6 @@ void ChannelAccessIFTest::test_channel() { testOk(!channel->isConnected(), "%s: yet again destroyed channel should not be connected ", CURRENT_FUNCTION); testOk(channel->getConnectionState() == Channel::DESTROYED , "%s: yet again destroyed channel connection state DESTROYED ", CURRENT_FUNCTION); -*/ } diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 41e9adb..495735c 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -397,7 +397,7 @@ namespace epics { public: int runAllTest() { - testPlan(5884); + testPlan(5882); testHeaderProcess(); testInvalidHeaderMagic(); testInvalidHeaderSegmentedInNormal(); From f2ee8de75854c97fafea5614160f7637e7f408cc Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Sun, 9 Mar 2014 01:36:44 +0100 Subject: [PATCH 09/19] codec: BlockingTCPClientTransportCodec --- pvAccessApp/remote/codec.cpp | 279 +++++++++++++++++++++++++++++++++++ pvAccessApp/remote/codec.h | 160 +++++++++++++++++--- 2 files changed, 420 insertions(+), 19 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 87abd57..081e42a 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -1602,4 +1602,283 @@ namespace epics { control->flush(true); } } + + + + // TODO + /* + + void BlockingServerTCPTransportCodec::destroyAllChannels() { + Lock lock(_channelsMutex); + if(_channels.size()==0) return; + + char ipAddrStr[64]; + ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); + + LOG( + logLevelDebug, + "Transport to %s still has %u channel(s) active and closing...", + ipAddrStr, (unsigned int)_channels.size()); + + map::iterator it = _channels.begin(); + for(; it!=_channels.end(); it++) + it->second->destroy(); + + _channels.clear(); + } + + void BlockingServerTCPTransportCodec::internalClose(bool force) { + Transport::shared_pointer thisSharedPtr = shared_from_this(); + BlockingTCPTransport::internalClose(force); + destroyAllChannels(); + } + + void BlockingServerTCPTransportCodec::internalPostClose(bool forced) { + BlockingTCPTransport::internalPostClose(forced); + } +*/ + + + BlockingClientTCPTransportCodec::BlockingClientTCPTransportCodec( + Context::shared_pointer const & context, + SOCKET channel, + std::auto_ptr& responseHandler, + int32_t sendBufferSize, + int32_t receiveBufferSize, + TransportClient::shared_pointer const & client, + epics::pvData::int8 remoteTransportRevision, + float beaconInterval, + int16_t priority ) : + BlockingTCPTransportCodec(context, channel, responseHandler, + sendBufferSize, receiveBufferSize, priority), + _connectionTimeout(beaconInterval*1000), + _unresponsiveTransport(false), + _verifyOrEcho(true), + _verified(false) + { + // initialize owners list, send queue + acquire(client); + + // use immediate for clients + //setFlushStrategy(DELAYED); + + // setup connection timeout timer (watchdog) - moved to start() method + epicsTimeGetCurrent(&_aliveTimestamp); + } + + void BlockingClientTCPTransportCodec::start() + { + TimerCallbackPtr tcb = std::tr1::dynamic_pointer_cast(shared_from_this()); + _context->getTimer()->schedulePeriodic(tcb, _connectionTimeout, _connectionTimeout); + BlockingTCPTransportCodec::start(); + } + + BlockingClientTCPTransportCodec::~BlockingClientTCPTransportCodec() { + } + + + + + + + + + + void BlockingClientTCPTransportCodec::callback() { + epicsTimeStamp currentTime; + epicsTimeGetCurrent(¤tTime); + + _mutex.lock(); + // no exception expected here + double diff = epicsTimeDiffInSeconds(¤tTime, &_aliveTimestamp); + _mutex.unlock(); + + if(diff>2*_connectionTimeout) { + unresponsiveTransport(); + } + // use some k (3/4) to handle "jitter" + else if(diff>=((3*_connectionTimeout)/4)) { + // send echo + TransportSender::shared_pointer transportSender = std::tr1::dynamic_pointer_cast(shared_from_this()); + enqueueSendRequest(transportSender); + } + } + +#define EXCEPTION_GUARD(code) try { code; } \ + catch (std::exception &e) { LOG(logLevelError, "Unhandled exception caught from code at %s:%d: %s", __FILE__, __LINE__, e.what()); } \ + catch (...) { LOG(logLevelError, "Unhandled exception caught from code at %s:%d.", __FILE__, __LINE__); } + + void BlockingClientTCPTransportCodec::unresponsiveTransport() { + Lock lock(_mutex); + if(!_unresponsiveTransport) { + _unresponsiveTransport = true; + + TransportClientMap_t::iterator it = _owners.begin(); + for(; it!=_owners.end(); it++) { + TransportClient::shared_pointer client = it->second.lock(); + if (client) + { + EXCEPTION_GUARD(client->transportUnresponsive()); + } + } + } + } + + bool BlockingClientTCPTransportCodec::acquire(TransportClient::shared_pointer const & client) { + Lock lock(_mutex); + if(isClosed()) return false; + + char ipAddrStr[48]; + ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); + LOG(logLevelDebug, "Acquiring transport to %s.", ipAddrStr); + + _owners[client->getID()] = TransportClient::weak_pointer(client); + //_owners.insert(TransportClient::weak_pointer(client)); + + return true; + } + + // _mutex is held when this method is called + void BlockingClientTCPTransportCodec::internalClose(bool forced) { +// TODO !!! BlockingTCPTransportCodec::internalClose(forced); + + TimerCallbackPtr tcb = std::tr1::dynamic_pointer_cast(shared_from_this()); + _context->getTimer()->cancel(tcb); + } + + void BlockingClientTCPTransportCodec::internalPostClose(bool forced) { +// TODO !!! BlockingTCPTransportCodec::internalPostClose(forced); + + // _owners cannot change when transport is closed + closedNotifyClients(); + } + + /** + * Notifies clients about disconnect. + */ + void BlockingClientTCPTransportCodec::closedNotifyClients() { + + // check if still acquired + size_t refs = _owners.size(); + if(refs>0) { + char ipAddrStr[48]; + ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); + LOG( + logLevelDebug, + "Transport to %s still has %d client(s) active and closing...", + ipAddrStr, refs); + + TransportClientMap_t::iterator it = _owners.begin(); + for(; it!=_owners.end(); it++) { + TransportClient::shared_pointer client = it->second.lock(); + if (client) + { + EXCEPTION_GUARD(client->transportClosed()); + } + } + + } + + _owners.clear(); + } + + //void BlockingClientTCPTransportCodec::release(TransportClient::shared_pointer const & client) { + void BlockingClientTCPTransportCodec::release(pvAccessID clientID) { + Lock lock(_mutex); + if(isClosed()) return; + + char ipAddrStr[48]; + ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); + + LOG(logLevelDebug, "Releasing transport to %s.", ipAddrStr); + + _owners.erase(clientID); + //_owners.erase(TransportClient::weak_pointer(client)); + + // not used anymore, close it + // TODO consider delayed destruction (can improve performance!!!) + if(_owners.size()==0) close(); // TODO close(false) + } + + void BlockingClientTCPTransportCodec::aliveNotification() { + Lock guard(_mutex); + epicsTimeGetCurrent(&_aliveTimestamp); + if(_unresponsiveTransport) responsiveTransport(); + } + + bool BlockingClientTCPTransportCodec::verify(epics::pvData::int32 timeoutMs) { + return _verifiedEvent.wait(timeoutMs/1000.0); + } + + void BlockingClientTCPTransportCodec::verified() { + epics::pvData::Lock lock(_verifiedMutex); + _verified = true; + _verifiedEvent.signal(); + } + + void BlockingClientTCPTransportCodec::responsiveTransport() { + Lock lock(_mutex); + if(_unresponsiveTransport) { + _unresponsiveTransport = false; + + Transport::shared_pointer thisSharedPtr = shared_from_this(); + TransportClientMap_t::iterator it = _owners.begin(); + for(; it!=_owners.end(); it++) { + TransportClient::shared_pointer client = it->second.lock(); + if (client) + { + EXCEPTION_GUARD(client->transportResponsive(thisSharedPtr)); + } + } + } + } + + void BlockingClientTCPTransportCodec::changedTransport() { + _outgoingIR.reset(); + + Lock lock(_mutex); + TransportClientMap_t::iterator it = _owners.begin(); + for(; it!=_owners.end(); it++) { + TransportClient::shared_pointer client = it->second.lock(); + if (client) + { + EXCEPTION_GUARD(client->transportChanged()); + } + } + } + + void BlockingClientTCPTransportCodec::send(ByteBuffer* buffer, + TransportSendControl* control) { + if(_verifyOrEcho) { + /* + * send verification response message + */ + + control->startMessage(CMD_CONNECTION_VALIDATION, 2*sizeof(int32)+sizeof(int16)); + + // receive buffer size + buffer->putInt(static_cast(getReceiveBufferSize())); + + // socket receive buffer size + buffer->putInt(static_cast(getSocketReceiveBufferSize())); + + // connection priority + buffer->putShort(getPriority()); + + // send immediately + control->flush(true); + + _verifyOrEcho = false; + } + else { + control->startMessage(CMD_ECHO, 0); + // send immediately + control->flush(true); + } + + } + + + + } diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index ba4a777..21146f9 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -648,7 +648,7 @@ namespace epics { ): BlockingSocketAbstractCodec(channel, sendBufferSize, receiveBufferSize), _context(context), _responseHandler(responseHandler), - _verified(false), _remoteTransportReceiveBufferSize(MAX_TCP_RECV), + _remoteTransportReceiveBufferSize(MAX_TCP_RECV), _remoteTransportRevision(0), _priority(priority) { LOG(logLevelTrace, @@ -656,22 +656,18 @@ namespace epics { epicsThreadGetIdSelf()); } - - private: - Context::shared_pointer _context; - std::auto_ptr _responseHandler; - bool _verified; - size_t _remoteTransportReceiveBufferSize; - epics::pvData::int8 _remoteTransportRevision; - epics::pvData::int16 _priority; osiSockAddr _socketAddress; - epics::pvData::Mutex _verifiedMutex; - epics::pvData::Event _verifiedEvent; IntrospectionRegistry _incomingIR; IntrospectionRegistry _outgoingIR; + private: + + std::auto_ptr _responseHandler; + size_t _remoteTransportReceiveBufferSize; + epics::pvData::int8 _remoteTransportRevision; + epics::pvData::int16 _priority; }; @@ -757,14 +753,6 @@ namespace epics { // noop } - void acquire() { - // noop, since does not make sence on itself - } - - void release() { - // noop, since does not make sence on itself - } - bool verify(epics::pvData::int32 timeoutMs) { LOG(logLevelTrace, @@ -806,6 +794,140 @@ namespace epics { epics::pvData::Mutex _channelsMutex; }; + + class BlockingClientTCPTransportCodec : + public BlockingTCPTransportCodec, + public TransportSender, + public epics::pvData::TimerCallback { + + public: + POINTER_DEFINITIONS(BlockingClientTCPTransportCodec); + + protected: + BlockingClientTCPTransportCodec( + Context::shared_pointer const & context, + SOCKET channel, + std::auto_ptr& responseHandler, + int32_t sendBufferSize, + int32_t receiveBufferSize, + TransportClient::shared_pointer const & client, + epics::pvData::int8 remoteTransportRevision, + float beaconInterval, + int16_t priority ); + + public: + static shared_pointer create( + Context::shared_pointer const & context, + SOCKET channel, + std::auto_ptr& responseHandler, + int32_t sendBufferSize, + int32_t receiveBufferSize, + TransportClient::shared_pointer const & client, + int8_t remoteTransportRevision, + float beaconInterval, + int16_t priority ) + { + shared_pointer thisPointer( + new BlockingClientTCPTransportCodec( + context, channel, responseHandler, + sendBufferSize, receiveBufferSize, + client, remoteTransportRevision, + beaconInterval, priority) + ); + thisPointer->activate(); + return thisPointer; + } + + public: + + void start(); + + virtual ~BlockingClientTCPTransportCodec(); + + virtual void timerStopped() { + // noop + } + + virtual void callback(); + + bool acquire(TransportClient::shared_pointer const & client); + + void release(pvAccessID clientId); + + void changedTransport(); + + void lock() { + // noop + } + + void unlock() { + // noop + } + + bool verify(epics::pvData::int32 timeoutMs); + + void verified(); + + void aliveNotification(); + + void send(epics::pvData::ByteBuffer* buffer, + TransportSendControl* control); + + protected: + + virtual void internalClose(bool force); + virtual void internalPostClose(bool force); + + private: + + /** + * Owners (users) of the transport. + */ + // TODO consider using TR1 hash map + typedef std::map TransportClientMap_t; + TransportClientMap_t _owners; + + /** + * Connection timeout (no-traffic) flag. + */ + double _connectionTimeout; + + /** + * Unresponsive transport flag. + */ + bool _unresponsiveTransport; + + /** + * Timestamp of last "live" event on this transport. + */ + epicsTimeStamp _aliveTimestamp; + + bool _verifyOrEcho; + + /** + * Unresponsive transport notify. + */ + void unresponsiveTransport(); + + /** + * Notifies clients about disconnect. + */ + void closedNotifyClients(); + + /** + * Responsive transport notify. + */ + void responsiveTransport(); + + + epics::pvData::Mutex _mutex; + + bool _verified; + epics::pvData::Mutex _verifiedMutex; + epics::pvData::Event _verifiedEvent; + + }; + } } From 80b93e2ddb3c9e34833d51211de9b4ecebbe3c31 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Sat, 22 Mar 2014 21:21:39 +0100 Subject: [PATCH 10/19] UDP blocking socket shutdown --- pvAccessApp/remote/blockingUDPTransport.cpp | 31 +++++++++++++++++---- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/pvAccessApp/remote/blockingUDPTransport.cpp b/pvAccessApp/remote/blockingUDPTransport.cpp index 95f0fa8..84aa0fd 100644 --- a/pvAccessApp/remote/blockingUDPTransport.cpp +++ b/pvAccessApp/remote/blockingUDPTransport.cpp @@ -56,8 +56,8 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so // set receive timeout so that we do not have problems at shutdown (recvfrom would block) struct timeval timeout; memset(&timeout, 0, sizeof(struct timeval)); - timeout.tv_sec = 0; - timeout.tv_usec = 100000; // 100ms TODO tune this + timeout.tv_sec = 1; + timeout.tv_usec = 0; if (unlikely(::setsockopt (_channel, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0)) { @@ -106,11 +106,32 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so LOG(logLevelDebug, "UDP socket %s closed.", inetAddressToString(_bindAddress).c_str()); - - // TODO should I wait thread to complete first and then destroy - // on some OSes (Darwin) this also exits rcvfrom() and speeds up shutdown + + epicsSocketSystemCallInterruptMechanismQueryInfo info = + epicsSocketSystemCallInterruptMechanismQuery (); + switch ( info ) { + case esscimqi_socketCloseRequired: + epicsSocketDestroy ( _channel ); + break; + case esscimqi_socketBothShutdownRequired: + { + int status = ::shutdown ( _channel, SHUT_RDWR ); + if ( status ) { + char sockErrBuf[64]; + epicsSocketConvertErrnoToString ( + sockErrBuf, sizeof ( sockErrBuf ) ); + LOG(logLevelDebug, + "UDP socket %s failed to shutdown: %s.", + inetAddressToString(_bindAddress).c_str(), sockErrBuf); + } + } + break; + case esscimqi_socketSigAlarmRequired: + // TODO (not supported anymore anyway) + default: epicsSocketDestroy(_channel); } +} // TODO send yourself a packet From dea00d53094fcf547ec39ffd7b1fb3ba98cd30f7 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 25 Mar 2014 13:16:34 +0100 Subject: [PATCH 11/19] enabled codec based client side --- pvAccessApp/remote/blockingTCPAcceptor.cpp | 1 + pvAccessApp/remote/blockingTCPConnector.cpp | 21 +++++++++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/pvAccessApp/remote/blockingTCPAcceptor.cpp b/pvAccessApp/remote/blockingTCPAcceptor.cpp index caf7a2a..0f7b381 100644 --- a/pvAccessApp/remote/blockingTCPAcceptor.cpp +++ b/pvAccessApp/remote/blockingTCPAcceptor.cpp @@ -196,6 +196,7 @@ namespace pvAccess { * Create transport, it registers itself to the registry. * Each transport should have its own response handler since it is not "shareable" */ + // TODO it is shareable?!!! but code is not adopted to it... std::auto_ptr responseHandler = _responseHandlerFactory->createResponseHandler(); BlockingServerTCPTransportCodec::shared_pointer transport = BlockingServerTCPTransportCodec::create( diff --git a/pvAccessApp/remote/blockingTCPConnector.cpp b/pvAccessApp/remote/blockingTCPConnector.cpp index 44397d8..1871e9d 100644 --- a/pvAccessApp/remote/blockingTCPConnector.cpp +++ b/pvAccessApp/remote/blockingTCPConnector.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -78,8 +79,7 @@ namespace epics { Context::shared_pointer context = _context.lock(); // first try to check cache w/o named lock... - Transport::shared_pointer tt = context->getTransportRegistry()->get("TCP", &address, priority); - BlockingClientTCPTransport::shared_pointer transport = std::tr1::static_pointer_cast(tt); + Transport::shared_pointer transport = context->getTransportRegistry()->get("TCP", &address, priority); if(transport.get()) { LOG(logLevelDebug, "Reusing existing connection to PVA server: %s", @@ -92,8 +92,7 @@ namespace epics { if(lockAcquired) { try { // ... transport created during waiting in lock - tt = context->getTransportRegistry()->get("TCP", &address, priority); - transport = std::tr1::static_pointer_cast(tt); + transport = context->getTransportRegistry()->get("TCP", &address, priority); if(transport.get()) { LOG(logLevelDebug, "Reusing existing connection to PVA server: %s", @@ -141,8 +140,18 @@ namespace epics { // create transport // TODO introduce factory - transport = BlockingClientTCPTransport::create( - context, socket, responseHandler, _receiveBufferSize, + // get TCP send buffer size + osiSocklen_t intLen = sizeof(int); + int _socketSendBufferSize; + retval = getsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *)&_socketSendBufferSize, &intLen); + if(retval<0) { + char strBuffer[64]; + epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); + LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer); + } + + transport = BlockingClientTCPTransportCodec::create( + context, socket, responseHandler, _receiveBufferSize, _socketSendBufferSize, client, transportRevision, _beaconInterval, priority); // verify From 0230d5f241706b3d959b0ed93a8bb1774f340540 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 25 Mar 2014 20:50:21 +0100 Subject: [PATCH 12/19] codec valgrind clean, fast search and UDP shutdown, robust testSum service --- pvAccessApp/remote/blockingUDPTransport.cpp | 1 + pvAccessApp/remote/codec.cpp | 19 +++++--- pvAccessApp/remote/codec.h | 2 +- .../remote/simpleChannelSearchManagerImpl.cpp | 21 ++++++--- testApp/remote/testCodec.cpp | 5 ++ testApp/remote/testServer.cpp | 46 ++++++++++++++----- 6 files changed, 68 insertions(+), 26 deletions(-) diff --git a/pvAccessApp/remote/blockingUDPTransport.cpp b/pvAccessApp/remote/blockingUDPTransport.cpp index 84aa0fd..1c46b8c 100644 --- a/pvAccessApp/remote/blockingUDPTransport.cpp +++ b/pvAccessApp/remote/blockingUDPTransport.cpp @@ -124,6 +124,7 @@ inline int sendto(int s, const char *buf, size_t len, int flags, const struct so "UDP socket %s failed to shutdown: %s.", inetAddressToString(_bindAddress).c_str(), sockErrBuf); } + epicsSocketDestroy ( _channel ); } break; case esscimqi_socketSigAlarmRequired: diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 081e42a..9698e8c 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -54,7 +54,7 @@ namespace epics { //PRIVATE _storedPayloadSize(0), _storedPosition(0), _startPosition(0), _maxSendPayloadSize(0), - _lastMessageStartPosition(0),_lastSegmentedMessageType(0), + _lastMessageStartPosition(std::numeric_limits::max()),_lastSegmentedMessageType(0), _lastSegmentedMessageCommand(0), _nextMessagePayloadOffset(0), _byteOrderFlag(EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG ? 0x80 : 0x00), _socketSendBufferSize(0) @@ -345,6 +345,10 @@ namespace epics { // do we already have requiredBytes available? std::size_t remainingBytes = _socketBuffer->getRemaining(); if (remainingBytes >= requiredBytes) { + LOG(logLevelTrace, + "AbstractCodec::readToBuffer requiredBytes: %u" + " <= remainingBytes: %d (threadId: %u)", + requiredBytes, remainingBytes); return true; } @@ -384,7 +388,7 @@ namespace epics { { LOG(logLevelTrace, - "AbstractCodec::before close (threadId: %u)", + "AbstractCodec::before close on bytesRead < 0 condition (threadId: %u)", epicsThreadGetIdSelf()); close(); @@ -689,6 +693,7 @@ namespace epics { if (_lastSegmentedMessageType == 0) { std::size_t flagsPosition = _lastMessageStartPosition + 2; + std::cout << "peek at " << flagsPosition << " " << _lastMessageStartPosition << std::endl; epics::pvData::int8 type = _sendBuffer->getByte(flagsPosition); // set first segment bit _sendBuffer->putByte(flagsPosition, (type | 0x10)); @@ -759,7 +764,7 @@ namespace epics { flush(false); } - + // assumes startMessage was called (or header is in place), because endMessage(true) is later called that peeks and sets _lastSegmentedMessageType void AbstractCodec::flushSerializeBuffer() { LOG(logLevelTrace, @@ -1120,7 +1125,7 @@ namespace epics { bool BlockingAbstractCodec::isOpen() { - LOG(logLevelTrace, "BlockingAbstractCodec::isOpen enter: (threadId: %u)", + LOG(logLevelTrace, "BlockingAbstractCodec::isOpen %d (threadId: %u)", _isOpen.get(), epicsThreadGetIdSelf()); return _isOpen.get(); @@ -1182,6 +1187,8 @@ namespace epics { " EXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIT: (threadId: %u)", epicsThreadGetIdSelf()); + bac->_shutdownEvent.signal(); + } @@ -1215,9 +1222,7 @@ namespace epics { // wait read thread to die - //TODO epics join thread - //readThread.join(); // TODO timeout - //bac->_shutdownEvent.signal(); + bac->_shutdownEvent.wait(); // call internal destroy LOG(logLevelTrace, "XXXXXXXXXXXXXXXXXXXXXXXXXXXX" diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index 21146f9..bcc034c 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -813,7 +813,7 @@ namespace epics { TransportClient::shared_pointer const & client, epics::pvData::int8 remoteTransportRevision, float beaconInterval, - int16_t priority ); + int16_t priority); public: static shared_pointer create( diff --git a/pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp b/pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp index 773a2bc..f747bf5 100644 --- a/pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp +++ b/pvAccessApp/remote/simpleChannelSearchManagerImpl.cpp @@ -107,13 +107,20 @@ void SimpleChannelSearchManagerImpl::registerSearchInstance(SearchInstance::shar if (m_canceled.get()) return; - Lock guard(m_channelMutex); - //overrides if already registered - m_channels[channel->getSearchInstanceID()] = channel; - - Lock guard2(m_userValueMutex); - int32_t& userValue = channel->getUserValue(); - userValue = 1; + bool immediateTrigger; + { + Lock guard(m_channelMutex); + //overrides if already registered + m_channels[channel->getSearchInstanceID()] = channel; + immediateTrigger = m_channels.size() == 1; + + Lock guard2(m_userValueMutex); + int32_t& userValue = channel->getUserValue(); + userValue = 1; + } + + if (immediateTrigger) + callback(); } void SimpleChannelSearchManagerImpl::unregisterSearchInstance(SearchInstance::shared_pointer const & channel) diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 495735c..0897354 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -3004,6 +3004,11 @@ namespace epics { void send(epics::pvData::ByteBuffer* buffer, TransportSendControl* control) { + // after connection_closed_exception is thrown codec is no longer valid, + // however we want to do some tests and in order for memory checkers not to complain + // the following step is needed + memset((void*)buffer->getBuffer(), 0, buffer->getSize()); + throw connection_closed_exception( "expected test exception"); } diff --git a/testApp/remote/testServer.cpp b/testApp/remote/testServer.cpp index 90eba81..6f1dd11 100644 --- a/testApp/remote/testServer.cpp +++ b/testApp/remote/testServer.cpp @@ -1499,21 +1499,45 @@ public: } else if (m_channelName == "testSum") { - int a = pvArgument->getIntField("a")->get(); - int b = pvArgument->getIntField("b")->get(); + PVStructure::shared_pointer args( + (pvArgument->getStructure()->getID() == "uri:ev4:nt/2012/pwd:NTURI") ? + pvArgument->getStructureField("query") : + pvArgument + ); - FieldCreatePtr fieldCreate = getFieldCreate(); + const String helpText = + "Calculates a sum of two integer values.\n" + "Arguments:\n" + "\tint a\tfirst integer number\n" + "\tint b\tsecond integer number\n"; + if (handleHelp(args, m_channelRPCRequester, helpText)) + return; - StringArray fieldNames; - fieldNames.push_back("c"); - FieldConstPtrArray fields; - fields.push_back(fieldCreate->createScalar(pvInt)); - StructureConstPtr resultStructure = fieldCreate->createStructure(fieldNames, fields); + PVInt::shared_pointer pa = args->getSubField("a"); + PVInt::shared_pointer pb = args->getSubField("b"); + if (!pa || !pb) + { + PVStructure::shared_pointer nullPtr; + Status errorStatus(Status::STATUSTYPE_ERROR, "int a and int b arguments are required"); + m_channelRPCRequester->requestDone(errorStatus, nullPtr); + return; + } - PVStructure::shared_pointer result = getPVDataCreate()->createPVStructure(resultStructure); - result->getIntField("c")->put(a+b); + int a = pa->get(); + int b = pb->get(); - m_channelRPCRequester->requestDone(Status::Ok, result); + FieldCreatePtr fieldCreate = getFieldCreate(); + + StringArray fieldNames; + fieldNames.push_back("c"); + FieldConstPtrArray fields; + fields.push_back(fieldCreate->createScalar(pvInt)); + StructureConstPtr resultStructure = fieldCreate->createStructure(fieldNames, fields); + + PVStructure::shared_pointer result = getPVDataCreate()->createPVStructure(resultStructure); + result->getIntField("c")->put(a+b); + + m_channelRPCRequester->requestDone(Status::Ok, result); } else if (m_channelName.find("testServerShutdown") == 0) From d925e0ccfb20ca9d5733f46f1e8243aa6804b7a2 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Tue, 25 Mar 2014 21:26:43 +0100 Subject: [PATCH 13/19] codec: socketAddress mirroring removed --- pvAccessApp/remote/codec.cpp | 2 -- pvAccessApp/remote/codec.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 9698e8c..88a1fcf 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -693,7 +693,6 @@ namespace epics { if (_lastSegmentedMessageType == 0) { std::size_t flagsPosition = _lastMessageStartPosition + 2; - std::cout << "peek at " << flagsPosition << " " << _lastMessageStartPosition << std::endl; epics::pvData::int8 type = _sendBuffer->getByte(flagsPosition); // set first segment bit _sendBuffer->putByte(flagsPosition, (type | 0x10)); @@ -1273,7 +1272,6 @@ namespace epics { & (~(PVA_ALIGNMENT - 1)))), sendBufferSize), _channel(channel) { - // get remote address osiSocklen_t saSize = sizeof(sockaddr); int retval = getpeername(_channel, &(_socketAddress.sa), &saSize); diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index bcc034c..5f48f32 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -441,7 +441,6 @@ namespace epics { void internalDestroy(); - private: SOCKET _channel; osiSockAddr _socketAddress; }; @@ -658,7 +657,6 @@ namespace epics { Context::shared_pointer _context; - osiSockAddr _socketAddress; IntrospectionRegistry _incomingIR; IntrospectionRegistry _outgoingIR; From 3e514c819accf18cbfafa3a4be234524e912b03a Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 26 Mar 2014 10:54:16 +0100 Subject: [PATCH 14/19] bad bad static_cast removed, clean read thread shutdown --- pvAccessApp/remote/codec.cpp | 29 ++++++++++++------- .../remoteClient/clientContextImpl.cpp | 2 +- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 88a1fcf..e01a8cb 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -199,16 +199,17 @@ namespace epics { { // handle response processApplicationMessage(); - postProcess = false; + + if (!isOpen()) + return; + + postProcess = false; postProcessApplicationMessage(); } - catch(...) //finally + catch(...) { if (postProcess) { - if (!isOpen()) - return; - postProcessApplicationMessage(); } @@ -230,9 +231,6 @@ namespace epics { void AbstractCodec::postProcessApplicationMessage() { - if (!isOpen()) - return; - // can be closed by now // isOpen() should be efficiently implemented while (true) @@ -1200,7 +1198,7 @@ namespace epics { BlockingAbstractCodec *bac = static_cast(param); Transport::shared_pointer ptr = bac->shared_from_this(); - + bac->setSenderThread(); while (bac->isOpen()) @@ -1232,7 +1230,7 @@ namespace epics { "YYYYYYYYYYYYYYYYYYYYYYYYYYYY (threadId: %u)", epicsThreadGetIdSelf()); - LOG(logLevelTrace, + LOG(logLevelTrace, "BlockingAbstractCodec::sendThread EXIIIIT (threadId: %u)", epicsThreadGetIdSelf()); @@ -1429,7 +1427,10 @@ namespace epics { int bytesRead = recv(_channel, (char*)(dst->getArray()+pos), remaining, 0); - + + // NOTE: do not log here, you might override SOCKERRNO relevant to recv() operation above + + /* LOG(logLevelTrace, "BlockingSocketAbstractCodec::read after recv, read: %d", bytesRead," (threadId: %u)", @@ -1440,6 +1441,7 @@ namespace epics { hexDump(std::string("READ"), (const int8 *)(dst->getArray()+pos), bytesRead); } + */ if(unlikely(bytesRead<=0)) { @@ -1447,6 +1449,11 @@ namespace epics { { int socketError = SOCKERRNO; + LOG(logLevelTrace, + "BlockingSocketAbstractCodec::read SOCKERRNO %d", socketError, + " (threadId: %u)", + epicsThreadGetIdSelf()); + // interrupted or timeout if (socketError == EINTR || socketError == EAGAIN || diff --git a/pvAccessApp/remoteClient/clientContextImpl.cpp b/pvAccessApp/remoteClient/clientContextImpl.cpp index ddd682c..9a162cb 100644 --- a/pvAccessApp/remoteClient/clientContextImpl.cpp +++ b/pvAccessApp/remoteClient/clientContextImpl.cpp @@ -4117,7 +4117,7 @@ TODO auto_ptr handler(new ClientResponseHandler(shared_from_this())); Transport::shared_pointer t = m_connector->connect(client, handler, *serverAddress, minorRevision, priority); // TODO !!! - static_pointer_cast(t)->setFlushStrategy(m_flushStrategy); + //static_pointer_cast(t)->setFlushStrategy(m_flushStrategy); return t; } catch (...) From baaea33bc6c12beb085ac13aafb2627bd1c00c9c Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 26 Mar 2014 11:51:28 +0100 Subject: [PATCH 15/19] no crashes, all tests needs to pass still --- pvAccessApp/remote/codec.cpp | 73 +++++++++++++++++++----------------- pvAccessApp/remote/codec.h | 19 ++++++++++ 2 files changed, 58 insertions(+), 34 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index e01a8cb..8a95a0f 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -1098,7 +1098,13 @@ namespace epics { " (threadId: %u)", epicsThreadGetIdSelf()); + // clean resources + internalClose(true); + _sendQueue.wakeup(); + + // post close + internalPostClose(true); } else { LOG(logLevelTrace, @@ -1108,6 +1114,11 @@ namespace epics { } } + void BlockingAbstractCodec::internalClose(bool /*force*/) { + } + + void BlockingAbstractCodec::internalPostClose(bool /*force*/) { + } bool BlockingAbstractCodec::terminated() { @@ -1115,7 +1126,6 @@ namespace epics { "BlockingAbstractCodec::terminated enter: (threadId: %u)", epicsThreadGetIdSelf()); - //TODO OPEN QUESTION TO MATEJ return !isOpen(); } @@ -1304,7 +1314,7 @@ namespace epics { epicsThreadGetIdSelf()); } - + // must be called only once, when there will be no operation on socket (e.g. just before tx/rx thread exists) void BlockingSocketAbstractCodec::internalDestroy() { LOG(logLevelTrace, @@ -1610,44 +1620,38 @@ namespace epics { // send immediately control->flush(true); - } } - - - // TODO - /* + void BlockingServerTCPTransportCodec::destroyAllChannels() { + Lock lock(_channelsMutex); + if(_channels.size()==0) return; - void BlockingServerTCPTransportCodec::destroyAllChannels() { - Lock lock(_channelsMutex); - if(_channels.size()==0) return; + 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()); + + std::map::iterator it = _channels.begin(); + for(; it!=_channels.end(); it++) + it->second->destroy(); + + _channels.clear(); + } + + void BlockingServerTCPTransportCodec::internalClose(bool force) { + Transport::shared_pointer thisSharedPtr = shared_from_this(); + BlockingTCPTransportCodec::internalClose(force); + destroyAllChannels(); + } - char ipAddrStr[64]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG( - logLevelDebug, - "Transport to %s still has %u channel(s) active and closing...", - ipAddrStr, (unsigned int)_channels.size()); - map::iterator it = _channels.begin(); - for(; it!=_channels.end(); it++) - it->second->destroy(); - _channels.clear(); - } - void BlockingServerTCPTransportCodec::internalClose(bool force) { - Transport::shared_pointer thisSharedPtr = shared_from_this(); - BlockingTCPTransport::internalClose(force); - destroyAllChannels(); - } - void BlockingServerTCPTransportCodec::internalPostClose(bool forced) { - BlockingTCPTransport::internalPostClose(forced); - } -*/ - BlockingClientTCPTransportCodec::BlockingClientTCPTransportCodec( Context::shared_pointer const & context, @@ -1750,14 +1754,14 @@ namespace epics { // _mutex is held when this method is called void BlockingClientTCPTransportCodec::internalClose(bool forced) { -// TODO !!! BlockingTCPTransportCodec::internalClose(forced); + BlockingTCPTransportCodec::internalClose(forced); TimerCallbackPtr tcb = std::tr1::dynamic_pointer_cast(shared_from_this()); _context->getTimer()->cancel(tcb); } void BlockingClientTCPTransportCodec::internalPostClose(bool forced) { -// TODO !!! BlockingTCPTransportCodec::internalPostClose(forced); + BlockingTCPTransportCodec::internalPostClose(forced); // _owners cannot change when transport is closed closedNotifyClients(); @@ -1890,5 +1894,6 @@ namespace epics { - + } + } diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index 5f48f32..f5b5b17 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -412,6 +412,20 @@ namespace epics { void sendBufferFull(int tries); virtual void internalDestroy() = 0; + /** + * Called to any resources just before closing transport + * @param[in] force flag indicating if forced (e.g. forced + * disconnect) is required + */ + virtual void internalClose(bool force); + + /** + * Called to any resources just after closing transport and without any locks held on transport + * @param[in] force flag indicating if forced (e.g. forced + * disconnect) is required + */ + virtual void internalPostClose(bool force); + private: AtomicValue _isOpen; volatile epicsThreadId _readThread; @@ -777,6 +791,11 @@ namespace epics { virtual ~BlockingServerTCPTransportCodec(); + protected: + + void destroyAllChannels(); + virtual void internalClose(bool force); + private: /** From ca2828c6dc633e8ceb50d3729a65636feb3a8bed Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 26 Mar 2014 12:42:50 +0100 Subject: [PATCH 16/19] trace log removal, old codec files removed --- pvAccessApp/Makefile | 3 - .../remote/blockingClientTCPTransport.cpp | 240 --- .../remote/blockingServerTCPTransport.cpp | 136 -- pvAccessApp/remote/blockingTCP.h | 721 --------- pvAccessApp/remote/blockingTCPAcceptor.cpp | 7 +- pvAccessApp/remote/blockingTCPConnector.cpp | 2 +- pvAccessApp/remote/blockingTCPTransport.cpp | 1342 ----------------- pvAccessApp/remote/codec.cpp | 317 +--- pvAccessApp/remote/codec.h | 163 +- pvAccessCPP.files | 3 - testApp/remote/testCodec.cpp | 1 + 11 files changed, 17 insertions(+), 2918 deletions(-) delete mode 100644 pvAccessApp/remote/blockingClientTCPTransport.cpp delete mode 100644 pvAccessApp/remote/blockingServerTCPTransport.cpp delete mode 100644 pvAccessApp/remote/blockingTCPTransport.cpp diff --git a/pvAccessApp/Makefile b/pvAccessApp/Makefile index fc2a966..df20643 100644 --- a/pvAccessApp/Makefile +++ b/pvAccessApp/Makefile @@ -49,10 +49,7 @@ INC += codec.h LIBSRCS += blockingUDPTransport.cpp LIBSRCS += blockingUDPConnector.cpp LIBSRCS += beaconHandler.cpp -LIBSRCS += blockingTCPTransport.cpp -LIBSRCS += blockingClientTCPTransport.cpp LIBSRCS += blockingTCPConnector.cpp -LIBSRCS += blockingServerTCPTransport.cpp LIBSRCS += simpleChannelSearchManagerImpl.cpp LIBSRCS += abstractResponseHandler.cpp LIBSRCS += blockingTCPAcceptor.cpp diff --git a/pvAccessApp/remote/blockingClientTCPTransport.cpp b/pvAccessApp/remote/blockingClientTCPTransport.cpp deleted file mode 100644 index ff6f2c6..0000000 --- a/pvAccessApp/remote/blockingClientTCPTransport.cpp +++ /dev/null @@ -1,240 +0,0 @@ -/** - * 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 -#include -#include - -#include - -#include -#include -#include - -using namespace std; -using namespace epics::pvData; - -namespace epics { - namespace pvAccess { - -#define EXCEPTION_GUARD(code) try { code; } \ - catch (std::exception &e) { LOG(logLevelError, "Unhandled exception caught from code at %s:%d: %s", __FILE__, __LINE__, e.what()); } \ - catch (...) { LOG(logLevelError, "Unhandled exception caught from code at %s:%d.", __FILE__, __LINE__); } - - BlockingClientTCPTransport::BlockingClientTCPTransport( - Context::shared_pointer const & context, SOCKET channel, - auto_ptr& responseHandler, int receiveBufferSize, - TransportClient::shared_pointer client, int8 /*remoteTransportRevision*/, - float beaconInterval, int16 priority) : - BlockingTCPTransport(context, channel, responseHandler, receiveBufferSize, priority), - _connectionTimeout(beaconInterval*1000), - _unresponsiveTransport(false), - _verifyOrEcho(true) - { -// _autoDelete = false; - - // initialize owners list, send queue - acquire(client); - - // use immediate for clients - setFlushStrategy(DELAYED); - - // setup connection timeout timer (watchdog) - epicsTimeGetCurrent(&_aliveTimestamp); - } - - void BlockingClientTCPTransport::start() - { - TimerCallbackPtr tcb = std::tr1::dynamic_pointer_cast(shared_from_this()); - _context->getTimer()->schedulePeriodic(tcb, _connectionTimeout, _connectionTimeout); - BlockingTCPTransport::start(); - } - - BlockingClientTCPTransport::~BlockingClientTCPTransport() { - } - - void BlockingClientTCPTransport::callback() { - epicsTimeStamp currentTime; - epicsTimeGetCurrent(¤tTime); - - _mutex.lock(); - // no exception expected here - double diff = epicsTimeDiffInSeconds(¤tTime, &_aliveTimestamp); - _mutex.unlock(); - - if(diff>2*_connectionTimeout) { - unresponsiveTransport(); - } - // use some k (3/4) to handle "jitter" - else if(diff>=((3*_connectionTimeout)/4)) { - // send echo - TransportSender::shared_pointer transportSender = std::tr1::dynamic_pointer_cast(shared_from_this()); - enqueueSendRequest(transportSender); - } - } - - void BlockingClientTCPTransport::unresponsiveTransport() { - Lock lock(_mutex); - if(!_unresponsiveTransport) { - _unresponsiveTransport = true; - - TransportClientMap_t::iterator it = _owners.begin(); - for(; it!=_owners.end(); it++) { - TransportClient::shared_pointer client = it->second.lock(); - if (client) - { - EXCEPTION_GUARD(client->transportUnresponsive()); - } - } - } - } - - bool BlockingClientTCPTransport::acquire(TransportClient::shared_pointer const & client) { - Lock lock(_mutex); - if(_closed.get()) return false; - - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG(logLevelDebug, "Acquiring transport to %s.", ipAddrStr); - - _owners[client->getID()] = TransportClient::weak_pointer(client); - //_owners.insert(TransportClient::weak_pointer(client)); - - return true; - } - - // _mutex is held when this method is called - void BlockingClientTCPTransport::internalClose(bool forced) { - BlockingTCPTransport::internalClose(forced); - - TimerCallbackPtr tcb = std::tr1::dynamic_pointer_cast(shared_from_this()); - _context->getTimer()->cancel(tcb); - } - - void BlockingClientTCPTransport::internalPostClose(bool forced) { - BlockingTCPTransport::internalPostClose(forced); - - // _owners cannot change when transport is closed - closedNotifyClients(); - } - - /** - * Notifies clients about disconnect. - */ - void BlockingClientTCPTransport::closedNotifyClients() { - - // check if still acquired - size_t refs = _owners.size(); - if(refs>0) { - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG( - logLevelDebug, - "Transport to %s still has %d client(s) active and closing...", - ipAddrStr, refs); - - TransportClientMap_t::iterator it = _owners.begin(); - for(; it!=_owners.end(); it++) { - TransportClient::shared_pointer client = it->second.lock(); - if (client) - { - EXCEPTION_GUARD(client->transportClosed()); - } - } - - } - - _owners.clear(); - } - - //void BlockingClientTCPTransport::release(TransportClient::shared_pointer const & client) { - void BlockingClientTCPTransport::release(pvAccessID clientID) { - Lock lock(_mutex); - if(_closed.get()) return; - - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - - LOG(logLevelDebug, "Releasing transport to %s.", ipAddrStr); - - _owners.erase(clientID); - //_owners.erase(TransportClient::weak_pointer(client)); - - // not used anymore, close it - // TODO consider delayed destruction (can improve performance!!!) - if(_owners.size()==0) close(); // TODO close(false) - } - - void BlockingClientTCPTransport::aliveNotification() { - Lock guard(_mutex); - epicsTimeGetCurrent(&_aliveTimestamp); - if(_unresponsiveTransport) responsiveTransport(); - } - - void BlockingClientTCPTransport::responsiveTransport() { - Lock lock(_mutex); - if(_unresponsiveTransport) { - _unresponsiveTransport = false; - - Transport::shared_pointer thisSharedPtr = shared_from_this(); - TransportClientMap_t::iterator it = _owners.begin(); - for(; it!=_owners.end(); it++) { - TransportClient::shared_pointer client = it->second.lock(); - if (client) - { - EXCEPTION_GUARD(client->transportResponsive(thisSharedPtr)); - } - } - } - } - - void BlockingClientTCPTransport::changedTransport() { - outgoingIR.reset(); - - Lock lock(_mutex); - TransportClientMap_t::iterator it = _owners.begin(); - for(; it!=_owners.end(); it++) { - TransportClient::shared_pointer client = it->second.lock(); - if (client) - { - EXCEPTION_GUARD(client->transportChanged()); - } - } - } - - void BlockingClientTCPTransport::send(ByteBuffer* buffer, - TransportSendControl* control) { - if(_verifyOrEcho) { - /* - * send verification response message - */ - - control->startMessage(CMD_CONNECTION_VALIDATION, 2*sizeof(int32)+sizeof(int16)); - - // receive buffer size - buffer->putInt(static_cast(getReceiveBufferSize())); - - // socket receive buffer size - buffer->putInt(static_cast(getSocketReceiveBufferSize())); - - // connection priority - buffer->putShort(getPriority()); - - // send immediately - control->flush(true); - - _verifyOrEcho = false; - } - else { - control->startMessage(CMD_ECHO, 0); - // send immediately - control->flush(true); - } - - } - - } -} diff --git a/pvAccessApp/remote/blockingServerTCPTransport.cpp b/pvAccessApp/remote/blockingServerTCPTransport.cpp deleted file mode 100644 index 9a7bc94..0000000 --- a/pvAccessApp/remote/blockingServerTCPTransport.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/** - * 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 -#include -#include - -#include -#include - -/* standard */ -#include - -using namespace epics::pvData; -using namespace std; - -namespace epics { -namespace pvAccess { - - BlockingServerTCPTransport::BlockingServerTCPTransport( - Context::shared_pointer const & context, SOCKET channel, - auto_ptr& responseHandler, int receiveBufferSize) : - BlockingTCPTransport(context, channel, responseHandler, receiveBufferSize, PVA_DEFAULT_PRIORITY), - _lastChannelSID(0) - { - // for performance testing - setFlushStrategy(DELAYED); - _delay = 0.000; - - // NOTE: priority not yet known, default priority is used to register/unregister - // TODO implement priorities in Reactor... not that user will - // change it.. still getPriority() must return "registered" priority! - - //start(); - } - - BlockingServerTCPTransport::~BlockingServerTCPTransport() { - } - - void BlockingServerTCPTransport::destroyAllChannels() { - Lock lock(_channelsMutex); - if(_channels.size()==0) return; - - char ipAddrStr[64]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - - LOG( - logLevelDebug, - "Transport to %s still has %u channel(s) active and closing...", - ipAddrStr, (unsigned int)_channels.size()); - - map::iterator it = _channels.begin(); - for(; it!=_channels.end(); it++) - it->second->destroy(); - - _channels.clear(); - } - - void BlockingServerTCPTransport::internalClose(bool force) { - Transport::shared_pointer thisSharedPtr = shared_from_this(); - BlockingTCPTransport::internalClose(force); - destroyAllChannels(); - } - - void BlockingServerTCPTransport::internalPostClose(bool forced) { - BlockingTCPTransport::internalPostClose(forced); - } - - pvAccessID BlockingServerTCPTransport::preallocateChannelSID() { - Lock lock(_channelsMutex); - // search first free (theoretically possible loop of death) - pvAccessID sid = ++_lastChannelSID; - while(_channels.find(sid)!=_channels.end()) - sid = ++_lastChannelSID; - return sid; - } - - void BlockingServerTCPTransport::registerChannel(pvAccessID sid, ServerChannel::shared_pointer const & channel) { - Lock lock(_channelsMutex); - _channels[sid] = channel; - } - - void BlockingServerTCPTransport::unregisterChannel(pvAccessID sid) { - Lock lock(_channelsMutex); - _channels.erase(sid); - } - - ServerChannel::shared_pointer BlockingServerTCPTransport::getChannel(pvAccessID sid) { - Lock lock(_channelsMutex); - - map::iterator it = _channels.find(sid); - if(it!=_channels.end()) return it->second; - - return ServerChannel::shared_pointer(); - } - - int BlockingServerTCPTransport::getChannelCount() { - Lock lock(_channelsMutex); - return static_cast(_channels.size()); - } - - void BlockingServerTCPTransport::send(ByteBuffer* buffer, - TransportSendControl* control) { - - // - // set byte order control message - // - - control->ensureBuffer(PVA_MESSAGE_HEADER_SIZE); - buffer->putByte(PVA_MAGIC); - buffer->putByte(PVA_VERSION); - buffer->putByte(0x01 | ((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00)); // control + big endian - buffer->putByte(2); // set byte order - buffer->putInt(0); - - - // - // send verification message - // - control->startMessage(CMD_CONNECTION_VALIDATION, 2*sizeof(int32)); - - // receive buffer size - buffer->putInt(static_cast(getReceiveBufferSize())); - - // socket receive buffer size - buffer->putInt(static_cast(getSocketReceiveBufferSize())); - - // send immediately - control->flush(true); - } - - } -} diff --git a/pvAccessApp/remote/blockingTCP.h b/pvAccessApp/remote/blockingTCP.h index 35d9945..b70c758 100644 --- a/pvAccessApp/remote/blockingTCP.h +++ b/pvAccessApp/remote/blockingTCP.h @@ -40,584 +40,9 @@ #include #include -// not implemented anyway -#define FLOW_CONTROL 0 - namespace epics { namespace pvAccess { - //class MonitorSender; - - enum ReceiveStage { - READ_FROM_SOCKET, PROCESS_HEADER, PROCESS_PAYLOAD, UNDEFINED_STAGE - }; - - class BlockingTCPTransport : - public Transport, - public TransportSendControl, - public std::tr1::enable_shared_from_this - { - protected: - BlockingTCPTransport(Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, int receiveBufferSize, - epics::pvData::int16 priority); - - public: - virtual bool isClosed() { - return _closed.get(); - } - - virtual epics::pvData::int8 getRevision() const { - return PVA_PROTOCOL_REVISION; - } - - virtual void setRemoteRevision(epics::pvData::int8 revision) { - _remoteTransportRevision = revision; - } - - virtual void setRemoteTransportReceiveBufferSize(std::size_t remoteTransportReceiveBufferSize) { - _remoteTransportReceiveBufferSize = remoteTransportReceiveBufferSize; - } - - virtual void setRemoteTransportSocketReceiveBufferSize(std::size_t socketReceiveBufferSize) { - _remoteTransportSocketReceiveBufferSize = socketReceiveBufferSize; - } - - virtual epics::pvData::String getType() const { - return epics::pvData::String("TCP"); - } - - virtual void aliveNotification() { - // noop - } - - virtual void changedTransport() { - // noop - } - - virtual const osiSockAddr* getRemoteAddress() const { - return &_socketAddress; - } - - virtual epics::pvData::int16 getPriority() const { - return _priority; - } - - virtual std::size_t getReceiveBufferSize() const { - return _socketBuffer->getSize(); - } - - /** - * Get remote transport receive buffer size (in bytes). - * @return remote transport receive buffer size - */ - virtual std::size_t getRemoteTransportReceiveBufferSize() const { - return _remoteTransportReceiveBufferSize; - } - - virtual std::size_t getSocketReceiveBufferSize() const; - - virtual bool verify(epics::pvData::int32 timeoutMs) { - return _verifiedEvent.wait(timeoutMs/1000.0); - - //epics::pvData::Lock lock(_verifiedMutex); - //return _verified; - } - - virtual void verified() { - epics::pvData::Lock lock(_verifiedMutex); - _verified = true; - _verifiedEvent.signal(); - } - - virtual void setRecipient(const osiSockAddr& /*sendTo*/) { - // noop - } - - virtual void flush(bool lastMessageCompleted); - virtual void startMessage(epics::pvData::int8 command, std::size_t ensureCapacity); - virtual void endMessage(); - - virtual void flushSerializeBuffer() { - flush(false); - } - - virtual void ensureBuffer(std::size_t size); - - virtual void alignBuffer(std::size_t alignment); - - virtual void ensureData(std::size_t size); - - virtual void alignData(std::size_t alignment); - - virtual bool directSerialize(epics::pvData::ByteBuffer *existingBuffer, const char* toSerialize, - std::size_t elementCount, std::size_t elementSize); - - virtual bool directDeserialize(epics::pvData::ByteBuffer *existingBuffer, char* deserializeTo, - std::size_t elementCount, std::size_t elementSize); - - void processReadIntoDirectBuffer(std::size_t bytesToRead); - - virtual void close(); - - virtual void setByteOrder(int /*byteOrder*/) - { - // not used this this implementation - } - - FlushStrategy getFlushStrategy() { - return _flushStrategy; - } - - void setFlushStrategy(FlushStrategy flushStrategy) { - _flushStrategy = flushStrategy; - } - - //void requestFlush(); - - /** - * Close and free connection resources. - */ - void freeConnectionResorces(); - - /** - * Starts the receive and send threads - */ - virtual void start(); - - virtual void enqueueSendRequest(TransportSender::shared_pointer const & sender); - - //void enqueueMonitorSendRequest(TransportSender::shared_pointer const & sender); - - virtual void enqueueOnlySendRequest(TransportSender::shared_pointer const & sender); - - virtual void flushSendQueue(); - - virtual void cachedSerialize( - const std::tr1::shared_ptr& field, epics::pvData::ByteBuffer* buffer) - { - outgoingIR.serialize(field, buffer, this); - } - - virtual std::tr1::shared_ptr - cachedDeserialize(epics::pvData::ByteBuffer* buffer) - { - return incomingIR.deserialize(buffer, this); - } - - protected: - - virtual void processReadCached(bool nestedCall, - ReceiveStage inStage, std::size_t requiredBytes); - - /** - * Called to any resources just before closing transport - * @param[in] force flag indicating if forced (e.g. forced - * disconnect) is required - */ - virtual void internalClose(bool force); - - /** - * Called to any resources just after closing transport and without any locks held on transport - * @param[in] force flag indicating if forced (e.g. forced - * disconnect) is required - */ - virtual void internalPostClose(bool force); - - /** - * Send a buffer through the transport. - * NOTE: TCP sent buffer/sending has to be synchronized (not done by this method). - * @param buffer[in] buffer to be sent - * @return success indicator - */ - virtual bool send(epics::pvData::ByteBuffer* buffer); - - virtual ~BlockingTCPTransport(); - - -#if FLOW_CONTROL - /** - * Default marker period. - */ - static const std::size_t MARKER_PERIOD = 1024; -#endif - static const std::size_t MAX_ENSURE_DATA_BUFFER_SIZE = 1024; - -// TODO - double _delay; - - /****** finally initialized at construction time and after start (called by the same thread) ********/ - - /** - * Corresponding channel. - */ - SOCKET _channel; - - /** - * Cached socket address. - */ - osiSockAddr _socketAddress; - - /** - * Priority. - * NOTE: Priority cannot just be changed, since it is registered - * in transport registry with given priority. - */ - epics::pvData::int16 _priority; - // TODO to be implemeneted - - /** - * PVAS response handler. - */ - std::auto_ptr _responseHandler; - - // TODO review int vs std::size_t - - /** - * Send buffer size. - */ - std::size_t _maxPayloadSize; - - /** - * Send buffer size. - */ - int _socketSendBufferSize; - - /** - * Marker "period" in bytes (every X bytes marker should be set). - */ - epics::pvData::int64 _markerPeriodBytes; - - - FlushStrategy _flushStrategy; - - - epicsThreadId _rcvThreadId; - - epicsThreadId _sendThreadId; - - // TODO - //MonitorSender* _monitorSender; - - Context::shared_pointer _context; - - bool _autoDelete; - - - - /**** after verification ****/ - - /** - * Remote side transport revision (minor). - */ - epics::pvData::int8 _remoteTransportRevision; - - /** - * Remote side transport receive buffer size. - */ - size_t _remoteTransportReceiveBufferSize; - - /** - * Remote side transport socket receive buffer size. - */ - size_t _remoteTransportSocketReceiveBufferSize; - - - - /*** send thread only - no need to sync ***/ - // NOTE: now all send-related external calls are TransportSender IF - // and its reference is only valid when called from send thread - - // initialized at construction time - std::deque _sendQueue; - epics::pvData::Mutex _sendQueueMutex; - - // initialized at construction time -// std::deque _monitorSendQueue; -// epics::pvData::Mutex _monitorMutex; - - /** - * Send buffer. - */ - epics::pvData::ByteBuffer* _sendBuffer; - -#if FLOW_CONTROL - /** - * Next planned marker position. - */ - epics::pvData::int64 _nextMarkerPosition; -#endif - - /** - * Send pending flag. - */ - bool _sendPending; - - /** - * Last message start position. - */ - int _lastMessageStartPosition; - - epics::pvData::int8 _lastSegmentedMessageType; - epics::pvData::int8 _lastSegmentedMessageCommand; - - bool _flushRequested; - - int _sendBufferSentPosition; - - epics::pvData::int8 _byteOrderFlag; - - /** - * Outgoing (codes generated by this party) introspection registry. - */ - IntrospectionRegistry outgoingIR; - - - - - - - /*** receive thread only - no need to sync ***/ - - // initialized at construction time - epics::pvData::ByteBuffer* _socketBuffer; - - std::size_t _startPosition; - - std::size_t _storedPayloadSize; - std::size_t _storedPosition; - std::size_t _storedLimit; - - epics::pvData::int8 _version; - epics::pvData::int8 _packetType; - epics::pvData::int8 _command; - std::size_t _payloadSize; - - ReceiveStage _stage; - - std::size_t _directPayloadRead; - char * _directBuffer; - -#if FLOW_CONTROL - - /** - * Total bytes received. - */ - epics::pvData::int64 _totalBytesReceived; -#endif - - /** - * Incoming (codes generated by other party) introspection registry. - */ - IntrospectionRegistry incomingIR; - - - - /*** send/receive thread shared ***/ - - /** - * Connection status - * NOTE: synced by _mutex - */ - AtomicBoolean _closed; - - // NOTE: synced by _mutex - bool _sendThreadExited; - - epics::pvData::Mutex _mutex; - - - bool _verified; - epics::pvData::Mutex _verifiedMutex; - - - - - epics::pvData::Event _sendQueueEvent; - - epics::pvData::Event _verifiedEvent; - - - - - -#if FLOW_CONTROL - /** - * Marker to send. - * NOTE: synced by _flowControlMutex - */ - int _markerToSend; - - /** - * Total bytes sent. - * NOTE: synced by _flowControlMutex - */ - epics::pvData::int64 _totalBytesSent; - - /** - * Calculated remote free buffer size. - * NOTE: synced by _flowControlMutex - */ - epics::pvData::int64 _remoteBufferFreeSpace; - - epics::pvData::Mutex _flowControlMutex; -#endif - - private: - - /** - * Internal method that clears and releases buffer. - * sendLock and sendBufferLock must be hold while calling this method. - */ - void clearAndReleaseBuffer(); - - void endMessage(bool hasMoreSegments); - - bool flush(); - - void processSendQueue(); - - static void rcvThreadRunner(void* param); - - static void sendThreadRunner(void* param); - - /** - * Free all send buffers (return them to the cached buffer allocator). - */ - void freeSendBuffers(); - }; - - - class BlockingClientTCPTransport : public BlockingTCPTransport, - public TransportSender, - public epics::pvData::TimerCallback { - - public: - POINTER_DEFINITIONS(BlockingClientTCPTransport); - - private: - BlockingClientTCPTransport(Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, int receiveBufferSize, - TransportClient::shared_pointer client, epics::pvData::int8 remoteTransportRevision, - float beaconInterval, epics::pvData::int16 priority); - - public: - static shared_pointer create(Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, int receiveBufferSize, - TransportClient::shared_pointer client, epics::pvData::int8 remoteTransportRevision, - float beaconInterval, epics::pvData::int16 priority) - { - shared_pointer thisPointer( - new BlockingClientTCPTransport(context, channel, responseHandler, receiveBufferSize, - client, remoteTransportRevision, beaconInterval, priority) - ); - thisPointer->start(); - return thisPointer; - } - - virtual void start(); - - virtual ~BlockingClientTCPTransport(); - - virtual void timerStopped() { - // noop - } - - virtual void callback(); - - /** - * Acquires transport. - * @param client client (channel) acquiring the transport - * @return true if transport was granted, false otherwise. - */ - virtual bool acquire(TransportClient::shared_pointer const & client); - - /** - * Releases transport. - * @param client client (channel) releasing the transport - */ - virtual void release(pvAccessID clientId); - //virtual void release(TransportClient::shared_pointer const & client); - - /** - * Alive notification. - * This method needs to be called (by newly received data or beacon) - * at least once in this period, if not echo will be issued - * and if there is not response to it, transport will be considered as unresponsive. - */ - virtual void aliveNotification(); - - /** - * Changed transport (server restared) notify. - */ - virtual void changedTransport(); - - virtual void lock() { - // noop - } - - virtual void unlock() { - // noop - } - - virtual void acquire() { - // noop, since does not make sence on itself - } - - virtual void release() { - // noop, since does not make sence on itself - } - - virtual void send(epics::pvData::ByteBuffer* buffer, - TransportSendControl* control); - - protected: - - virtual void internalClose(bool force); - virtual void internalPostClose(bool force); - - private: - - /** - * Owners (users) of the transport. - */ - // TODO consider using TR1 hash map - typedef std::map TransportClientMap_t; - TransportClientMap_t _owners; - - /** - * Connection timeout (no-traffic) flag. - */ - double _connectionTimeout; - - /** - * Unresponsive transport flag. - */ - bool _unresponsiveTransport; - - /** - * Timestamp of last "live" event on this transport. - */ - epicsTimeStamp _aliveTimestamp; - - bool _verifyOrEcho; - - /** - * Unresponsive transport notify. - */ - void unresponsiveTransport(); - - /** - * Notifies clients about disconnect. - */ - void closedNotifyClients(); - - /** - * Responsive transport notify. - */ - void responsiveTransport(); - }; - /** * Channel Access TCP connector. * @author Matej Sekoranja @@ -672,152 +97,6 @@ namespace epics { }; - class BlockingServerTCPTransport : public BlockingTCPTransport, - public ChannelHostingTransport, - public TransportSender { - public: - POINTER_DEFINITIONS(BlockingServerTCPTransport); - - private: - BlockingServerTCPTransport(Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, int receiveBufferSize); - public: - static shared_pointer create(Context::shared_pointer const & context, SOCKET channel, - std::auto_ptr& responseHandler, int receiveBufferSize) - { - shared_pointer thisPointer( - new BlockingServerTCPTransport(context, channel, responseHandler, receiveBufferSize) - ); - thisPointer->start(); - return thisPointer; - } - - virtual bool acquire(std::tr1::shared_ptr const & /*client*/) - { - return false; - } - - virtual void release(pvAccessID /*clientId*/) {} - - /** - * Preallocate new channel SID. - * @return new channel server id (SID). - */ - virtual pvAccessID preallocateChannelSID(); - - /** - * De-preallocate new channel SID. - * @param sid preallocated channel SID. - */ - virtual void depreallocateChannelSID(pvAccessID /*sid*/) { - // noop - } - - /** - * Register a new channel. - * @param sid preallocated channel SID. - * @param channel channel to register. - */ - virtual void registerChannel(pvAccessID sid, ServerChannel::shared_pointer const & channel); - - /** - * Unregister a new channel (and deallocates its handle). - * @param sid SID - */ - virtual void unregisterChannel(pvAccessID sid); - - /** - * Get channel by its SID. - * @param sid channel SID - * @return channel with given SID, NULL otherwise - */ - virtual ServerChannel::shared_pointer getChannel(pvAccessID sid); - - /** - * Get channel count. - * @return channel count. - */ - virtual int getChannelCount(); - - virtual epics::pvData::PVField::shared_pointer getSecurityToken() { - return epics::pvData::PVField::shared_pointer(); - } - - virtual void lock() { - // noop - } - - virtual void unlock() { - // noop - } - - virtual void acquire() { - // noop, since does not make sence on itself - } - - virtual void release() { - // noop, since does not make sence on itself - } - - /** - * Verify transport. Server side is self-verified. - */ - virtual bool verify(epics::pvData::int32 /*timeoutMs*/) { - TransportSender::shared_pointer transportSender = std::tr1::dynamic_pointer_cast(shared_from_this()); - enqueueSendRequest(transportSender); - verified(); - return true; - } - - /** - * PVA connection validation request. - * A server sends a validate connection message when it receives a new connection. - * The message indicates that the server is ready to receive requests; the client must - * not send any messages on the connection until it has received the validate connection message - * from the server. No reply to the message is expected by the server. - * The purpose of the validate connection message is two-fold: - * It informs the client of the protocol version supported by the server. - * It prevents the client from writing a request message to its local transport - * buffers until after the server has acknowledged that it can actually process the - * request. This avoids a race condition caused by the server's TCP/IP stack - * accepting connections in its backlog while the server is in the process of shutting down: - * if the client were to send a request in this situation, the request - * would be lost but the client could not safely re-issue the request because that - * might violate at-most-once semantics. - * The validate connection message guarantees that a server is not in the middle - * of shutting down when the server's TCP/IP stack accepts an incoming connection - * and so avoids the race condition. - * @see org.epics.ca.impl.remote.TransportSender#send(java.nio.ByteBuffer, org.epics.ca.impl.remote.TransportSendControl) - */ - virtual void send(epics::pvData::ByteBuffer* buffer, - TransportSendControl* control); - - virtual ~BlockingServerTCPTransport(); - - protected: - - virtual void internalClose(bool force); - virtual void internalPostClose(bool force); - - private: - /** - * Last SID cache. - */ - pvAccessID _lastChannelSID; - - /** - * Channel table (SID -> channel mapping). - */ - std::map _channels; - - epics::pvData::Mutex _channelsMutex; - - /** - * Destroy all channels. - */ - void destroyAllChannels(); - }; - class ResponseHandlerFactory { public: diff --git a/pvAccessApp/remote/blockingTCPAcceptor.cpp b/pvAccessApp/remote/blockingTCPAcceptor.cpp index 0f7b381..bd99b9a 100644 --- a/pvAccessApp/remote/blockingTCPAcceptor.cpp +++ b/pvAccessApp/remote/blockingTCPAcceptor.cpp @@ -191,15 +191,12 @@ namespace pvAccess { LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer); } - /** * Create transport, it registers itself to the registry. - * Each transport should have its own response handler since it is not "shareable" */ - // TODO it is shareable?!!! but code is not adopted to it... std::auto_ptr responseHandler = _responseHandlerFactory->createResponseHandler(); - BlockingServerTCPTransportCodec::shared_pointer transport = - BlockingServerTCPTransportCodec::create( + detail::BlockingServerTCPTransportCodec::shared_pointer transport = + detail::BlockingServerTCPTransportCodec::create( _context, newClient, responseHandler, diff --git a/pvAccessApp/remote/blockingTCPConnector.cpp b/pvAccessApp/remote/blockingTCPConnector.cpp index 1871e9d..2e12404 100644 --- a/pvAccessApp/remote/blockingTCPConnector.cpp +++ b/pvAccessApp/remote/blockingTCPConnector.cpp @@ -150,7 +150,7 @@ namespace epics { LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer); } - transport = BlockingClientTCPTransportCodec::create( + transport = detail::BlockingClientTCPTransportCodec::create( context, socket, responseHandler, _receiveBufferSize, _socketSendBufferSize, client, transportRevision, _beaconInterval, priority); diff --git a/pvAccessApp/remote/blockingTCPTransport.cpp b/pvAccessApp/remote/blockingTCPTransport.cpp deleted file mode 100644 index a99c655..0000000 --- a/pvAccessApp/remote/blockingTCPTransport.cpp +++ /dev/null @@ -1,1342 +0,0 @@ -/** - * 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. - */ - -#ifdef _WIN32 -#define NOMINMAX -#include -typedef SSIZE_T ssize_t; -#endif - -#define __STDC_LIMIT_MACROS 1 -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -using namespace epics::pvData; - -using std::max; -using std::min; -using std::ostringstream; - -// TODO to be completely replaced by codec based implementation (see Java) -namespace epics { -namespace pvAccess { - - /* - class MonitorSender : public TransportSender, public NoDefaultMethods { - public: - MonitorSender(Mutex* monitorMutex, GrowingCircularBuffer< - TransportSender*>* monitorSendQueue) : - _monitorMutex(monitorMutex), - _monitorSendQueue(monitorSendQueue) { - } - - virtual ~MonitorSender() { - } - - virtual void lock() { - } - - virtual void unlock() { - } - - virtual void acquire() { - } - - virtual void release() { - } - - virtual void - send(ByteBuffer* buffer, TransportSendControl* control); - - private: - Mutex* _monitorMutex; - GrowingCircularBuffer* _monitorSendQueue; - }; - */ - - PVACCESS_REFCOUNT_MONITOR_DEFINE(blockingTCPTransport); - - //const double BlockingTCPTransport::_delay = 0.000; - - BlockingTCPTransport::BlockingTCPTransport(Context::shared_pointer const & context, - SOCKET channel, std::auto_ptr& responseHandler, - int receiveBufferSize, int16 priority) : - _delay(0.0), - _channel(channel), - _priority(priority), - _responseHandler(responseHandler), -#if FLOW_CONTROL - _markerPeriodBytes(MARKER_PERIOD), -#endif - _flushStrategy(DELAYED), - _rcvThreadId(0), - _sendThreadId(0), - //_monitorSender(new MonitorSender(&_monitorMutex,_monitorSendQueue)), - _context(context), - _autoDelete(true), - _remoteTransportRevision(0), - _remoteTransportReceiveBufferSize(MAX_TCP_RECV), - _remoteTransportSocketReceiveBufferSize(MAX_TCP_RECV), - _sendQueue(), - //_monitorSendQueue(), -#if FLOW_CONTROL - _nextMarkerPosition(_markerPeriodBytes), -#endif - _sendPending(false), - _lastMessageStartPosition(0), - _lastSegmentedMessageType(0), - _lastSegmentedMessageCommand(0), - _flushRequested(false), - _sendBufferSentPosition(0), - _byteOrderFlag((EPICS_BYTE_ORDER == EPICS_ENDIAN_BIG) ? 0x80 : 0x00), - _storedPayloadSize(0), - _storedPosition(0), - _storedLimit(0), - _version(0), - _packetType(0), - _command(0), - _payloadSize(0), - _stage(READ_FROM_SOCKET), - _directPayloadRead(0), - _directBuffer(0), -#if FLOW_CONTROL - _totalBytesReceived(0), -#endif - _closed(), - _sendThreadExited(false), - _verified(false) -#if FLOW_CONTROL - , - _markerToSend(0), - _totalBytesSent(0), - _remoteBufferFreeSpace(INT64_MAX) -#endif - { - PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(blockingTCPTransport); - - // TODO minor tweak: deque size is not preallocated... - - unsigned int bufferSize = max((int)(MAX_TCP_RECV+MAX_ENSURE_DATA_BUFFER_SIZE), receiveBufferSize); - // size must be "aligned" - bufferSize = (bufferSize + (PVA_ALIGNMENT - 1)) & (~(PVA_ALIGNMENT - 1)); - - _socketBuffer = new ByteBuffer(bufferSize); - _socketBuffer->setPosition(_socketBuffer->getLimit()); - _startPosition = _socketBuffer->getPosition(); - - // allocate buffer - _sendBuffer = new ByteBuffer(bufferSize); - _maxPayloadSize = _sendBuffer->getSize() - 2*PVA_MESSAGE_HEADER_SIZE; // one for header, one for flow control - - // get TCP send buffer size - osiSocklen_t intLen = sizeof(int); - int retval = getsockopt(_channel, SOL_SOCKET, SO_SNDBUF, (char *)&_socketSendBufferSize, &intLen); - if(unlikely(retval<0)) { - _socketSendBufferSize = MAX_TCP_RECV; - char errStr[64]; - epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); - LOG(logLevelDebug, - "Unable to retrieve socket send buffer size: %s", - errStr); - } - - // get remote address - osiSocklen_t saSize = sizeof(sockaddr); - retval = getpeername(_channel, &(_socketAddress.sa), &saSize); - if(unlikely(retval<0)) { - char errStr[64]; - epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); - LOG(logLevelError, - "Error fetching socket remote address: %s", - errStr); - } - - // set receive timeout so that we do not have problems at shutdown (recvfrom would block) - struct timeval timeout; - memset(&timeout, 0, sizeof(struct timeval)); - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - if (unlikely(::setsockopt (_channel, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)) < 0)) - { - char errStr[64]; - epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); - LOG(logLevelError, - "Failed to set SO_RCVTIMEO for TDP socket %s: %s.", - inetAddressToString(_socketAddress).c_str(), errStr); - } - - // TODO this will create marker with invalid endian flag - // prepare buffer - clearAndReleaseBuffer(); - } - - BlockingTCPTransport::~BlockingTCPTransport() { - PVACCESS_REFCOUNT_MONITOR_DESTRUCT(blockingTCPTransport); - - close(); - - // TODO use auto_ptr class members - - delete _socketBuffer; - delete _sendBuffer; - } - - // TODO consider epics::pvData::Thread - void BlockingTCPTransport::start() { - - // TODO this was in constructor - // add to registry - Transport::shared_pointer thisSharedPtr = shared_from_this(); - _context->getTransportRegistry()->put(thisSharedPtr); - - - String socketAddressString = inetAddressToString(_socketAddress); - - // - // start receive thread - // - - String threadName = "TCP-receive " + socketAddressString; - LOG(logLevelDebug, "Starting thread: %s", threadName.c_str()); - - _rcvThreadId = epicsThreadCreate(threadName.c_str(), - epicsThreadPriorityMedium, - epicsThreadGetStackSize(epicsThreadStackBig), - BlockingTCPTransport::rcvThreadRunner, this); - - // - // start send thread - // - - threadName = "TCP-send " + socketAddressString; - LOG(logLevelDebug, "Starting thread: %s",threadName.c_str()); - - _sendThreadId = epicsThreadCreate(threadName.c_str(), - epicsThreadPriorityMedium, - epicsThreadGetStackSize(epicsThreadStackSmall), - BlockingTCPTransport::sendThreadRunner, this); - } - - void BlockingTCPTransport::clearAndReleaseBuffer() { -#if FLOW_CONTROL - // NOTE: take care that nextMarkerPosition is set right - // fix position to be correct when buffer is cleared - // do not include pre-buffered flow control message; not 100% correct, but OK - _nextMarkerPosition -= _sendBuffer->getPosition() - PVA_MESSAGE_HEADER_SIZE; -#endif - - _sendQueueMutex.lock(); - _flushRequested = false; - _sendQueueMutex.unlock(); - - _sendBuffer->clear(); - - _sendPending = false; - -#if FLOW_CONTROL - // prepare ACK marker - _sendBuffer->putByte(PVA_MAGIC); - _sendBuffer->putByte(PVA_VERSION); - _sendBuffer->putByte(0x01 | _byteOrderFlag); // control data - _sendBuffer->putByte(1); // marker ACK - _sendBuffer->putInt(0); -#endif - } - - void BlockingTCPTransport::close() { - Lock lock(_mutex); - - // already closed check - if(_closed.get()) return; - _closed.set(); - - // remove from registry - Transport::shared_pointer thisSharedPtr = shared_from_this(); - _context->getTransportRegistry()->remove(thisSharedPtr).get(); - - // TODO !!! - bool force = true; - - // clean resources - internalClose(force); - - // notify send queue - _sendQueueEvent.signal(); - - lock.unlock(); - - // post close without a lock - internalPostClose(force); - } - - void BlockingTCPTransport::internalClose(bool /*force*/) { - // close the socket - if(_channel!=INVALID_SOCKET) { - epicsSocketDestroy(_channel); - } - } - - void BlockingTCPTransport::internalPostClose(bool /*force*/) { - } - - size_t BlockingTCPTransport::getSocketReceiveBufferSize() const { - // Get value of the SO_RCVBUF option for this DatagramSocket, - // that is the buffer size used by the platform for input on - // this DatagramSocket. - - int sockBufSize; - osiSocklen_t intLen = sizeof(int); - - int retval = getsockopt(_channel, SOL_SOCKET, SO_RCVBUF, (char *)&sockBufSize, &intLen); - if(unlikely(retval<0)) - { - char errStr[64]; - epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); - LOG(logLevelError, - "Socket getsockopt SO_RCVBUF error: %s", - errStr); - } - - return (size_t)sockBufSize; - } - - void BlockingTCPTransport::flush(bool lastMessageCompleted) { - - // automatic end - endMessage(!lastMessageCompleted); - - bool moreToSend = true; - while(moreToSend) { - moreToSend = !flush(); - - // all sent, exit - if(!moreToSend) break; - // TODO check if this is OK - else if (_closed.get()) THROW_BASE_EXCEPTION("transport closed"); - - // TODO solve this sleep in a better way - epicsThreadSleep(0.01); - } - - _lastMessageStartPosition = _sendBuffer->getPosition(); - - // start with last header - if (unlikely(!lastMessageCompleted && _lastSegmentedMessageType!=0)) - startMessage(_lastSegmentedMessageCommand, 0); - } - - void BlockingTCPTransport::startMessage(int8 command, size_t ensureCapacity) { - _lastMessageStartPosition = -1; - ensureBuffer(PVA_MESSAGE_HEADER_SIZE+ensureCapacity); - _lastMessageStartPosition = _sendBuffer->getPosition(); - _sendBuffer->putByte(PVA_MAGIC); - _sendBuffer->putByte(PVA_VERSION); - _sendBuffer->putByte(_lastSegmentedMessageType | _byteOrderFlag); // data + endianess - _sendBuffer->putByte(command); // command - _sendBuffer->putInt(0); // temporary zero payload - - } - - void BlockingTCPTransport::endMessage() { - endMessage(false); - } - - void BlockingTCPTransport::ensureBuffer(size_t size) { - if(likely(_sendBuffer->getRemaining()>=size)) return; - - // too large for buffer... - if(unlikely(_maxPayloadSizegetRemaining()getRemaining()<(alignment-1))) - ensureBuffer(alignment-1); - - _sendBuffer->align(alignment); - } - - void BlockingTCPTransport::endMessage(bool hasMoreSegments) { - if(likely(_lastMessageStartPosition>=0)) { - - // align - // alignBuffer(PVA_ALIGNMENT); - - // set paylaod size - const size_t payloadSize = _sendBuffer->getPosition()-_lastMessageStartPosition-PVA_MESSAGE_HEADER_SIZE; - - // TODO by spec? - // ignore empty segmented messages - if (payloadSize == 0 && _lastSegmentedMessageType != 0) - { - _sendBuffer->setPosition(_lastMessageStartPosition); - if (!hasMoreSegments) - _lastSegmentedMessageType = 0; - return; - - } - - _sendBuffer->putInt(_lastMessageStartPosition+sizeof(int16)+2, (int32)payloadSize); - - int flagsPosition = _lastMessageStartPosition+sizeof(int16); - // set segmented bit - if(likely(hasMoreSegments)) { - // first segment - if(unlikely(_lastSegmentedMessageType==0)) { - int8 type = _sendBuffer->getByte(flagsPosition); - - // set first segment bit - _sendBuffer->putByte(flagsPosition, (int8)(type|0x10)); - - // first + last segment bit == in-between segment - _lastSegmentedMessageType = (int8)(type|0x30); - _lastSegmentedMessageCommand = _sendBuffer->getByte( - flagsPosition+1); - } - } - else { - // last segment - if(unlikely(_lastSegmentedMessageType!=0)) { - // set last segment bit (by clearing first segment bit) - _sendBuffer->putByte(flagsPosition, - (int8)(_lastSegmentedMessageType&0xEF)); - _lastSegmentedMessageType = 0; - } - } - -#if FLOW_CONTROL - // manage markers - int position = _sendBuffer->getPosition(); - int bytesLeft = _sendBuffer->getRemaining(); - - if(unlikely(position>=_nextMarkerPosition && - bytesLeft>=PVA_MESSAGE_HEADER_SIZE)) { - _sendBuffer->putByte(PVA_MAGIC); - _sendBuffer->putByte(PVA_VERSION); - _sendBuffer->putByte(0x01 | _byteOrderFlag); // control data - _sendBuffer->putByte(0); // marker - s_sendBuffer->putInt((int)(_totalBytesSent+position+PVA_MESSAGE_HEADER_SIZE)); - _nextMarkerPosition = position+_markerPeriodBytes; - } -#endif - } - } - - void BlockingTCPTransport::ensureData(size_t size) { - // enough of data? - const size_t remainingBytes = _socketBuffer->getRemaining(); - if (likely(remainingBytes>=size)) return; - - // too large for buffer... - if (unlikely(MAX_ENSURE_DATA_BUFFER_SIZEgetPosition()-_storedPosition; - - // no more data and we have some payload left => read buffer - if (likely(_storedPayloadSize>=size)) - { - //LOG(logLevelInfo, - // "storedPayloadSize >= size, remaining: %d", - // _socketBuffer->getRemaining()); - - // just read up remaining payload, move current (getPosition(); - _storedLimit = _socketBuffer->getLimit(); - _socketBuffer->setLimit(min(_storedPosition+_storedPayloadSize, - _storedLimit)); - } - else - { - // copy remaining bytes to safe region of buffer, if any - for(size_t i = 0; iputByte(i, _socketBuffer->getByte()); - - // extend limit to what was read - _socketBuffer->setLimit(_storedLimit); - - _stage = PROCESS_HEADER; - processReadCached(true, UNDEFINED_STAGE, size-remainingBytes); - - if (unlikely(remainingBytes > 0)) - { - // copy saved back to before position - for(int i = static_cast(remainingBytes-1), j = _socketBuffer->getPosition()-1; - i>=0; - i--, j--) - _socketBuffer->putByte(j, _socketBuffer->getByte(i)); - _startPosition = _socketBuffer->getPosition()-remainingBytes; - _socketBuffer->setPosition(_startPosition); - _storedPosition = _startPosition; - } - else - { - _storedPosition = _socketBuffer->getPosition(); - } - - _storedLimit = _socketBuffer->getLimit(); - _socketBuffer->setLimit(min(_storedPosition+_storedPayloadSize, - _storedLimit)); - - // add if missing, since UNDEFINED_STAGE and return less... - if(unlikely(!_closed.get()&&(_socketBuffer->getRemaining()getRemaining()<(alignment-1))) - ensureData(alignment-1); - - _socketBuffer->align(alignment); - } - - bool BlockingTCPTransport::directSerialize(ByteBuffer* /*existingBuffer*/, const char* toSerialize, - std::size_t elementCount, std::size_t elementSize) - { - // TODO overflow check, size_t type, other is int32 for payloadSize header field !!! - // TODO do not ignore or new field in 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 < 1024) - return false; - - // first end current message indicating the we will segment - endMessage(true); - - // append segmented message header - startMessage(_lastSegmentedMessageCommand, 0); - // set segmented message size - _sendBuffer->putInt(_lastMessageStartPosition+sizeof(int16)+2, (int32)count); - - // flush (TODO this is code is duplicated) - bool moreToSend = true; - while (moreToSend) { - moreToSend = !flush(); - - // all sent, exit - if(!moreToSend) break; - // TODO check if this is OK - else if (_closed.get()) THROW_BASE_EXCEPTION("transport closed"); - - // TODO solve this sleep in a better way - epicsThreadSleep(0.01); - } - _lastMessageStartPosition = _sendBuffer->getPosition(); - - // TODO think if alignment is preserved after... - - try { - //LOG(logLevelInfo, - // "Sending (direct) %d bytes in the packet to %s.", - // count, - // inetAddressToString(_socketAddress).c_str()); - const char* ptr = toSerialize; - while(count>0) { - ssize_t bytesSent = ::send(_channel, - ptr, - count, 0); - - if(unlikely(bytesSent<0)) { - - int socketError = SOCKERRNO; - - // spurious EINTR check - if (socketError==SOCK_EINTR) - continue; - - // TODO check this (copy below)... consolidate!!! - if (socketError==SOCK_ENOBUFS) { - // TODO improve this - epicsThreadSleep(0.01); - continue; - } - - // connection lost - - char errStr[64]; - epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); - ostringstream temp; - temp<<"error in sending TCP data: "<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 BlockingTCPTransport::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; - - } - } - - void BlockingTCPTransport::processReadCached(bool nestedCall, - ReceiveStage inStage, size_t requiredBytes) { - try { - // TODO we need to throw exception in nextedCall not just bail out!!!! - while(likely(!_closed.get())) { - if(_stage==READ_FROM_SOCKET||inStage!=UNDEFINED_STAGE) { - - // add to bytes read -#if FLOW_CONTROL - int currentPosition = _socketBuffer->getPosition(); - _totalBytesReceived += (currentPosition - _startPosition); -#endif - // preserve alignment - int currentStartPosition = _startPosition = - MAX_ENSURE_DATA_BUFFER_SIZE; // "TODO uncomment align" + (unsigned int)currentPosition % PVA_ALIGNMENT; - - // copy remaining bytes, if any - int remainingBytes = _socketBuffer->getRemaining(); - - int endPosition = currentStartPosition + remainingBytes; - // TODO memmove - for(int i = MAX_ENSURE_DATA_BUFFER_SIZE; iputByte(i, _socketBuffer->getByte()); - - _socketBuffer->setPosition(endPosition); - _socketBuffer->setLimit(_socketBuffer->getSize()); - - // read at least requiredBytes bytes - - size_t requiredPosition = (currentStartPosition+requiredBytes); - while(_socketBuffer->getPosition()getPosition(); - - ssize_t bytesRead = recv(_channel, (char*)(_socketBuffer->getArray()+pos), -// _socketBuffer->getRemaining(), 0); -// TODO we assume that caller is smart and requiredBytes > remainingBytes -// if in direct read mode, try to read only header so that rest can be read directly to direct buffers -(_directPayloadRead > 0 && inStage == PROCESS_HEADER) ? (requiredBytes-remainingBytes) : _socketBuffer->getRemaining(), 0); -//std::cout << "i: " << 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(); - - if(nestedCall) - THROW_BASE_EXCEPTION("bytesRead < 0"); - - return; - } - - _socketBuffer->setPosition(pos+bytesRead); - } - - std::size_t pos = _socketBuffer->getPosition(); - _storedLimit = pos; - _socketBuffer->setLimit(pos); - _socketBuffer->setPosition(currentStartPosition); - - /* - hexDump("\n\n\n", "READ", - (const int8*)_socketBuffer->getArray(), - _socketBuffer->getPosition(), _socketBuffer->getRemaining()); - */ - - // notify liveness - aliveNotification(); - - // exit - if(inStage!=UNDEFINED_STAGE) return; - - _stage = PROCESS_HEADER; - } - - if(likely(_stage==PROCESS_HEADER)) { - - // reveal what's already in buffer - _socketBuffer->setLimit(_storedLimit); - - // ensure PVAConstants.PVA_MESSAGE_HEADER_SIZE bytes of data - if(unlikely(((int)_socketBuffer->getRemaining())getByte(); - _version = _socketBuffer->getByte(); - if(unlikely(magic != PVA_MAGIC)) - { - // error... disconnect - LOG( - logLevelError, - "Invalid header received from client %s, disconnecting...", - inetAddressToString(_socketAddress).c_str()); - close(); - return; - } - - // data vs. control packet - _packetType = _socketBuffer->getByte(); - - // command - _command = _socketBuffer->getByte(); - - // read payload size - _payloadSize = _socketBuffer->getInt(); - - int8 type = (int8)(_packetType&0x0F); - if(likely(type==0)) - { - // data - _stage = PROCESS_PAYLOAD; - } - else if(unlikely(type==1)) - { - // control - // marker request sent - if (_command == CMD_SET_MARKER) { -#if FLOW_CONTROL - _flowControlMutex.lock(); - if(_markerToSend==0) - _markerToSend = _payloadSize; - // TODO send back response - _flowControlMutex.unlock(); -#endif - } - - // marker received back - else if (_command == CMD_ACK_MARKER) - { -#if FLOW_CONTROL - _flowControlMutex.lock(); - int difference = (int)_totalBytesSent-_payloadSize+PVA_MESSAGE_HEADER_SIZE; - // overrun check - if(difference<0) difference += INT_MAX; - _remoteBufferFreeSpace - = _remoteTransportReceiveBufferSize - +_remoteTransportSocketReceiveBufferSize - -difference; - // TODO if this is calculated wrong, this can be critical !!! - _flowControlMutex.unlock(); -#endif - } - // set byte order - else if (_command == CMD_SET_ENDIANESS) - { - // check 7-th bit - - int endianess = (_packetType < 0 ? EPICS_ENDIAN_BIG : EPICS_ENDIAN_LITTLE); - _socketBuffer->setEndianess(endianess); - - // TODO register as TransportSender and add to the queue - // current implementation is OK, but not nice - _sendQueueMutex.lock(); - _sendBuffer->setEndianess(endianess); - _byteOrderFlag = (endianess == EPICS_ENDIAN_BIG) ? 0x80 : 0x00; - _sendQueueMutex.unlock(); - } - - // no payload - //stage = ReceiveStage.PROCESS_HEADER; - continue; - } - else { - LOG( - logLevelError, - "Unknown packet type %d, received from client %s, disconnecting...", - type, - inetAddressToString(_socketAddress).c_str()); - close(); - return; - } - } - - if(likely(_stage==PROCESS_PAYLOAD)) { - // read header - - // last segment bit set (means in-between segment or last segment) - bool notFirstSegment = (_packetType&0x20)!=0; - - _storedPayloadSize = _payloadSize; - - // if segmented, exit reading code - if(nestedCall&¬FirstSegment) return; - - // ignore segmented messages with no payload - if (likely(!notFirstSegment || _payloadSize > 0)) - { - - // NOTE: nested data (w/ payload) messages between segmented messages are not supported - _storedPosition = _socketBuffer->getPosition(); - _storedLimit = _socketBuffer->getLimit(); - _socketBuffer->setLimit(min(_storedPosition+_storedPayloadSize, _storedLimit)); - try { - // handle response - Transport::shared_pointer thisPointer = shared_from_this(); - _responseHandler->handleResponse(&_socketAddress, - thisPointer, _version, _command, _payloadSize, - _socketBuffer); - } catch (std::exception& ex) { - - LOG( - logLevelDebug, - "Unexpected exception in responseHandler: %s", - ex.what() - ); - - } catch(...) { - - LOG( - logLevelDebug, - "Unexpected exception in responseHandler!" - ); - - } - - _socketBuffer->setLimit(_storedLimit); - size_t newPosition = _storedPosition+_storedPayloadSize; - if(unlikely(newPosition>_storedLimit)) { - newPosition -= _storedLimit; - _socketBuffer->setPosition(_storedLimit); - processReadCached(true, PROCESS_PAYLOAD,newPosition); - newPosition += _startPosition; - } - _socketBuffer->setPosition(newPosition); - // TODO discard all possible segments?!!! - - } - - _stage = PROCESS_HEADER; - - continue; - } - - } - } catch(...) { - // close connection - close(); - - if(nestedCall) throw; - } - } - - bool BlockingTCPTransport::flush() { - // request issues, has not sent anything yet (per partes) - if(likely(!_sendPending)) { - _sendPending = true; - - // start sending from the start - _sendBufferSentPosition = 0; - -#if FLOW_CONTROL - // if not set skip marker otherwise set it - _flowControlMutex.lock(); - int markerValue = _markerToSend; - _markerToSend = 0; - _flowControlMutex.unlock(); - if(markerValue==0) - _sendBufferSentPosition = PVA_MESSAGE_HEADER_SIZE; - else - _sendBuffer->putInt(4, markerValue); -#endif - } - - bool success = false; - try { - // remember current position - int currentPos = _sendBuffer->getPosition(); - - // set to send position - _sendBuffer->setPosition(_sendBufferSentPosition); - _sendBuffer->setLimit(currentPos); - - success = send(_sendBuffer); - - // all sent? - if(likely(success)) - clearAndReleaseBuffer(); - else { - // remember position - _sendBufferSentPosition = _sendBuffer->getPosition(); - - // .. reset to previous state - _sendBuffer->setPosition(currentPos); - _sendBuffer->setLimit(_sendBuffer->getSize()); - } - //} catch(std::exception& e) { - // LOG(logLevelError, "%s", e.what()); - // // error, release lock - // clearAndReleaseBuffer(); - } catch(...) { - clearAndReleaseBuffer(); - } - return success; - } - - bool BlockingTCPTransport::send(ByteBuffer* buffer) { - try { - // TODO simply use value from marker???!!! - // On Windows, limiting the buffer size is important to prevent - // poor throughput performances when transferring large amount of - // data. See Microsoft KB article KB823764. - // We do it also for other systems just to be safe. - int maxBytesToSend = std::min(_socketSendBufferSize, - static_cast(_remoteTransportSocketReceiveBufferSize))/2; - - int limit = buffer->getLimit(); - int bytesToSend = limit-buffer->getPosition(); - - //LOG(logLevelInfo,"Total bytes to send: %d", bytesToSend); - //printf("Total bytes to send: %d\n", bytesToSend); - - // limit sending - if(bytesToSend>maxBytesToSend) { - bytesToSend = maxBytesToSend; - buffer->setLimit(buffer->getPosition()+bytesToSend); - } - - //LOG(logLevelInfo, - // "Sending %d of total %d bytes in the packet to %s.", - // bytesToSend, limit, - // inetAddressToString(_socketAddress).c_str()); - - while(buffer->getRemaining()>0) { - ssize_t bytesSent = ::send(_channel, - &buffer->getArray()[buffer->getPosition()], - buffer->getRemaining(), 0); - - if(unlikely(bytesSent<0)) { - - int socketError = SOCKERRNO; - - // spurious EINTR check - if (socketError==SOCK_EINTR) - continue; - - // TODO check this (copy below)... consolidate!!! - if (socketError==SOCK_ENOBUFS) { - /* buffers full, reset the limit and indicate that there - * is more data to be sent - */ - if(bytesSent==maxBytesToSend) buffer->setLimit(limit); - return false; - } - - // connection lost - - char errStr[64]; - epicsSocketConvertErrnoToString(errStr, sizeof(errStr)); - ostringstream temp; - temp<<"error in sending TCP data: "<getPosition(), limit); - - /* buffers full, reset the limit and indicate that there - * is more data to be sent - */ - if(bytesSent==maxBytesToSend) buffer->setLimit(limit); - - //LOG(logLevelInfo, - // "Send buffer full for %s, waiting...", - // inetAddressToString(_socketAddress)); - return false; - } - - buffer->setPosition(buffer->getPosition()+bytesSent); - -#if FLOW_CONTROL - _flowControlMutex.lock(); - _totalBytesSent += bytesSent; - _flowControlMutex.unlock(); -#endif - - // readjust limit - if(bytesToSend==maxBytesToSend) { - bytesToSend = limit-buffer->getPosition(); - if(bytesToSend>maxBytesToSend) bytesToSend - = maxBytesToSend; - buffer->setLimit(buffer->getPosition()+bytesToSend); - } - - //LOG(logLevelInfo, - // "Sent, position %d of total %d bytes.", - // buffer->getPosition(), limit); - } // while - } catch(...) { - close(); - throw; - } - - // all sent - return true; - } - - void BlockingTCPTransport::processSendQueue() { - while(unlikely(!_closed.get())) { - - _sendQueueMutex.lock(); - // TODO optimize - TransportSender::shared_pointer sender; - if (likely(!_sendQueue.empty())) - { - sender = _sendQueue.front(); - _sendQueue.pop_front(); - } - _sendQueueMutex.unlock(); - - // wait for new message - while(likely(sender.get()==0&&!_flushRequested&&!_closed.get())) { - if(_flushStrategy==DELAYED) { - if(_delay>0) epicsThreadSleep(_delay); - if(unlikely(_sendQueue.empty())) { - // if (hasMonitors || sendBuffer.position() > PVAConstants.PVA_MESSAGE_HEADER_SIZE) -#if FLOW_CONTROL - if(((int)_sendBuffer->getPosition())>PVA_MESSAGE_HEADER_SIZE) -#else - if(((int)_sendBuffer->getPosition())>0) -#endif - _flushRequested = true; - else - _sendQueueEvent.wait(); - } - } - else - _sendQueueEvent.wait(); - - _sendQueueMutex.lock(); - if (likely(!_sendQueue.empty())) - { - sender = _sendQueue.front(); - _sendQueue.pop_front(); - } - else - sender.reset(); - _sendQueueMutex.unlock(); - } - - // always do flush from this thread - if(unlikely(_flushRequested)) { - /* - if (hasMonitors) - { - monitorSender.send(sendBuffer, this); - } - */ - - flush(); - } - - if(likely(sender.get() != 0)) { - sender->lock(); - try { - _lastMessageStartPosition = _sendBuffer->getPosition(); - sender->send(_sendBuffer, this); - - if(_flushStrategy==IMMEDIATE) - flush(true); - else - endMessage(false);// automatic end (to set payload) - } catch(std::exception &) { - //LOG(logLevelError, "%s", e.what()); - _sendBuffer->setPosition(_lastMessageStartPosition); - } catch(...) { - _sendBuffer->setPosition(_lastMessageStartPosition); - } - sender->unlock(); - } // if(sender!=NULL) - } // while(!_closed.get()) - } - - void BlockingTCPTransport::freeSendBuffers() { - // TODO ? - } - - void BlockingTCPTransport::freeConnectionResorces() { - freeSendBuffers(); - - LOG(logLevelDebug, "Connection to %s closed.", - inetAddressToString(_socketAddress).c_str()); -/* - if(_channel!=INVALID_SOCKET) { - epicsSocketDestroy(_channel); - _channel = INVALID_SOCKET; - } -*/ - } - - void BlockingTCPTransport::rcvThreadRunner(void* param) { - BlockingTCPTransport* obj = (BlockingTCPTransport*)param; - Transport::shared_pointer ptr = obj->shared_from_this(); // hold reference - -try{ - obj->processReadCached(false, UNDEFINED_STAGE, PVA_MESSAGE_HEADER_SIZE); -} catch (...) { -printf("rcvThreadRunnner exception\n"); -} - - /* - if(obj->_autoDelete) { - while(true) - { - bool exited; - obj->_mutex.lock(); - exited = obj->_sendThreadExited; - obj->_mutex.unlock(); - if (exited) - break; - epicsThreadSleep(0.1); - } - delete obj; - } - */ - } - - void BlockingTCPTransport::sendThreadRunner(void* param) { - BlockingTCPTransport* obj = (BlockingTCPTransport*)param; - Transport::shared_pointer ptr = obj->shared_from_this(); // hold reference -try { - obj->processSendQueue(); -} catch (std::exception& ex) { - printf("sendThreadRunnner exception %s\n", ex.what()); // TODO -} catch (...) { -printf("sendThreadRunnner exception\n"); -} - - obj->freeConnectionResorces(); - - // TODO possible crash on unlock - obj->_mutex.lock(); - obj->_sendThreadExited = true; - obj->_mutex.unlock(); - } - - void BlockingTCPTransport::enqueueSendRequest(TransportSender::shared_pointer const & sender) { - Lock lock(_sendQueueMutex); - if(unlikely(_closed.get())) return; - _sendQueue.push_back(sender); - _sendQueueEvent.signal(); - } - - void BlockingTCPTransport::enqueueOnlySendRequest(TransportSender::shared_pointer const & sender) { - Lock lock(_sendQueueMutex); - if(unlikely(_closed.get())) return; - _sendQueue.push_back(sender); - } - - class FlushTransportSender : public TransportSender { - public: - virtual void send(epics::pvData::ByteBuffer*, TransportSendControl* control) - { - control->flush(true); - } - - virtual void lock() {} - virtual void unlock() {} - }; - - static TransportSender::shared_pointer flushTransportSender(new FlushTransportSender()); - - void BlockingTCPTransport::flushSendQueue() { - enqueueSendRequest(flushTransportSender); - } - - /* - void BlockingTCPTransport::enqueueMonitorSendRequest(TransportSender::shared_pointer sender) { - Lock lock(_monitorMutex); - if(unlikely(_closed.get())) return; - _monitorSendQueue.insert(sender); - if(_monitorSendQueue.size()==1) enqueueSendRequest(_monitorSender); - } - - - void MonitorSender::send(ByteBuffer* buffer, TransportSendControl* control) { - control->startMessage(19, 0); - - while(true) { - TransportSender* sender; - _monitorMutex->lock(); - if(_monitorSendQueue->size()>0) - sender = _monitorSendQueue->extract(); - else - sender = NULL; - _monitorMutex->unlock(); - - if(sender==NULL) { - control->ensureBuffer(sizeof(int32)); - buffer->putInt(INVALID_IOID); - break; - } - sender->send(buffer, control); - sender->release(); - } - } -*/ - } -} diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 8a95a0f..df3bb2c 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -21,7 +21,6 @@ #include #include - #include using namespace epics::pvData; @@ -30,6 +29,7 @@ using namespace epics::pvAccess; namespace epics { namespace pvAccess { + namespace detail { const std::size_t AbstractCodec::MAX_MESSAGE_PROCESS = 100; const std::size_t AbstractCodec::MAX_MESSAGE_SEND = 100; @@ -91,16 +91,10 @@ namespace epics { _sendBuffer->getSize() - 2*PVA_MESSAGE_HEADER_SIZE; _socketSendBufferSize = socketSendBufferSize; _blockingProcessQueue = blockingProcessQueue; - LOG(logLevelTrace, "AbstractCodec constructed (threadId: %u)", - epicsThreadGetIdSelf()); } void AbstractCodec::processRead() { - - LOG(logLevelTrace, "AbstractCodec::processRead: enter (threadId: %u)", - epicsThreadGetIdSelf()); - switch (_readMode) { case NORMAL: @@ -118,10 +112,6 @@ namespace epics { void AbstractCodec::processHeader() { - LOG(logLevelTrace, "AbstractCodec::processHeader enter (threadId: %u)", - epicsThreadGetIdSelf()); - - // magic code int8_t magicCode = _socketBuffer->getByte(); @@ -153,10 +143,6 @@ namespace epics { void AbstractCodec::processReadNormal() { - LOG(logLevelTrace, - "AbstractCodec::processReadNormal enter (threadId: %u)", - epicsThreadGetIdSelf()); - try { std::size_t messageProcessCount = 0; @@ -286,10 +272,6 @@ namespace epics { void AbstractCodec::processReadSegmented() { - LOG(logLevelTrace, - "AbstractCodec::processReadSegmented enter (threadId: %u)", - epicsThreadGetIdSelf()); - while (true) { // read as much as available, but at least for a header @@ -335,18 +317,9 @@ namespace epics { std::size_t requiredBytes, bool persistent) { - LOG(logLevelTrace, - "AbstractCodec::readToBuffer enter requiredBytes: %u," - " persistant: %d (threadId: %u)", - requiredBytes, persistent, epicsThreadGetIdSelf()); - // do we already have requiredBytes available? std::size_t remainingBytes = _socketBuffer->getRemaining(); if (remainingBytes >= requiredBytes) { - LOG(logLevelTrace, - "AbstractCodec::readToBuffer requiredBytes: %u" - " <= remainingBytes: %d (threadId: %u)", - requiredBytes, remainingBytes); return true; } @@ -378,17 +351,8 @@ namespace epics { { int bytesRead = read(_socketBuffer.get()); - LOG(logLevelTrace, - "AbstractCodec::readToBuffer READ BYTES: %d (threadId: %u)", - bytesRead, epicsThreadGetIdSelf()); - if (bytesRead < 0) { - - LOG(logLevelTrace, - "AbstractCodec::before close on bytesRead < 0 condition (threadId: %u)", - epicsThreadGetIdSelf()); - close(); throw connection_closed_exception("bytesRead < 0"); } @@ -418,11 +382,6 @@ namespace epics { void AbstractCodec::ensureData(std::size_t size) { - LOG(logLevelTrace, - "AbstractCodec::ensureData enter: size: %u (threadId: %u)", - size, epicsThreadGetIdSelf()); - - // enough of data? if (_socketBuffer->getRemaining() >= size) return; @@ -553,11 +512,6 @@ namespace epics { std::size_t value, std::size_t alignment) { - LOG(logLevelTrace, - "AbstractCodec::alignedValue enter: value: %u, alignment:%u" - " (threadId: %u)", - value, alignment, epicsThreadGetIdSelf()); - std::size_t k = (alignment - 1); return (value + k) & (~k); } @@ -565,10 +519,6 @@ namespace epics { void AbstractCodec::alignData(std::size_t alignment) { - LOG(logLevelTrace, - "AbstractCodec::alignData enter: alignment:%u (threadId: %u)", - alignment, epicsThreadGetIdSelf()); - std::size_t k = (alignment - 1); std::size_t pos = _socketBuffer->getPosition(); std::size_t newpos = (pos + k) & (~k); @@ -592,10 +542,6 @@ namespace epics { void AbstractCodec::alignBuffer(std::size_t alignment) { - LOG(logLevelTrace, "AbstractCodec::alignBuffer enter:" - " alignment:%u (threadId: %u)", - alignment, epicsThreadGetIdSelf()); - std::size_t k = (alignment - 1); std::size_t pos = _sendBuffer->getPosition(); std::size_t newpos = (pos + k) & (~k); @@ -612,11 +558,6 @@ namespace epics { epics::pvData::int8 command, std::size_t ensureCapacity) { - LOG(logLevelTrace, - "AbstractCodec::startMessage enter: command:%x " - " ensureCapacity:%u (threadId: %u)", - command, ensureCapacity, epicsThreadGetIdSelf()); - _lastMessageStartPosition = std::numeric_limits::max(); // TODO revise this ensureBuffer( @@ -640,11 +581,6 @@ namespace epics { epics::pvData::int8 command, epics::pvData::int32 data) { - LOG(logLevelTrace, - "AbstractCodec::putControlMessage enter: command:%x " - "data:%d (threadId: %u)", - command, data, epicsThreadGetIdSelf()); - _lastMessageStartPosition = std::numeric_limits::max(); // TODO revise this ensureBuffer(PVA_MESSAGE_HEADER_SIZE); @@ -657,20 +593,12 @@ namespace epics { void AbstractCodec::endMessage() { - - LOG(logLevelTrace, "AbstractCodec::endMessage enter: (threadId: %u)", - epicsThreadGetIdSelf()); - endMessage(false); } void AbstractCodec::endMessage(bool hasMoreSegments) { - LOG(logLevelTrace, - "AbstractCodec::endMessage enter: hasMoreSegments:%d (threadId: %u)", - hasMoreSegments, epicsThreadGetIdSelf()); - if (_lastMessageStartPosition != std::numeric_limits::max()) { std::size_t lastPayloadBytePosition = _sendBuffer->getPosition(); @@ -739,10 +667,6 @@ namespace epics { void AbstractCodec::ensureBuffer(std::size_t size) { - LOG(logLevelTrace, - "AbstractCodec::ensureBuffer enter: size:%u (threadId: %u)", - size, epicsThreadGetIdSelf()); - if (_sendBuffer->getRemaining() >= size) return; @@ -763,21 +687,12 @@ namespace epics { // assumes startMessage was called (or header is in place), because endMessage(true) is later called that peeks and sets _lastSegmentedMessageType void AbstractCodec::flushSerializeBuffer() { - - LOG(logLevelTrace, - "AbstractCodec::flushSerializeBuffer enter: (threadId: %u)", - epicsThreadGetIdSelf()); - flush(false); } void AbstractCodec::flush(bool lastMessageCompleted) { - LOG(logLevelTrace, - "AbstractCodec::flush enter: lastMessageCompleted:%d (threadId: %u)", - lastMessageCompleted, epicsThreadGetIdSelf()); - // automatic end endMessage(!lastMessageCompleted); @@ -807,10 +722,6 @@ namespace epics { void AbstractCodec::processWrite() { - LOG(logLevelTrace, - "AbstractCodec::processWrite enter: (threadId: %u)", - epicsThreadGetIdSelf()); - // TODO catch ConnectionClosedException, InvalidStreamException? switch (_writeMode) { @@ -827,10 +738,6 @@ namespace epics { void AbstractCodec::send(ByteBuffer *buffer) { - LOG(logLevelTrace, "AbstractCodec::send enter: (threadId: %u)", - epicsThreadGetIdSelf()); - - // On Windows, limiting the buffer size is important to prevent // poor throughput performances when transferring large amount of // data. See Microsoft KB article KB823764. @@ -856,11 +763,13 @@ namespace epics { //int p = buffer.position(); int bytesSent = write(buffer); + /* if (IS_LOGGABLE(logLevelTrace)) { hexDump(std::string("AbstractCodec::send WRITE"), (const int8 *)buffer->getArray(), buffer->getPosition(), buffer->getRemaining()); } + */ if (bytesSent < 0) { @@ -894,10 +803,6 @@ namespace epics { void AbstractCodec::processSendQueue() { - LOG(logLevelTrace, - "AbstractCodec::processSendQueue enter: (threadId: %u)", - epicsThreadGetIdSelf()); - { std::size_t senderProcessed = 0; while (senderProcessed++ < MAX_MESSAGE_SEND) @@ -935,22 +840,12 @@ namespace epics { void AbstractCodec::clearSendQueue() { - LOG(logLevelTrace, - "AbstractCodec::clearSendQueue enter: (threadId: %u)", - epicsThreadGetIdSelf()); - _sendQueue.clean(); } void AbstractCodec::enqueueSendRequest( TransportSender::shared_pointer const & sender) { - - LOG(logLevelTrace, - "AbstractCodec::enqueueSendRequest enter: sender is set:%d" - " (threadId: %u)", - (sender.get() != 0), epicsThreadGetIdSelf()); - _sendQueue.put(sender); scheduleSend(); } @@ -958,10 +853,6 @@ namespace epics { void AbstractCodec::setSenderThread() { - LOG(logLevelTrace, - "AbstractCodec::setSenderThread enter: (threadId: %u)", - epicsThreadGetIdSelf()); - _senderThread = epicsThreadGetIdSelf(); } @@ -970,10 +861,6 @@ namespace epics { TransportSender::shared_pointer const & sender) { - LOG(logLevelTrace, - "AbstractCodec::processSender enter: sender is set:%d (threadId: %u)", - (sender.get() != 0), epicsThreadGetIdSelf); - ScopedLock lock(sender); try { @@ -1007,11 +894,6 @@ namespace epics { TransportSender::shared_pointer const & sender, std::size_t requiredBufferSize) { - LOG(logLevelTrace, - "AbstractCodec::enqueueSendRequest enter: sender is set:%d " - "requiredBufferSize:%u (threadId: %u)", - (sender.get() != 0), requiredBufferSize, epicsThreadGetIdSelf); - if (_senderThread == epicsThreadGetIdSelf() && _sendQueue.empty() && _sendBuffer->getRemaining() >= requiredBufferSize) @@ -1031,22 +913,12 @@ namespace epics { void AbstractCodec::setRecipient(osiSockAddr const & sendTo) { - - LOG(logLevelTrace, - "AbstractCodec::setRecipient enter: (threadId: %u)", - epicsThreadGetIdSelf); - _sendTo = sendTo; } void AbstractCodec::setByteOrder(int byteOrder) { - - LOG(logLevelTrace, - "AbstractCodec::setByteOrder enter: byteOrder:%x (threadId: %u)", - byteOrder, epicsThreadGetIdSelf()); - _socketBuffer->setEndianess(byteOrder); // TODO sync _sendBuffer->setEndianess(byteOrder); @@ -1063,40 +935,21 @@ namespace epics { // void BlockingAbstractCodec::readPollOne() { - - LOG(logLevelTrace, - "BlockingAbstractCodec::readPollOne enter: (threadId: %u)", - epicsThreadGetIdSelf()); - throw std::logic_error("should not be called for blocking IO"); } void BlockingAbstractCodec::writePollOne() { - - LOG(logLevelTrace, - "BlockingAbstractCodec::writePollOne enter: (threadId: %u)", - epicsThreadGetIdSelf()); - throw std::logic_error("should not be called for blocking IO"); } void BlockingAbstractCodec::close() { - LOG(logLevelTrace, - "BlockingAbstractCodec::close enter: (threadId: %u)", - epicsThreadGetIdSelf()); - - if (_isOpen.getAndSet(false)) { // always close in the same thread, same way, etc. // wakeup processSendQueue - LOG(logLevelTrace, - "BlockingAbstractCodec::close _sendQueue.waaaaakeup: " - " (threadId: %u)", - epicsThreadGetIdSelf()); // clean resources internalClose(true); @@ -1106,12 +959,6 @@ namespace epics { // post close internalPostClose(true); } - else { - LOG(logLevelTrace, - "BlockingAbstractCodec::close NOT WAKING UP _sendQueue: " - " (threadId: %u)", - epicsThreadGetIdSelf()); - } } void BlockingAbstractCodec::internalClose(bool /*force*/) { @@ -1121,20 +968,11 @@ namespace epics { } bool BlockingAbstractCodec::terminated() { - - LOG(logLevelTrace, - "BlockingAbstractCodec::terminated enter: (threadId: %u)", - epicsThreadGetIdSelf()); - return !isOpen(); } bool BlockingAbstractCodec::isOpen() { - - LOG(logLevelTrace, "BlockingAbstractCodec::isOpen %d (threadId: %u)", _isOpen.get(), - epicsThreadGetIdSelf()); - return _isOpen.get(); } @@ -1142,9 +980,6 @@ namespace epics { // NOTE: must not be called from constructor (e.g. needs shared_from_this()) void BlockingAbstractCodec::start() { - LOG(logLevelTrace, "BlockingAbstractCodec::start enter: (threadId: %u)", - epicsThreadGetIdSelf()); - _readThread = epicsThreadCreate( "BlockingAbstractCodec-readThread", epicsThreadPriorityMedium, @@ -1161,21 +996,12 @@ namespace epics { BlockingAbstractCodec::sendThread, this); - LOG(logLevelTrace, - "BlockingAbstractCodec::start exit WITH readThread: %u," - " sendThread:%u (threadId: %u)", - _readThread, _sendThread, epicsThreadGetIdSelf()); - } void BlockingAbstractCodec::receiveThread(void *param) { - LOG(logLevelTrace, - "BlockingAbstractCodec::receiveThread enter: (threadId: %u)", - epicsThreadGetIdSelf()); - BlockingAbstractCodec *bac = static_cast(param); Transport::shared_pointer ptr = bac->shared_from_this(); @@ -1190,23 +1016,14 @@ namespace epics { } } - LOG(logLevelTrace, "BlockingAbstractCodec::receiveThread" - " EXIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIT: (threadId: %u)", - epicsThreadGetIdSelf()); - bac->_shutdownEvent.signal(); - } void BlockingAbstractCodec::sendThread(void *param) { - LOG(logLevelTrace, - "BlockingAbstractCodec::sendThread enter: (threadId: %u)", - epicsThreadGetIdSelf()); - - BlockingAbstractCodec *bac = static_cast(param); + BlockingAbstractCodec *bac = static_cast(param); Transport::shared_pointer ptr = bac->shared_from_this(); bac->setSenderThread(); @@ -1222,38 +1039,16 @@ namespace epics { } } - LOG(logLevelTrace, - "BlockingAbstractCodec::sendThread EXIIIIIIIIIIIIIIT" - " while(bac->isOpen): (threadId: %u)", - epicsThreadGetIdSelf()); - - // wait read thread to die bac->_shutdownEvent.wait(); // call internal destroy - LOG(logLevelTrace, "XXXXXXXXXXXXXXXXXXXXXXXXXXXX" - "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX (threadId: %u)", - epicsThreadGetIdSelf()); bac->internalDestroy(); - LOG(logLevelTrace, "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" - "YYYYYYYYYYYYYYYYYYYYYYYYYYYY (threadId: %u)", - epicsThreadGetIdSelf()); - - LOG(logLevelTrace, - "BlockingAbstractCodec::sendThread EXIIIIT (threadId: %u)", - epicsThreadGetIdSelf()); } void BlockingAbstractCodec::sendBufferFull(int tries) { - - LOG(logLevelTrace, - "BlockingAbstractCodec::sendBufferFull enter: tries: %d " - "(threadId: %u)", - tries, epicsThreadGetIdSelf()); - // TODO constants epicsThreadSleep(std::max(tries * 0.1, 1)); } @@ -1309,18 +1104,11 @@ namespace epics { inetAddressToString(_socketAddress).c_str(), errStr); } - LOG(logLevelTrace, - "BlockingSocketAbstractCodec constructed (threadId: %u)", - epicsThreadGetIdSelf()); } // must be called only once, when there will be no operation on socket (e.g. just before tx/rx thread exists) void BlockingSocketAbstractCodec::internalDestroy() { - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::internalDestroy enter: (threadId: %u)", - epicsThreadGetIdSelf()); - if(_channel != INVALID_SOCKET) { epicsSocketDestroy(_channel); _channel = INVALID_SOCKET; @@ -1330,12 +1118,6 @@ namespace epics { void BlockingSocketAbstractCodec::invalidDataStreamHandler() { - - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::invalidDataStreamHandler enter:" - " (threadId: %u)", - epicsThreadGetIdSelf()); - close(); } @@ -1343,29 +1125,14 @@ namespace epics { int BlockingSocketAbstractCodec::write( epics::pvData::ByteBuffer *src) { - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::write enter: position:%u, " - "remaining:%u (threadId: %u)", - src->getPosition(), src->getRemaining(), epicsThreadGetIdSelf()); - std::size_t remaining; while((remaining=src->getRemaining()) > 0) { - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::write before send" - " (threadId: %u)", - epicsThreadGetIdSelf()); - - int bytesSent = ::send(_channel, &src->getArray()[src->getPosition()], remaining, 0); - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::write afer send, read:%d" - " (threadId: %u)", - bytesSent, epicsThreadGetIdSelf()); - + // NOTE: do not log here, you might override SOCKERRNO relevant to recv() operation above if(unlikely(bytesSent<0)) { @@ -1384,18 +1151,13 @@ namespace epics { } - //TODO check what to return - return -1; + return 0; } std::size_t BlockingSocketAbstractCodec::getSocketReceiveBufferSize() const { - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::getSocketReceiveBufferSize" - " enter (threadId: %u)", epicsThreadGetIdSelf()); - osiSocklen_t intLen = sizeof(int); char strBuffer[64]; int socketRecvBufferSize; @@ -1407,46 +1169,24 @@ namespace epics { //LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer); } - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::getSocketReceiveBufferSize" - " returning:%u (threadId: %u)", socketRecvBufferSize, - epicsThreadGetIdSelf()); - return socketRecvBufferSize; } int BlockingSocketAbstractCodec::read(epics::pvData::ByteBuffer* dst) { - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::read enter: " - "read bytes:%u (threadId: %u)", - dst->getRemaining(), epicsThreadGetIdSelf()); - std::size_t remaining; while((remaining=dst->getRemaining()) > 0) { // read std::size_t pos = dst->getPosition(); - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::read before recv" - " (threadId: %u)", - epicsThreadGetIdSelf()); - - int bytesRead = recv(_channel, (char*)(dst->getArray()+pos), remaining, 0); // NOTE: do not log here, you might override SOCKERRNO relevant to recv() operation above /* - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::read after recv, read: %d", - bytesRead," (threadId: %u)", - epicsThreadGetIdSelf()); - - if (IS_LOGGABLE(logLevelTrace)) { hexDump(std::string("READ"), (const int8 *)(dst->getArray()+pos), bytesRead); @@ -1459,11 +1199,6 @@ namespace epics { { int socketError = SOCKERRNO; - LOG(logLevelTrace, - "BlockingSocketAbstractCodec::read SOCKERRNO %d", socketError, - " (threadId: %u)", - epicsThreadGetIdSelf()); - // interrupted or timeout if (socketError == EINTR || socketError == EAGAIN || @@ -1497,28 +1232,15 @@ namespace epics { //register/unregister // TODO implement priorities in Reactor... not that user will // change it.. still getPriority() must return "registered" priority! - - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec constructed (threadId: %u)", - epicsThreadGetIdSelf()); - } BlockingServerTCPTransportCodec::~BlockingServerTCPTransportCodec() { - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec DESTRUCTED (threadId: %u)", - epicsThreadGetIdSelf()); } pvAccessID BlockingServerTCPTransportCodec::preallocateChannelSID() { - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::preallocateChannelSID enter:" - " (threadId: %u)", - epicsThreadGetIdSelf()); - Lock lock(_channelsMutex); // search first free (theoretically possible loop of death) pvAccessID sid = ++_lastChannelSID; @@ -1532,11 +1254,6 @@ namespace epics { pvAccessID sid, ServerChannel::shared_pointer const & channel) { - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::registerChannel enter: sid:%d " - " (threadId: %u)", - channel->getSID(), epicsThreadGetIdSelf()); - Lock lock(_channelsMutex); _channels[sid] = channel; @@ -1545,11 +1262,6 @@ namespace epics { void BlockingServerTCPTransportCodec::unregisterChannel(pvAccessID sid) { - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::unregisterChannel enter:" - " sid:%d (threadId: %u)", - sid, epicsThreadGetIdSelf()); - Lock lock(_channelsMutex); _channels.erase(sid); } @@ -1558,11 +1270,6 @@ namespace epics { ServerChannel::shared_pointer BlockingServerTCPTransportCodec::getChannel(pvAccessID sid) { - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::getChannel enter:" - " sid:%d (threadId: %u)", - sid, epicsThreadGetIdSelf()); - Lock lock(_channelsMutex); std::map::iterator it = @@ -1576,11 +1283,6 @@ namespace epics { int BlockingServerTCPTransportCodec::getChannelCount() { - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::getChannelCount enter: " - "(threadId: %u)", - epicsThreadGetIdSelf()); - Lock lock(_channelsMutex); return static_cast(_channels.size()); } @@ -1589,10 +1291,6 @@ namespace epics { void BlockingServerTCPTransportCodec::send(ByteBuffer* buffer, TransportSendControl* control) { - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::send enter: (threadId: %u)", - epicsThreadGetIdSelf()); - // // set byte order control message // @@ -1893,7 +1591,6 @@ namespace epics { } - + } } - } diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index f5b5b17..a752acd 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -28,7 +28,6 @@ #include #include #include -#include #ifdef abstractCodecEpicsExportSharedSymbols # define epicsExportSharedSymbols @@ -44,6 +43,7 @@ namespace epics { namespace pvAccess { + namespace detail { // TODO replace mutex with atomic (CAS) operations template @@ -80,24 +80,17 @@ namespace epics { */ ~queue(void) { - LOG(logLevelTrace, - "queue::~queue DESTROY (threadId: %u)", epicsThreadGetIdSelf()); } bool empty(void) { - LOG(logLevelTrace, - "queue::empty enter: (threadId: %u)", epicsThreadGetIdSelf()); epics::pvData::Lock lock(_queueMutex); return _queue.empty(); } void clean() { - LOG(logLevelTrace, "queue::clean enter: (threadId: %u)", - epicsThreadGetIdSelf()); - epics::pvData::Lock lock(_queueMutex); _queue.clear(); } @@ -105,15 +98,8 @@ namespace epics { void wakeup() { - - LOG(logLevelTrace, "queue::wakeup enter: (threadId: %u)", - epicsThreadGetIdSelf()); - if (!_wakeup.getAndSet(true)) { - LOG(logLevelTrace, - "queue::wakeup signaling on _queueEvent: (threadId: %u)", - epicsThreadGetIdSelf()); _queueEvent.signal(); } } @@ -121,9 +107,6 @@ namespace epics { void put(T const & elem) { - LOG(logLevelTrace, - "queue::put enter (threadId: %u)", epicsThreadGetIdSelf()); - { epics::pvData::Lock lock(_queueMutex); _queue.push_back(elem); @@ -135,11 +118,6 @@ namespace epics { T take(int timeOut) { - - LOG(logLevelTrace, - "queue::take enter timeOut:%d (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); - while (true) { @@ -149,10 +127,6 @@ namespace epics { { if (timeOut < 0) { - epics::pvAccess::LOG(logLevelTrace, - "queue::take exit timeOut:%d (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); - return T(); } @@ -160,45 +134,21 @@ namespace epics { { if (timeOut == 0) { - - LOG(logLevelTrace, - "queue::take going to wait timeOut:%d (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); - _queueEvent.wait(); } else { - - LOG(logLevelTrace, - "queue::take going to wait timeOut:%d (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); - _queueEvent.wait(timeOut); } - LOG(logLevelTrace, - "queue::take waking up timeOut:%d (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); - isEmpty = empty(); if (isEmpty) { if (timeOut > 0) { // TODO spurious wakeup, but not critical - LOG(logLevelTrace, - "queue::take exit after being woken up timeOut:%d" - " (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); return T(); } else // if (timeout == 0) cannot be negative { if (_wakeup.getAndSet(false)) { - - LOG(logLevelTrace, - "queue::take exit after being woken up timeOut:%d" - " (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); - return T(); } } @@ -207,20 +157,9 @@ namespace epics { } else { - - LOG(logLevelTrace, - "queue::take obtaining lock for front element timeOut:%d" - " (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); - epics::pvData::Lock lock(_queueMutex); T sender = _queue.front(); _queue.pop_front(); - - LOG(logLevelTrace, - "queue::take exit with sender timeOut:%d (threadId: %u)", - timeOut, epicsThreadGetIdSelf()); - return sender; } } @@ -296,9 +235,6 @@ namespace epics { virtual ~AbstractCodec() { - LOG(logLevelTrace, - "AbstractCodec::~AbstractCodec DESTROY (threadId: %u)", - epicsThreadGetIdSelf()); } void alignBuffer(std::size_t alignment); @@ -351,7 +287,7 @@ namespace epics { std::tr1::shared_ptr _socketBuffer; std::tr1::shared_ptr _sendBuffer; - epics::pvAccess::queue _sendQueue; + queue _sendQueue; private: @@ -473,11 +409,6 @@ namespace epics { void internalDestroy() { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::internalDestroy() enter: (threadId: %u)", - epicsThreadGetIdSelf()); - BlockingSocketAbstractCodec::internalDestroy(); Transport::shared_pointer thisSharedPtr = this->shared_from_this(); _context->getTransportRegistry()->remove(thisSharedPtr); @@ -488,12 +419,6 @@ namespace epics { void processControlMessage() { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::processControlMessage()" - "enter: (threadId: %u)", - epicsThreadGetIdSelf()); - if (_command == 2) { // check 7-th bit @@ -503,12 +428,6 @@ namespace epics { void processApplicationMessage() { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::processApplicationMessage() enter:" - " (threadId: %u)", - epicsThreadGetIdSelf()); - _responseHandler->handleResponse(&_socketAddress, shared_from_this(), _version, _command, _payloadSize, _socketBuffer.get()); } @@ -535,37 +454,18 @@ namespace epics { void setRemoteRevision(epics::pvData::int8 revision) { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::setRemoteRevision() enter:" - " revision: %d (threadId: %u)", - revision, epicsThreadGetIdSelf()); - _remoteTransportRevision = revision; } void setRemoteTransportReceiveBufferSize( std::size_t remoteTransportReceiveBufferSize) { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::setRemoteTransportReceiveBufferSize()" - " enter: remoteTransportReceiveBufferSize:%u (threadId: %u)", - remoteTransportReceiveBufferSize, epicsThreadGetIdSelf()); - _remoteTransportReceiveBufferSize = remoteTransportReceiveBufferSize; } void setRemoteTransportSocketReceiveBufferSize( std::size_t socketReceiveBufferSize) { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::" - "setRemoteTransportSocketReceiveBufferSize()" - "enter: socketReceiveBufferSize:%u (threadId: %u)", - socketReceiveBufferSize, epicsThreadGetIdSelf()); - _remoteTransportSocketReceiveBufferSize = socketReceiveBufferSize; } @@ -573,12 +473,6 @@ namespace epics { std::tr1::shared_ptr cachedDeserialize(epics::pvData::ByteBuffer* buffer) { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::cachedDeserialize() enter:" - " (threadId: %u)", - epicsThreadGetIdSelf()); - return _incomingIR.deserialize(buffer, this); } @@ -587,12 +481,6 @@ namespace epics { const std::tr1::shared_ptr& field, epics::pvData::ByteBuffer* buffer) { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::cachedSerialize() enter:" - " (threadId: %u)", - epicsThreadGetIdSelf()); - _outgoingIR.serialize(field, buffer, this); } @@ -602,11 +490,7 @@ namespace epics { const char* /*toSerialize*/, std::size_t /*elementCount*/, std::size_t /*elementSize*/) { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::directSerialize() enter: (threadId: %u)", - epicsThreadGetIdSelf()); - + // TODO !!!! return false; } @@ -614,12 +498,7 @@ namespace epics { bool directDeserialize(epics::pvData::ByteBuffer * /*existingBuffer*/, char* /*deserializeTo*/, std::size_t /*elementCount*/, std::size_t /*elementSize*/) { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::directDeserialize() enter:" - " (threadId: %u)", - epicsThreadGetIdSelf()); - + // TODO !!! return false; } @@ -628,21 +507,11 @@ namespace epics { bool isClosed() { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::isClosed() enter: (threadId: %u)", - epicsThreadGetIdSelf()); - return !isOpen(); } void activate() { - - LOG(logLevelTrace, - "BlockingTCPTransportCodec::activate() enter: (threadId: %u)", - epicsThreadGetIdSelf()); - Transport::shared_pointer thisSharedPtr = shared_from_this(); _context->getTransportRegistry()->put(thisSharedPtr); @@ -664,9 +533,6 @@ namespace epics { _remoteTransportReceiveBufferSize(MAX_TCP_RECV), _remoteTransportRevision(0), _priority(priority) { - LOG(logLevelTrace, - "BlockingTCPTransportCodec constructed: (threadId: %u)", - epicsThreadGetIdSelf()); } Context::shared_pointer _context; @@ -720,12 +586,6 @@ namespace epics { bool acquire(std::tr1::shared_ptr const & client) { - - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::acquire() enter:" - " client is set: %d (threadId: %u)", - (client.get() != 0), epicsThreadGetIdSelf()); - return false; } @@ -748,12 +608,6 @@ namespace epics { int getChannelCount(); epics::pvData::PVField::shared_pointer getSecurityToken() { - - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::getSecurityToken() enter:" - " (threadId: %u)", - epicsThreadGetIdSelf()); - return epics::pvData::PVField::shared_pointer(); } @@ -766,12 +620,6 @@ namespace epics { } bool verify(epics::pvData::int32 timeoutMs) { - - LOG(logLevelTrace, - "BlockingServerTCPTransportCodec::verify() enter: " - "timeoutMs:%d (threadId: %u)", - timeoutMs, epicsThreadGetIdSelf()); - TransportSender::shared_pointer transportSender = std::tr1::dynamic_pointer_cast(shared_from_this()); enqueueSendRequest(transportSender); @@ -944,7 +792,8 @@ namespace epics { epics::pvData::Event _verifiedEvent; }; - + + } } } diff --git a/pvAccessCPP.files b/pvAccessCPP.files index 82a19d7..a0e4c1e 100644 --- a/pvAccessCPP.files +++ b/pvAccessCPP.files @@ -23,12 +23,9 @@ pvAccessApp/mb/pvAccessMB.h pvAccessApp/remote/abstractResponseHandler.cpp pvAccessApp/remote/beaconHandler.cpp pvAccessApp/remote/beaconHandler.h -pvAccessApp/remote/blockingClientTCPTransport.cpp -pvAccessApp/remote/blockingServerTCPTransport.cpp pvAccessApp/remote/blockingTCP.h pvAccessApp/remote/blockingTCPAcceptor.cpp pvAccessApp/remote/blockingTCPConnector.cpp -pvAccessApp/remote/blockingTCPTransport.cpp pvAccessApp/remote/blockingUDP.h pvAccessApp/remote/blockingUDPConnector.cpp pvAccessApp/remote/blockingUDPTransport.cpp diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index 0897354..b400b7f 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -16,6 +16,7 @@ #include using namespace epics::pvData; +using namespace epics::pvAccess::detail; namespace epics { From 3c7bac27fd1cd98d25a71c831339723bee4d8e64 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 26 Mar 2014 13:20:13 +0100 Subject: [PATCH 17/19] codec now tests pass --- pvAccessApp/remote/codec.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index df3bb2c..4c1c57b 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -194,6 +194,9 @@ namespace epics { } catch(...) { + if (!isOpen()) + return; + if (postProcess) { postProcessApplicationMessage(); From b2d2734b583063fe4930fbc44a51f615821b4b84 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 26 Mar 2014 14:02:16 +0100 Subject: [PATCH 18/19] TCP vs UDP initialization order, logging handling --- pvAccessApp/remote/codec.cpp | 76 ++++++++++++++++------------ pvAccessApp/remote/codec.h | 6 +-- pvAccessApp/server/serverContext.cpp | 8 +-- testApp/remote/testCodec.cpp | 9 ++-- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/pvAccessApp/remote/codec.cpp b/pvAccessApp/remote/codec.cpp index 4c1c57b..e26f25a 100644 --- a/pvAccessApp/remote/codec.cpp +++ b/pvAccessApp/remote/codec.cpp @@ -131,9 +131,9 @@ namespace epics { if (magicCode != PVA_MAGIC) { LOG(logLevelError, - "Invalid header received from the client at %s:%d: %d," + "Invalid header received from the client at %s:%d: %s," " disconnecting...", - __FILE__, __LINE__, getLastReadBufferSocketAddress()); + __FILE__, __LINE__, inetAddressToString(*getLastReadBufferSocketAddress()).c_str()); invalidDataStreamHandler(); throw invalid_data_stream_exception("invalid header received"); } @@ -168,8 +168,8 @@ namespace epics { { LOG(logLevelWarn, "Not-a-frst segmented message received in normal mode" - " from the client at %s:%d: %d, disconnecting...", - __FILE__, __LINE__, getLastReadBufferSocketAddress()); + " from the client at %s:%d: %s, disconnecting...", + __FILE__, __LINE__, inetAddressToString(*getLastReadBufferSocketAddress()).c_str()); invalidDataStreamHandler(); throw invalid_data_stream_exception( "not-a-first segmented message received in normal mode"); @@ -260,9 +260,9 @@ namespace epics { // TODO we do not handle this for now (maybe never) LOG(logLevelWarn, - "unprocessed read buffer from client at %s:%d: %d," + "unprocessed read buffer from client at %s:%d: %s," " disconnecting...", - __FILE__, __LINE__, getLastReadBufferSocketAddress()); + __FILE__, __LINE__, inetAddressToString(*getLastReadBufferSocketAddress()).c_str()); invalidDataStreamHandler(); throw invalid_data_stream_exception( "unprocessed read buffer"); @@ -299,8 +299,8 @@ namespace epics { { LOG(logLevelWarn, "Not-a-first segmented message expected from the client at" - " %s:%d: %d, disconnecting...", - __FILE__, __LINE__, getLastReadBufferSocketAddress()); + " %s:%d: %s, disconnecting...", + __FILE__, __LINE__, inetAddressToString(*getLastReadBufferSocketAddress()).c_str()); invalidDataStreamHandler(); throw new invalid_data_stream_exception( "not-a-first segmented message expected"); @@ -1162,14 +1162,17 @@ namespace epics { const { osiSocklen_t intLen = sizeof(int); - char strBuffer[64]; int socketRecvBufferSize; int retval = getsockopt(_channel, SOL_SOCKET, SO_RCVBUF, (char *)&socketRecvBufferSize, &intLen); if(retval<0) { - epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); - //LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer); + if (IS_LOGGABLE(logLevelDebug)) + { + char strBuffer[64]; + epicsSocketConvertErrnoToString(strBuffer, sizeof(strBuffer)); + LOG(logLevelDebug, "Error getting SO_SNDBUF: %s", strBuffer); + } } return socketRecvBufferSize; @@ -1327,13 +1330,15 @@ namespace epics { Lock lock(_channelsMutex); if(_channels.size()==0) return; - 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()); + 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()); + } std::map::iterator it = _channels.begin(); for(; it!=_channels.end(); it++) @@ -1443,9 +1448,12 @@ namespace epics { Lock lock(_mutex); if(isClosed()) return false; - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG(logLevelDebug, "Acquiring transport to %s.", ipAddrStr); + if (IS_LOGGABLE(logLevelDebug)) + { + char ipAddrStr[48]; + ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); + LOG(logLevelDebug, "Acquiring transport to %s.", ipAddrStr); + } _owners[client->getID()] = TransportClient::weak_pointer(client); //_owners.insert(TransportClient::weak_pointer(client)); @@ -1476,12 +1484,16 @@ namespace epics { // check if still acquired size_t refs = _owners.size(); if(refs>0) { - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - LOG( - logLevelDebug, - "Transport to %s still has %d client(s) active and closing...", - ipAddrStr, refs); + + 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); + } TransportClientMap_t::iterator it = _owners.begin(); for(; it!=_owners.end(); it++) { @@ -1502,10 +1514,12 @@ namespace epics { Lock lock(_mutex); if(isClosed()) return; - char ipAddrStr[48]; - ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); - - LOG(logLevelDebug, "Releasing transport to %s.", ipAddrStr); + if (IS_LOGGABLE(logLevelDebug)) + { + char ipAddrStr[48]; + ipAddrToDottedIP(&_socketAddress.ia, ipAddrStr, sizeof(ipAddrStr)); + LOG(logLevelDebug, "Releasing transport to %s.", ipAddrStr); + } _owners.erase(clientID); //_owners.erase(TransportClient::weak_pointer(client)); diff --git a/pvAccessApp/remote/codec.h b/pvAccessApp/remote/codec.h index a752acd..ce4a5d3 100644 --- a/pvAccessApp/remote/codec.h +++ b/pvAccessApp/remote/codec.h @@ -220,7 +220,7 @@ namespace epics { virtual void processControlMessage() = 0; virtual void processApplicationMessage() = 0; - virtual osiSockAddr getLastReadBufferSocketAddress() = 0; + virtual const osiSockAddr* getLastReadBufferSocketAddress() = 0; virtual void invalidDataStreamHandler() = 0; virtual void readPollOne()=0; virtual void writePollOne() = 0; @@ -383,7 +383,7 @@ namespace epics { int read(epics::pvData::ByteBuffer* dst); int write(epics::pvData::ByteBuffer* src); - osiSockAddr getLastReadBufferSocketAddress() { return _socketAddress;} + const osiSockAddr* getLastReadBufferSocketAddress() { return &_socketAddress; } void invalidDataStreamHandler(); std::size_t getSocketReceiveBufferSize() const; @@ -488,7 +488,7 @@ namespace epics { bool directSerialize( epics::pvData::ByteBuffer * /*existingBuffer*/, const char* /*toSerialize*/, - std::size_t /*elementCount*/, std::size_t /*elementSize*/) + std::size_t /*elementCount*/, std::size_t /*elementSize*/) { // TODO !!!! return false; diff --git a/pvAccessApp/server/serverContext.cpp b/pvAccessApp/server/serverContext.cpp index 832067b..d16f9bb 100644 --- a/pvAccessApp/server/serverContext.cpp +++ b/pvAccessApp/server/serverContext.cpp @@ -204,15 +204,15 @@ void ServerContextImpl::internalInitialize() _timer.reset(new Timer("pvAccess-server timer", lowerPriority)); _transportRegistry.reset(new TransportRegistry()); - // setup broadcast UDP transport - initializeBroadcastTransport(); - ServerContextImpl::shared_pointer thisServerContext = shared_from_this(); _acceptor.reset(new BlockingTCPAcceptor(thisServerContext, thisServerContext, _serverPort, _receiveBufferSize)); _serverPort = ntohs(_acceptor->getBindAddress()->ia.sin_port); - _beaconEmitter.reset(new BeaconEmitter(_broadcastTransport, thisServerContext)); + // setup broadcast UDP transport + initializeBroadcastTransport(); + + _beaconEmitter.reset(new BeaconEmitter(_broadcastTransport, thisServerContext)); } void ServerContextImpl::initializeBroadcastTransport() diff --git a/testApp/remote/testCodec.cpp b/testApp/remote/testCodec.cpp index b400b7f..8adb5c3 100644 --- a/testApp/remote/testCodec.cpp +++ b/testApp/remote/testCodec.cpp @@ -84,7 +84,8 @@ namespace epics { _disconnected(false), _forcePayloadRead(-1), _readBuffer(new ByteBuffer(receiveBufferSize)), - _writeBuffer(sendBufferSize) + _writeBuffer(sendBufferSize), + _dummyAddress() { } @@ -273,10 +274,9 @@ namespace epics { return _sendBuffer; } - osiSockAddr getLastReadBufferSocketAddress() + const osiSockAddr* getLastReadBufferSocketAddress() { - osiSockAddr tmp = {{0}}; - return tmp; + return &_dummyAddress; } void invalidDataStreamHandler() { _invalidDataStreamCount++; } @@ -380,6 +380,7 @@ namespace epics { std::auto_ptr _readPollOneCallback; std::auto_ptr _writePollOneCallback; + osiSockAddr _dummyAddress; protected: From 049ee0c6af32a0431d4f723152908c10452b5db6 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Wed, 26 Mar 2014 14:07:51 +0100 Subject: [PATCH 19/19] flow: Closed 'codec'.