port from pvAccessCPP-md

This commit is contained in:
Matej Sekoranja
2011-05-12 12:47:55 +02:00
parent a077d7d084
commit c36ba5264d
68 changed files with 4762 additions and 5612 deletions

View File

@@ -6,9 +6,9 @@
*/
#include "blockingTCP.h"
#include "inetAddressUtil.h"
#include "growingCircularBuffer.h"
#include "caConstants.h"
#include <inetAddressUtil.h>
#include <caConstants.h>
#include <CDRMonitor.h>
/* pvData */
#include <lock.h>
@@ -37,6 +37,7 @@ using std::ostringstream;
namespace epics {
namespace pvAccess {
/*
class MonitorSender : public TransportSender, public NoDefaultMethods {
public:
MonitorSender(Mutex* monitorMutex, GrowingCircularBuffer<
@@ -67,49 +68,67 @@ namespace epics {
Mutex* _monitorMutex;
GrowingCircularBuffer<TransportSender*>* _monitorSendQueue;
};
*/
BlockingTCPTransport::BlockingTCPTransport(Context* context,
SOCKET channel, ResponseHandler* responseHandler,
PVDATA_REFCOUNT_MONITOR_DEFINE(blockingTCPTransport);
BlockingTCPTransport::BlockingTCPTransport(Context::shared_pointer& context,
SOCKET channel, auto_ptr<ResponseHandler>& responseHandler,
int receiveBufferSize, int16 priority) :
_closed(false), _channel(channel),
_channel(channel),
_priority(priority),
_responseHandler(responseHandler),
_markerPeriodBytes(MARKER_PERIOD),
_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),
_priority(priority), _responseHandler(responseHandler),
_totalBytesReceived(0), _totalBytesSent(0),
_markerToSend(0), _verified(false), _remoteBufferFreeSpace(
LONG_LONG_MAX), _autoDelete(true),
_markerPeriodBytes(MARKER_PERIOD), _nextMarkerPosition(
_markerPeriodBytes), _sendPending(false),
_lastMessageStartPosition(0), _stage(READ_FROM_SOCKET),
_lastSegmentedMessageType(0), _lastSegmentedMessageCommand(
0), _storedPayloadSize(0), _storedPosition(0),
_storedLimit(0), _magicAndVersion(0), _packetType(0),
_command(0), _payloadSize(0), _flushRequested(false),
_sendBufferSentPosition(0), _flushStrategy(DELAYED),
_sendQueue(
new GrowingCircularBuffer<TransportSender*> (100)),
_rcvThreadId(NULL), _sendThreadId(NULL), _monitorSendQueue(
new GrowingCircularBuffer<TransportSender*> (100)),
_monitorSender(new MonitorSender(&_monitorMutex,
_monitorSendQueue)), _context(context),
_sendThreadExited(false) {
_sendQueue(),
//_monitorSendQueue(),
_nextMarkerPosition(_markerPeriodBytes),
_sendPending(false),
_lastMessageStartPosition(0),
_lastSegmentedMessageType(0),
_lastSegmentedMessageCommand(0),
_flushRequested(false),
_sendBufferSentPosition(0),
_storedPayloadSize(0),
_storedPosition(0),
_storedLimit(0),
_magicAndVersion(0),
_packetType(0),
_command(0),
_payloadSize(0),
_stage(READ_FROM_SOCKET),
_totalBytesReceived(0),
_closed(false),
_sendThreadExited(false),
_verified(false),
_markerToSend(0),
_totalBytesSent(0),
_remoteBufferFreeSpace(LONG_LONG_MAX)
{
PVDATA_REFCOUNT_MONITOR_CONSTRUCT(blockingTCPTransport);
_socketBuffer = new ByteBuffer(max(MAX_TCP_RECV
+MAX_ENSURE_DATA_BUFFER_SIZE, receiveBufferSize), EPICS_ENDIAN_BIG);
// TODO minor tweak: deque size is not preallocated...
_socketBuffer = new ByteBuffer(max(MAX_TCP_RECV+MAX_ENSURE_DATA_BUFFER_SIZE, receiveBufferSize), EPICS_ENDIAN_BIG);
_socketBuffer->setPosition(_socketBuffer->getLimit());
_startPosition = _socketBuffer->getPosition();
// allocate buffer
_sendBuffer = new ByteBuffer(_socketBuffer->getSize(), EPICS_ENDIAN_BIG);
_maxPayloadSize = _sendBuffer->getSize()-2*CA_MESSAGE_HEADER_SIZE; // one for header, one for flow control
_maxPayloadSize = _sendBuffer->getSize() - 2*CA_MESSAGE_HEADER_SIZE; // one for header, one for flow control
// get send buffer size
socklen_t intLen = sizeof(int);
int retval = getsockopt(_channel, SOL_SOCKET, SO_SNDBUF,
&_socketSendBufferSize, &intLen);
int retval = getsockopt(_channel, SOL_SOCKET, SO_SNDBUF, &_socketSendBufferSize, &intLen);
if(retval<0) {
_socketSendBufferSize = MAX_TCP_RECV;
errlogSevPrintf(errlogMinor,
@@ -121,74 +140,64 @@ namespace epics {
retval = getpeername(_channel, &(_socketAddress.sa), &saSize);
if(retval<0) {
errlogSevPrintf(errlogMajor,
"Error fetching socket remote address: %s", strerror(
errno));
"Error fetching socket remote address: %s",
strerror(errno));
}
// prepare buffer
clearAndReleaseBuffer();
// add to registry
_context->acquire();
_context->getTransportRegistry()->put(this);
}
BlockingTCPTransport::~BlockingTCPTransport() {
PVDATA_REFCOUNT_MONITOR_DESTRUCT(blockingTCPTransport);
close(true);
TransportSender* sender;
while ((sender = _monitorSendQueue->extract()))
sender->release();
delete _monitorSendQueue;
while ((sender = _sendQueue->extract()))
sender->release();
delete _sendQueue;
delete _monitorSender;
delete _socketBuffer;
delete _sendBuffer;
delete _responseHandler;
_context->release();
}
// TODO consider epics::pvData::Thread
void BlockingTCPTransport::start() {
// TODO consuder epics::pvData::Thread
String threadName = "TCP-receive "+inetAddressToString(
_socketAddress);
// TODO this was in constructor
// add to registry
Transport::shared_pointer thisSharedPtr = shared_from_this();
_context->getTransportRegistry()->put(thisSharedPtr);
errlogSevPrintf(errlogInfo, "Starting thread: %s",
threadName.c_str());
String socketAddressString = inetAddressToString(_socketAddress);
//
// start receive thread
//
String threadName = "TCP-receive " + socketAddressString;
errlogSevPrintf(errlogInfo, "Starting thread: %s", threadName.c_str());
_rcvThreadId = epicsThreadCreate(threadName.c_str(),
epicsThreadPriorityMedium, epicsThreadGetStackSize(
epicsThreadStackMedium),
epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
BlockingTCPTransport::rcvThreadRunner, this);
threadName = "TCP-send "+inetAddressToString(_socketAddress);
//
// start send thread
//
errlogSevPrintf(errlogInfo, "Starting thread: %s",
threadName.c_str());
threadName = "TCP-send " + socketAddressString;
errlogSevPrintf(errlogInfo, "Starting thread: %s",threadName.c_str());
_sendThreadId = epicsThreadCreate(threadName.c_str(),
epicsThreadPriorityMedium, epicsThreadGetStackSize(
epicsThreadStackMedium),
epicsThreadPriorityMedium,
epicsThreadGetStackSize(epicsThreadStackMedium),
BlockingTCPTransport::sendThreadRunner, this);
}
void BlockingTCPTransport::clearAndReleaseBuffer() {
// 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()
-CA_MESSAGE_HEADER_SIZE;
_nextMarkerPosition -= _sendBuffer->getPosition() - CA_MESSAGE_HEADER_SIZE;
_sendQueueMutex.lock();
_flushRequested = false;
@@ -214,7 +223,8 @@ namespace epics {
_closed = true;
// remove from registry
_context->getTransportRegistry()->remove(this);
Transport::shared_pointer thisSharedPtr = shared_from_this();
_context->getTransportRegistry()->remove(thisSharedPtr);
// clean resources
internalClose(force);
@@ -239,33 +249,17 @@ namespace epics {
int sockBufSize;
socklen_t intLen = sizeof(int);
int retval = getsockopt(_channel, SOL_SOCKET, SO_RCVBUF,
&sockBufSize, &intLen);
if(retval<0) errlogSevPrintf(errlogMajor,
"Socket getsockopt SO_RCVBUF error: %s", strerror(errno));
int retval = getsockopt(_channel, SOL_SOCKET, SO_RCVBUF,&sockBufSize, &intLen);
if(retval<0)
errlogSevPrintf(errlogMajor,
"Socket getsockopt SO_RCVBUF error: %s",
strerror(errno));
return sockBufSize;
}
// TODO reimplement using Event
bool BlockingTCPTransport::waitUntilVerified(double timeout) {
double internalTimeout = timeout;
bool internalVerified = false;
_verifiedMutex.lock();
internalVerified = _verified;
_verifiedMutex.unlock();
while(!internalVerified&&internalTimeout>0) {
epicsThreadSleep(min(0.1, internalTimeout));
internalTimeout -= 0.1;
_verifiedMutex.lock();
internalVerified = _verified;
_verifiedMutex.unlock();
}
return internalVerified;
return _verifiedEvent.wait(timeout);
}
void BlockingTCPTransport::flush(bool lastMessageCompleted) {
@@ -291,15 +285,14 @@ namespace epics {
_lastSegmentedMessageCommand, 0);
}
void BlockingTCPTransport::startMessage(int8 command,
int ensureCapacity) {
void BlockingTCPTransport::startMessage(int8 command, int ensureCapacity) {
_lastMessageStartPosition = -1;
ensureBuffer(CA_MESSAGE_HEADER_SIZE+ensureCapacity);
_lastMessageStartPosition = _sendBuffer->getPosition();
_sendBuffer->putShort(CA_MAGIC_AND_VERSION);
_sendBuffer->putByte(_lastSegmentedMessageType); // data
_sendBuffer->putByte(command); // command
_sendBuffer->putInt(0); // temporary zero payload
_sendBuffer->putByte(command); // command
_sendBuffer->putInt(0); // temporary zero payload
}
@@ -586,16 +579,14 @@ namespace epics {
_socketBuffer->setLimit(min(_storedPosition+_storedPayloadSize, _storedLimit));
try {
// handle response
Transport::shared_pointer thisPointer = shared_from_this();
_responseHandler->handleResponse(&_socketAddress,
this, version, _command, _payloadSize,
thisPointer, version, _command, _payloadSize,
_socketBuffer);
} catch(...) {
//noop // TODO print?
}
/*
* Java finally start
*/
_socketBuffer->setLimit(_storedLimit);
int newPosition = _storedPosition+_storedPayloadSize;
if(newPosition>_storedLimit) {
@@ -606,9 +597,6 @@ namespace epics {
}
_socketBuffer->setPosition(newPosition);
// TODO discard all possible segments?!!!
/*
* Java finally end
*/
_stage = PROCESS_HEADER;
@@ -757,27 +745,25 @@ namespace epics {
return true;
}
TransportSender* BlockingTCPTransport::extractFromSendQueue() {
TransportSender* retval;
_sendQueueMutex.lock();
retval = _sendQueue->extract();
_sendQueueMutex.unlock();
return retval;
}
void BlockingTCPTransport::processSendQueue() {
// TODO sync _closed
while(!_closed) {
TransportSender* sender;
sender = extractFromSendQueue();
_sendQueueMutex.lock();
// TODO optimize
TransportSender::shared_pointer sender;
if (!_sendQueue.empty())
{
sender = _sendQueue.front();
_sendQueue.pop_front();
}
_sendQueueMutex.unlock();
// wait for new message
while(sender==NULL&&!_flushRequested&&!_closed) {
while(sender.get()==0&&!_flushRequested&&!_closed) {
if(_flushStrategy==DELAYED) {
if(_delay>0) epicsThreadSleep(_delay);
if(_sendQueue->size()==0) {
if(_sendQueue.empty()) {
// if (hasMonitors || sendBuffer.position() > CAConstants.CA_MESSAGE_HEADER_SIZE)
if(_sendBuffer->getPosition()>CA_MESSAGE_HEADER_SIZE)
_flushRequested = true;
@@ -787,7 +773,16 @@ namespace epics {
}
else
_sendQueueEvent.wait();
sender = extractFromSendQueue();
_sendQueueMutex.lock();
if (!_sendQueue.empty())
{
sender = _sendQueue.front();
_sendQueue.pop_front();
}
else
sender.reset();
_sendQueueMutex.unlock();
}
// always do flush from this thread
@@ -802,7 +797,7 @@ namespace epics {
flush();
}
if(sender!=NULL) {
if(sender.get()) {
sender->lock();
try {
_lastMessageStartPosition = _sendBuffer->getPosition();
@@ -820,7 +815,6 @@ namespace epics {
_sendBuffer->setPosition(_lastMessageStartPosition);
}
sender->unlock();
sender->release();
} // if(sender!=NULL)
} // while(!_closed)
}
@@ -843,6 +837,7 @@ namespace epics {
void BlockingTCPTransport::rcvThreadRunner(void* param) {
BlockingTCPTransport* obj = (BlockingTCPTransport*)param;
Transport::shared_pointer ptr = obj->shared_from_this(); // hold reference
try{
obj->processReadCached(false, NONE, CA_MESSAGE_HEADER_SIZE, false);
@@ -850,6 +845,7 @@ try{
printf("rcvThreadRunnner exception\n");
}
/*
if(obj->_autoDelete) {
while(true)
{
@@ -863,12 +859,16 @@ printf("rcvThreadRunnner exception\n");
}
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());
} catch (...) {
printf("sendThreadRunnner exception\n");
}
@@ -881,21 +881,21 @@ printf("sendThreadRunnner exception\n");
obj->_mutex.unlock();
}
void BlockingTCPTransport::enqueueSendRequest(TransportSender* sender) {
void BlockingTCPTransport::enqueueSendRequest(TransportSender::shared_pointer& sender) {
Lock lock(_sendQueueMutex);
if(_closed) return;
sender->acquire();
_sendQueue->insert(sender);
_sendQueue.push_back(sender);
_sendQueueEvent.signal();
}
void BlockingTCPTransport::enqueueMonitorSendRequest(TransportSender* sender) {
/*
void BlockingTCPTransport::enqueueMonitorSendRequest(TransportSender::shared_pointer sender) {
Lock lock(_monitorMutex);
if(_closed) return;
sender->acquire();
_monitorSendQueue->insert(sender);
if(_monitorSendQueue->size()==1) enqueueSendRequest(_monitorSender);
_monitorSendQueue.insert(sender);
if(_monitorSendQueue.size()==1) enqueueSendRequest(_monitorSender);
}
void MonitorSender::send(ByteBuffer* buffer, TransportSendControl* control) {
control->startMessage(19, 0);
@@ -911,13 +911,13 @@ printf("sendThreadRunnner exception\n");
if(sender==NULL) {
control->ensureBuffer(sizeof(int32));
buffer->putInt(CAJ_INVALID_IOID);
buffer->putInt(INVALID_IOID);
break;
}
sender->send(buffer, control);
sender->release();
}
}
*/
}
}