Replaced 4 notification threads with 2 notifierConveyors

Channel connection notifications are now handled by connectNotifier,
getDone, putDone and monitor events handled by resultNotifier.
A notifierConveyor is generic, and contains a queue and a thread.
You pass a Notification pointing to a NotifierClient to a Conveyor's
notifyClient() method, and the thread will call the client's
notifyClient() method once it reaches the front of the queue.

The conveyor threads stop when the caProvider is destroyed.
The queue stores weak pointers, so queued notifications won't prevent
client objects from being destroyed.
This commit is contained in:
Andrew Johnson
2020-10-06 00:48:32 -05:00
committed by mdavidsaver
parent 2729903a10
commit 06c2fb579f
16 changed files with 213 additions and 833 deletions

View File

@ -11,10 +11,7 @@ LIB_SYS_LIBS_WIN32 += netapi32 ws2_32
INC += pv/caProvider.h
pvAccessCA_SRCS += channelConnectThread.cpp
pvAccessCA_SRCS += monitorEventThread.cpp
pvAccessCA_SRCS += getDoneThread.cpp
pvAccessCA_SRCS += putDoneThread.cpp
pvAccessCA_SRCS += notifierConveyor.cpp
pvAccessCA_SRCS += caProvider.cpp
pvAccessCA_SRCS += caChannel.cpp
pvAccessCA_SRCS += dbdToPv.cpp

View File

@ -5,17 +5,10 @@
*/
#include <epicsVersion.h>
#include <pv/standardField.h>
#include <pv/logger.h>
#include <pv/pvAccess.h>
#define epicsExportSharedSymbols
#include "channelConnectThread.h"
#include "monitorEventThread.h"
#include "getDoneThread.h"
#include "putDoneThread.h"
#include "caChannel.h"
using namespace epics::pvData;
@ -40,12 +33,14 @@ CAChannel::shared_pointer CAChannel::create(CAChannelProvider::shared_pointer co
return caChannel;
}
extern "C" {
static void ca_connection_handler(struct connection_handler_args args)
{
CAChannel *channel = static_cast<CAChannel*>(ca_puser(args.chid));
channel->connect(args.op == CA_OP_CONN_UP);
}
}
void CAChannel::connect(bool isConnected)
{
@ -55,7 +50,13 @@ void CAChannel::connect(bool isConnected)
}
CAChannelProviderPtr provider(channelProvider.lock());
if (!provider) return;
provider->getChannelConnectThread().channelConnected(notifyChannelRequester);
provider->notifyConnection(connectNotification);
}
void CAChannel::notifyResult(NotificationPtr const &notificationPtr) {
CAChannelProviderPtr provider(channelProvider.lock());
if (!provider) return;
provider->notifyResult(notificationPtr);
}
void CAChannel::notifyClient()
@ -109,7 +110,8 @@ CAChannel::CAChannel(std::string const & channelName,
channelRequester(channelRequester),
channelID(0),
channelCreated(false),
channelConnected(false)
channelConnected(false),
connectNotification(new Notification())
{
}
@ -117,8 +119,7 @@ void CAChannel::activate(short priority)
{
ChannelRequester::shared_pointer req(channelRequester.lock());
if (!req) return;
notifyChannelRequester = NotifyChannelRequesterPtr(new NotifyChannelRequester());
notifyChannelRequester->setChannel(shared_from_this());
connectNotification->setClient(shared_from_this());
attachContext();
int result = ca_create_channel(channelName.c_str(),
ca_connection_handler,
@ -161,10 +162,7 @@ void CAChannel::disconnectChannel()
}
monitorlist.resize(0);
/* Clear CA Channel */
CAChannelProviderPtr provider(channelProvider.lock());
if (provider) {
std::tr1::static_pointer_cast<CAChannelProvider>(provider)->attachContext();
}
attachContext();
int result = ca_clear_channel(channelID);
if (result == ECA_NORMAL) return;
string mess("CAChannel::disconnectChannel() ");
@ -367,7 +365,7 @@ void CAChannel::attachContext()
{
CAChannelProviderPtr provider(channelProvider.lock());
if (provider) {
std::tr1::static_pointer_cast<CAChannelProvider>(provider)->attachContext();
provider->attachContext();
return;
}
string mess("CAChannel::attachContext provider does not exist ");
@ -391,7 +389,7 @@ CAChannelGet::CAChannelGet(CAChannel::shared_pointer const & channel,
channelGetRequester(channelGetRequester),
pvRequest(pvRequest),
getStatus(Status::Ok),
getDoneThread(GetDoneThread::get())
getNotification(new Notification())
{}
CAChannelGet::~CAChannelGet()
@ -406,8 +404,7 @@ void CAChannelGet::activate()
dbdToPv->getChoices(channel);
pvStructure = dbdToPv->createPVStructure();
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
notifyGetRequester = NotifyGetRequesterPtr(new NotifyGetRequester());
notifyGetRequester->setChannelGet(shared_from_this());
getNotification->setClient(shared_from_this());
EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(),
pvStructure->getStructure()));
}
@ -419,7 +416,7 @@ std::string CAChannelGet::getRequesterName()
return "CAChannelGet";
}
namespace {
extern "C" {
static void ca_get_handler(struct event_handler_args args)
{
@ -427,14 +424,14 @@ static void ca_get_handler(struct event_handler_args args)
channelGet->getDone(args);
}
} // namespace
} // extern "C"
void CAChannelGet::getDone(struct event_handler_args &args)
{
ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock());
if (!getRequester) return;
getStatus = dbdToPv->getFromDBD(pvStructure,bitSet,args);
getDoneThread->getDone(notifyGetRequester);
channel->notifyResult(getNotification);
}
void CAChannelGet::notifyClient()
@ -495,7 +492,7 @@ CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & channel,
isPut(false),
getStatus(Status::Ok),
putStatus(Status::Ok),
putDoneThread(PutDoneThread::get())
putNotification(new Notification())
{}
CAChannelPut::~CAChannelPut()
@ -517,8 +514,7 @@ void CAChannelPut::activate()
if (val.compare("true")==0)
block = true;
}
notifyPutRequester = NotifyPutRequesterPtr(new NotifyPutRequester());
notifyPutRequester->setChannelPut(shared_from_this());
putNotification->setClient(shared_from_this());
EXCEPTION_GUARD(putRequester->channelPutConnect(Status::Ok, shared_from_this(),
pvStructure->getStructure()));
}
@ -531,7 +527,7 @@ std::string CAChannelPut::getRequesterName()
/* --------------- epics::pvAccess::ChannelPut --------------- */
namespace {
extern "C" {
static void ca_put_handler(struct event_handler_args args)
{
@ -545,7 +541,7 @@ static void ca_put_get_handler(struct event_handler_args args)
channelPut->getDone(args);
}
} // namespace
} // extern "C"
void CAChannelPut::put(PVStructure::shared_pointer const & pvPutStructure,
@ -574,7 +570,7 @@ void CAChannelPut::putDone(struct event_handler_args &args)
else {
putStatus = Status::Ok;
}
putDoneThread->putDone(notifyPutRequester);
channel->notifyResult(putNotification);
}
void CAChannelPut::getDone(struct event_handler_args &args)
@ -582,7 +578,7 @@ void CAChannelPut::getDone(struct event_handler_args &args)
ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock());
if (!putRequester) return;
getStatus = dbdToPv->getFromDBD(pvStructure,bitSet,args);
putDoneThread->putDone(notifyPutRequester);
channel->notifyResult(putNotification);
}
void CAChannelPut::notifyClient()
@ -739,9 +735,9 @@ CAChannelMonitor::CAChannelMonitor(
monitorRequester(monitorRequester),
pvRequest(pvRequest),
isStarted(false),
monitorEventThread(MonitorEventThread::get()),
pevid(NULL),
eventMask(DBE_VALUE | DBE_ALARM)
eventMask(DBE_VALUE | DBE_ALARM),
eventNotification(new Notification())
{}
CAChannelMonitor::~CAChannelMonitor()
@ -778,8 +774,7 @@ void CAChannelMonitor::activate()
if (value.find("PROPERTY")!=std::string::npos) eventMask|=DBE_PROPERTY;
}
}
notifyMonitorRequester = NotifyMonitorRequesterPtr(new NotifyMonitorRequester());
notifyMonitorRequester->setChannelMonitor(shared_from_this());
eventNotification->setClient(shared_from_this());
monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize));
EXCEPTION_GUARD(requester->monitorConnect(Status::Ok, shared_from_this(),
pvStructure->getStructure()));
@ -807,7 +802,7 @@ void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args)
else {
*(activeElement->overrunBitSet) |= *(activeElement->changedBitSet);
}
monitorEventThread->event(notifyMonitorRequester);
channel->notifyResult(eventNotification);
}
else {
string mess("CAChannelMonitor::subscriptionEvent ");

View File

@ -15,12 +15,11 @@
#include <queue>
#include <vector>
#include <cadef.h>
#include <pv/pvAccess.h>
#include <pv/event.h>
/* for CA */
#include <cadef.h>
#include "caProviderPvt.h"
#include "dbdToPv.h"
@ -31,28 +30,6 @@ namespace ca {
class CAChannel;
typedef std::tr1::shared_ptr<CAChannel> CAChannelPtr;
typedef std::tr1::weak_ptr<CAChannel> CAChannelWPtr;
class ChannelConnectThread;
typedef std::tr1::shared_ptr<ChannelConnectThread> ChannelConnectThreadPtr;
class NotifyChannelRequester;
typedef std::tr1::shared_ptr<NotifyChannelRequester> NotifyChannelRequesterPtr;
class NotifyMonitorRequester;
typedef std::tr1::shared_ptr<NotifyMonitorRequester> NotifyMonitorRequesterPtr;
class MonitorEventThread;
typedef std::tr1::shared_ptr<MonitorEventThread> MonitorEventThreadPtr;
class NotifyGetRequester;
typedef std::tr1::shared_ptr<NotifyGetRequester> NotifyGetRequesterPtr;
typedef std::tr1::weak_ptr<NotifyGetRequester> NotifyGetRequesterWPtr;
class GetDoneThread;
typedef std::tr1::shared_ptr<GetDoneThread> GetDoneThreadPtr;
class NotifyPutRequester;
typedef std::tr1::shared_ptr<NotifyPutRequester> NotifyPutRequesterPtr;
typedef std::tr1::weak_ptr<NotifyPutRequester> NotifyPutRequesterWPtr;
class PutDoneThread;
typedef std::tr1::shared_ptr<PutDoneThread> PutDoneThreadPtr;
class CAChannelGetField;
typedef std::tr1::shared_ptr<CAChannelGetField> CAChannelGetFieldPtr;
@ -85,8 +62,10 @@ private:
std::string subField;
};
class CAChannel :
public Channel,
public NotifierClient,
public std::tr1::enable_shared_from_this<CAChannel>
{
public:
@ -120,7 +99,9 @@ public:
void attachContext();
void disconnectChannel();
void connect(bool isConnected);
void notifyClient();
virtual void notifyClient();
void notifyResult(NotificationPtr const &notificationPtr);
private:
virtual void destroy() {}
CAChannel(std::string const & channelName,
@ -135,7 +116,7 @@ private:
chid channelID;
bool channelCreated;
bool channelConnected;
NotifyChannelRequesterPtr notifyChannelRequester;
NotificationPtr connectNotification;
epics::pvData::Mutex requestsMutex;
std::queue<CAChannelGetFieldPtr> getFieldQueue;
@ -148,11 +129,12 @@ private:
class CAChannelGet :
public ChannelGet,
public NotifierClient,
public std::tr1::enable_shared_from_this<CAChannelGet>
{
public:
POINTER_DEFINITIONS(CAChannelGet);
static CAChannelGet::shared_pointer create(CAChannel::shared_pointer const & channel,
static CAChannelGetPtr create(CAChannel::shared_pointer const & channel,
ChannelGetRequester::shared_pointer const & channelGetRequester,
epics::pvData::PVStructurePtr const & pvRequest);
virtual ~CAChannelGet();
@ -164,7 +146,7 @@ public:
virtual std::string getRequesterName();
void activate();
void notifyClient();
virtual void notifyClient();
private:
virtual void destroy() {}
CAChannelGet(CAChannel::shared_pointer const & _channel,
@ -175,8 +157,7 @@ private:
ChannelGetRequester::weak_pointer channelGetRequester;
const epics::pvData::PVStructure::shared_pointer pvRequest;
epics::pvData::Status getStatus;
GetDoneThreadPtr getDoneThread;
NotifyGetRequesterPtr notifyGetRequester;
NotificationPtr getNotification;
DbdToPvPtr dbdToPv;
epics::pvData::Mutex mutex;
epics::pvData::PVStructure::shared_pointer pvStructure;
@ -185,9 +166,9 @@ private:
class CAChannelPut :
public ChannelPut,
public NotifierClient,
public std::tr1::enable_shared_from_this<CAChannelPut>
{
public:
POINTER_DEFINITIONS(CAChannelPut);
static CAChannelPut::shared_pointer create(CAChannel::shared_pointer const & channel,
@ -207,7 +188,7 @@ public:
virtual std::string getRequesterName();
void activate();
void notifyClient();
virtual void notifyClient();
private:
virtual void destroy() {}
CAChannelPut(CAChannel::shared_pointer const & _channel,
@ -220,8 +201,7 @@ private:
bool isPut;
epics::pvData::Status getStatus;
epics::pvData::Status putStatus;
PutDoneThreadPtr putDoneThread;
NotifyPutRequesterPtr notifyPutRequester;
NotificationPtr putNotification;
DbdToPvPtr dbdToPv;
epics::pvData::Mutex mutex;
epics::pvData::PVStructure::shared_pointer pvStructure;
@ -233,9 +213,9 @@ typedef std::tr1::shared_ptr<CACMonitorQueue> CACMonitorQueuePtr;
class CAChannelMonitor :
public Monitor,
public NotifierClient,
public std::tr1::enable_shared_from_this<CAChannelMonitor>
{
public:
POINTER_DEFINITIONS(CAChannelMonitor);
static CAChannelMonitor::shared_pointer create(CAChannel::shared_pointer const & channel,
@ -251,7 +231,7 @@ public:
virtual void cancel();
virtual std::string getRequesterName();
void activate();
void notifyClient();
virtual void notifyClient();
private:
virtual void destroy() {}
CAChannelMonitor(CAChannel::shared_pointer const & _channel,
@ -261,10 +241,9 @@ private:
MonitorRequester::weak_pointer monitorRequester;
const epics::pvData::PVStructure::shared_pointer pvRequest;
bool isStarted;
MonitorEventThreadPtr monitorEventThread;
evid pevid;
unsigned long eventMask;
NotifyMonitorRequesterPtr notifyMonitorRequester;
NotificationPtr eventNotification;
DbdToPvPtr dbdToPv;
epics::pvData::Mutex mutex;

View File

@ -6,14 +6,11 @@
#include <cadef.h>
#include <epicsSignal.h>
#include <epicsThread.h>
#include <epicsExit.h>
#include <pv/logger.h>
#include <pv/pvAccess.h>
#define epicsExportSharedSymbols
#include <pv/caProvider.h>
#include "caProviderPvt.h"
#include "caChannel.h"
@ -30,12 +27,10 @@ CAChannelProvider::CAChannelProvider()
}
CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr<Configuration> &)
: current_context(0),
monitorEventThread(MonitorEventThread::get()),
getDoneThread(GetDoneThread::get()),
putDoneThread(PutDoneThread::get())
: current_context(0)
{
channelConnectThread.start();
connectNotifier.start();
resultNotifier.start();
initialize();
}
@ -60,11 +55,6 @@ CAChannelProvider::~CAChannelProvider()
ca_context_destroy();
}
ChannelConnectThread & CAChannelProvider::getChannelConnectThread()
{
return channelConnectThread;
}
std::string CAChannelProvider::getProviderName()
{
return "ca";

View File

@ -14,13 +14,11 @@
#include <cadef.h>
#include <pv/caProvider.h>
#include <pv/logger.h>
#include <pv/pvAccess.h>
#include "channelConnectThread.h"
#include "monitorEventThread.h"
#include "getDoneThread.h"
#include "putDoneThread.h"
#include <pv/caProvider.h>
#include "notifierConveyor.h"
namespace epics {
@ -55,8 +53,6 @@ public:
CAChannelProvider(const std::tr1::shared_ptr<Configuration>&);
virtual ~CAChannelProvider();
ChannelConnectThread& getChannelConnectThread();
/* --------------- epics::pvAccess::ChannelProvider --------------- */
virtual std::string getProviderName();
@ -85,17 +81,21 @@ public:
void attachContext();
void addChannel(const CAChannelPtr & channel);
void notifyConnection(NotificationPtr const &notificationPtr) {
connectNotifier.notifyClient(notificationPtr);
}
void notifyResult(NotificationPtr const &notificationPtr) {
resultNotifier.notifyClient(notificationPtr);
}
private:
virtual void destroy() EPICS_DEPRECATED {}
void initialize();
ca_client_context* current_context;
epics::pvData::Mutex channelListMutex;
std::vector<CAChannelWPtr> caChannelList;
ChannelConnectThread channelConnectThread;
MonitorEventThreadPtr monitorEventThread;
GetDoneThreadPtr getDoneThread;
PutDoneThreadPtr putDoneThread;
NotifierConveyor connectNotifier;
NotifierConveyor resultNotifier;
};
}}}

View File

@ -1,93 +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.
*/
/**
* @author mrk
* @date 2018.07
*/
#include <epicsExit.h>
#define epicsExportSharedSymbols
#include "channelConnectThread.h"
#include "caChannel.h"
using namespace epics::pvData;
using namespace std;
namespace epics {
namespace pvAccess {
namespace ca {
ChannelConnectThread::ChannelConnectThread()
: isStop(false)
{
}
ChannelConnectThread::~ChannelConnectThread()
{
{
Lock the(mutex);
isStop = true;
}
workToDo.signal();
thread->exitWait();
}
void ChannelConnectThread::start()
{
if (thread) return;
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(
*this,
"channelConnectThread",
epicsThreadGetStackSize(epicsThreadStackSmall),
epicsThreadPriorityLow));
thread->start();
}
void ChannelConnectThread::channelConnected(
NotifyChannelRequesterPtr const &notifyChannelRequester)
{
{
Lock the(mutex);
if (notifyChannelRequester->isOnQueue) return;
notifyChannelRequester->isOnQueue = true;
notifyChannelQueue.push(notifyChannelRequester);
}
workToDo.signal();
}
void ChannelConnectThread::run()
{
do {
workToDo.wait();
while (true) {
bool more = false;
NotifyChannelRequester* notifyChannelRequester(NULL);
{
Lock the(mutex);
if (!notifyChannelQueue.empty())
{
more = true;
NotifyChannelRequesterWPtr req(notifyChannelQueue.front());
notifyChannelQueue.pop();
NotifyChannelRequesterPtr reqPtr(req.lock());
if (reqPtr) {
notifyChannelRequester = reqPtr.get();
reqPtr->isOnQueue = false;
}
}
}
if (!more) break;
if (notifyChannelRequester)
{
CAChannelPtr channel(notifyChannelRequester->channel.lock());
if (channel) channel->notifyClient();
}
}
} while (!stopping());
}
}}}

View File

@ -1,73 +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.
*/
/**
* @author mrk
* @date 2018.07
*/
#ifndef ChannelConnectThread_H
#define ChannelConnectThread_H
#include <queue>
#include <cadef.h>
#include <shareLib.h>
#include <epicsThread.h>
#include <pv/event.h>
#include <pv/lock.h>
#include <pv/pvAccess.h>
namespace epics {
namespace pvAccess {
namespace ca {
class NotifyChannelRequester;
typedef std::tr1::shared_ptr<NotifyChannelRequester> NotifyChannelRequesterPtr;
typedef std::tr1::weak_ptr<NotifyChannelRequester> NotifyChannelRequesterWPtr;
class ChannelConnectThread;
typedef std::tr1::shared_ptr<ChannelConnectThread> ChannelConnectThreadPtr;
class CAChannel;
typedef std::tr1::shared_ptr<CAChannel> CAChannelPtr;
typedef std::tr1::weak_ptr<CAChannel> CAChannelWPtr;
class NotifyChannelRequester
{
public:
ChannelRequester::weak_pointer channelRequester;
CAChannelWPtr channel;
bool isOnQueue;
NotifyChannelRequester() : isOnQueue(false) {}
void setChannel(CAChannelPtr const &channel)
{ this->channel = channel;}
};
class ChannelConnectThread :
public epicsThreadRunable
{
public:
ChannelConnectThread();
~ChannelConnectThread();
virtual void run();
void start();
void stop();
void channelConnected(NotifyChannelRequesterPtr const &notifyChannelRequester);
private:
bool stopping() {
pvData::Lock the(mutex);
return isStop;
}
bool isStop;
std::tr1::shared_ptr<epicsThread> thread;
epics::pvData::Mutex mutex;
epics::pvData::Event workToDo;
std::queue<NotifyChannelRequesterWPtr> notifyChannelQueue;
};
}}}
#endif /* ChannelConnectThread_H */

View File

@ -1,110 +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.
*/
/**
* @author mrk
* @date 2018.07
*/
#include "caChannel.h"
#include <epicsExit.h>
#define epicsExportSharedSymbols
#include "getDoneThread.h"
using namespace epics::pvData;
using namespace std;
namespace epics {
namespace pvAccess {
namespace ca {
GetDoneThreadPtr GetDoneThread::get()
{
static GetDoneThreadPtr master;
static Mutex mutex;
Lock xx(mutex);
if(!master) {
master = GetDoneThreadPtr(new GetDoneThread());
master->start();
}
return master;
}
GetDoneThread::GetDoneThread()
: isStop(false)
{
}
GetDoneThread::~GetDoneThread()
{
{
Lock xx(mutex);
isStop = true;
}
waitForCommand.signal();
waitForStop.wait();
}
void GetDoneThread::start()
{
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(
*this,
"getDoneThread",
epicsThreadGetStackSize(epicsThreadStackBig),
epicsThreadPriorityLow));
thread->start();
}
void GetDoneThread::stop()
{
}
void GetDoneThread::getDone(NotifyGetRequesterPtr const &notifyGetRequester)
{
{
Lock lock(mutex);
if(notifyGetRequester->isOnQueue) return;
notifyGetRequester->isOnQueue = true;
notifyGetQueue.push(notifyGetRequester);
}
waitForCommand.signal();
}
void GetDoneThread::run()
{
while (true) {
waitForCommand.wait();
while (true) {
bool more = false;
NotifyGetRequester* notifyGetRequester(NULL);
{
Lock lock(mutex);
if (!notifyGetQueue.empty()) {
more = true;
NotifyGetRequesterWPtr req(notifyGetQueue.front());
notifyGetQueue.pop();
NotifyGetRequesterPtr reqPtr(req.lock());
if (reqPtr) {
notifyGetRequester = reqPtr.get();
reqPtr->isOnQueue = false;
}
}
}
if (!more) break;
if (notifyGetRequester!=NULL) {
CAChannelGetPtr channelGet(notifyGetRequester->channelGet.lock());
if (channelGet) channelGet->notifyClient();
}
}
if (stopping()) {
waitForStop.signal();
break;
}
}
}
}}}

View File

@ -1,75 +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.
*/
/**
* @author mrk
* @date 2018.07
*/
#ifndef GetDoneThread_H
#define GetDoneThread_H
#include <queue>
#include <cadef.h>
#include <shareLib.h>
#include <epicsThread.h>
#include <pv/event.h>
#include <pv/lock.h>
namespace epics {
namespace pvAccess {
namespace ca {
class NotifyGetRequester;
typedef std::tr1::shared_ptr<NotifyGetRequester> NotifyGetRequesterPtr;
typedef std::tr1::weak_ptr<NotifyGetRequester> NotifyGetRequesterWPtr;
class GetDoneThread;
typedef std::tr1::shared_ptr<GetDoneThread> GetDoneThreadPtr;
class CAChannelGet;
typedef std::tr1::shared_ptr<CAChannelGet> CAChannelGetPtr;
typedef std::tr1::weak_ptr<CAChannelGet> CAChannelGetWPtr;
class NotifyGetRequester
{
public:
ChannelGetRequester::weak_pointer channelGetRequester;
CAChannelGetWPtr channelGet;
bool isOnQueue;
NotifyGetRequester() : isOnQueue(false) {}
void setChannelGet(CAChannelGetPtr const &channelGet)
{ this->channelGet = channelGet;}
};
class GetDoneThread :
public epicsThreadRunable
{
public:
static GetDoneThreadPtr get();
~GetDoneThread();
virtual void run();
void start();
void stop();
void getDone(NotifyGetRequesterPtr const &notifyGetRequester);
private:
GetDoneThread();
bool stopping() {
pvData::Lock the(mutex);
return isStop;
}
bool isStop;
std::tr1::shared_ptr<epicsThread> thread;
epics::pvData::Mutex mutex;
epics::pvData::Event waitForCommand;
epics::pvData::Event waitForStop;
std::queue<NotifyGetRequesterWPtr> notifyGetQueue;
};
}}}
#endif /* GetDoneThread_H */

View File

@ -1,110 +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.
*/
/**
* @author mrk
* @date 2018.06
*/
#include "caChannel.h"
#include <epicsExit.h>
#define epicsExportSharedSymbols
#include "monitorEventThread.h"
using namespace epics::pvData;
using namespace std;
namespace epics {
namespace pvAccess {
namespace ca {
MonitorEventThreadPtr MonitorEventThread::get()
{
static MonitorEventThreadPtr master;
static Mutex mutex;
Lock xx(mutex);
if(!master) {
master = MonitorEventThreadPtr(new MonitorEventThread());
master->start();
}
return master;
}
MonitorEventThread::MonitorEventThread()
: isStop(false)
{
}
MonitorEventThread::~MonitorEventThread()
{
{
Lock xx(mutex);
isStop = true;
}
waitForCommand.signal();
waitForStop.wait();
}
void MonitorEventThread::start()
{
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(
*this,
"monitorEventThread",
epicsThreadGetStackSize(epicsThreadStackBig),
epicsThreadPriorityLow));
thread->start();
}
void MonitorEventThread::stop()
{
}
void MonitorEventThread::event(NotifyMonitorRequesterPtr const &notifyMonitorRequester)
{
{
Lock lock(mutex);
if(notifyMonitorRequester->isOnQueue) return;
notifyMonitorRequester->isOnQueue = true;
notifyMonitorQueue.push(notifyMonitorRequester);
}
waitForCommand.signal();
}
void MonitorEventThread::run()
{
while (true) {
waitForCommand.wait();
while (true) {
bool more = false;
NotifyMonitorRequester* notifyMonitorRequester(NULL);
{
Lock lock(mutex);
if (!notifyMonitorQueue.empty())
{
more = true;
NotifyMonitorRequesterWPtr req(notifyMonitorQueue.front());
notifyMonitorQueue.pop();
NotifyMonitorRequesterPtr reqPtr(req.lock());
if (reqPtr) {
notifyMonitorRequester = reqPtr.get();
reqPtr->isOnQueue = false;
}
}
}
if (!more) break;
if (notifyMonitorRequester!=NULL) {
CAChannelMonitorPtr channelMonitor(notifyMonitorRequester->channelMonitor.lock());
if (channelMonitor) channelMonitor->notifyClient();
}
}
if (stopping()) {
waitForStop.signal();
break;
}
}
}
}}}

View File

@ -1,75 +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.
*/
/**
* @author mrk
* @date 2018.06
*/
#ifndef MonitorEventThread_H
#define MonitorEventThread_H
#include <queue>
#include <cadef.h>
#include <shareLib.h>
#include <epicsThread.h>
#include <pv/event.h>
#include <pv/lock.h>
namespace epics {
namespace pvAccess {
namespace ca {
class NotifyMonitorRequester;
typedef std::tr1::shared_ptr<NotifyMonitorRequester> NotifyMonitorRequesterPtr;
typedef std::tr1::weak_ptr<NotifyMonitorRequester> NotifyMonitorRequesterWPtr;
class MonitorEventThread;
typedef std::tr1::shared_ptr<MonitorEventThread> MonitorEventThreadPtr;
class CAChannelMonitor;
typedef std::tr1::shared_ptr<CAChannelMonitor> CAChannelMonitorPtr;
typedef std::tr1::weak_ptr<CAChannelMonitor> CAChannelMonitorWPtr;
class NotifyMonitorRequester
{
public:
MonitorRequester::weak_pointer monitorRequester;
CAChannelMonitorWPtr channelMonitor;
bool isOnQueue;
NotifyMonitorRequester() : isOnQueue(false) {}
void setChannelMonitor(CAChannelMonitorPtr const &channelMonitor)
{ this->channelMonitor = channelMonitor;}
};
class MonitorEventThread :
public epicsThreadRunable
{
public:
static MonitorEventThreadPtr get();
~MonitorEventThread();
virtual void run();
void start();
void stop();
void event(NotifyMonitorRequesterPtr const &notifyMonitorRequester);
private:
MonitorEventThread();
bool stopping() {
pvData::Lock the(mutex);
return isStop;
}
bool isStop;
std::tr1::shared_ptr<epicsThread> thread;
epics::pvData::Mutex mutex;
epics::pvData::Event waitForCommand;
epics::pvData::Event waitForStop;
std::queue<NotifyMonitorRequesterWPtr> notifyMonitorQueue;
};
}}}
#endif /* MonitorEventThread_H */

View File

@ -0,0 +1,79 @@
/**
* 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 "notifierConveyor.h"
using epics::pvData::Lock;
namespace epics {
namespace pvAccess {
namespace ca {
NotifierConveyor::~NotifierConveyor()
{
if (thread) {
{
Lock the(mutex);
halt = true;
}
workToDo.signal();
thread->exitWait();
}
}
void NotifierConveyor::start()
{
if (thread) return;
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(*this,
"caProvider::clientNotifier",
epicsThreadGetStackSize(epicsThreadStackBig),
epicsThreadPriorityLow));
thread->start();
}
void NotifierConveyor::notifyClient(
NotificationPtr const &notificationPtr)
{
{
Lock the(mutex);
if (halt || notificationPtr->queued) return;
notificationPtr->queued = true;
workQueue.push(notificationPtr);
}
workToDo.signal();
}
void NotifierConveyor::run()
{
bool stopping;
do {
workToDo.wait();
Lock the(mutex);
stopping = halt;
bool work = !workQueue.empty();
while (work)
{
NotificationWPtr notificationWPtr(workQueue.front());
workQueue.pop();
work = !workQueue.empty();
NotificationPtr notification(notificationWPtr.lock());
if (notification) {
notification->queued = false;
NotifierClientPtr client(notification->client.lock());
if (client) {
the.unlock();
client->notifyClient();
if (work) {
the.lock();
stopping = halt;
}
}
}
}
} while (!stopping);
}
}}}

66
src/ca/notifierConveyor.h Normal file
View File

@ -0,0 +1,66 @@
/**
* 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 INC_notifierConveyor_H
#define INC_notifierConveyor_H
#include <queue>
#include <epicsThread.h>
#include <pv/event.h>
#include <pv/lock.h>
namespace epics {
namespace pvAccess {
namespace ca {
class Notification;
typedef std::tr1::shared_ptr<Notification> NotificationPtr;
typedef std::tr1::weak_ptr<Notification> NotificationWPtr;
class NotifierClient;
typedef std::tr1::shared_ptr<NotifierClient> NotifierClientPtr;
typedef std::tr1::weak_ptr<NotifierClient> NotifierClientWPtr;
class NotifierClient
{
public:
virtual void notifyClient() = 0;
};
class Notification
{
public:
Notification() : queued(false) {}
void setClient(NotifierClientPtr const &client) {
this->client = client;
}
private:
NotifierClientWPtr client;
bool queued;
friend class NotifierConveyor;
};
class NotifierConveyor :
public epicsThreadRunable
{
public:
NotifierConveyor() : halt(false) {}
~NotifierConveyor();
virtual void run();
void start();
void notifyClient(NotificationPtr const &notificationPtr);
private:
std::tr1::shared_ptr<epicsThread> thread;
epics::pvData::Mutex mutex;
epics::pvData::Event workToDo;
std::queue<NotificationWPtr> workQueue;
bool halt;
};
}}}
#endif /* INC_notifierConveyor_H */

View File

@ -1,110 +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.
*/
/**
* @author mrk
* @date 2018.07
*/
#include "caChannel.h"
#include <epicsExit.h>
#define epicsExportSharedSymbols
#include "putDoneThread.h"
using namespace epics::pvData;
using namespace std;
namespace epics {
namespace pvAccess {
namespace ca {
PutDoneThreadPtr PutDoneThread::get()
{
static PutDoneThreadPtr master;
static Mutex mutex;
Lock xx(mutex);
if(!master) {
master = PutDoneThreadPtr(new PutDoneThread());
master->start();
}
return master;
}
PutDoneThread::PutDoneThread()
: isStop(false)
{
}
PutDoneThread::~PutDoneThread()
{
{
Lock xx(mutex);
isStop = true;
}
waitForCommand.signal();
waitForStop.wait();
}
void PutDoneThread::start()
{
thread = std::tr1::shared_ptr<epicsThread>(new epicsThread(
*this,
"putDoneThread",
epicsThreadGetStackSize(epicsThreadStackBig),
epicsThreadPriorityLow));
thread->start();
}
void PutDoneThread::stop()
{
}
void PutDoneThread::putDone(NotifyPutRequesterPtr const &notifyPutRequester)
{
{
Lock lock(mutex);
if(notifyPutRequester->isOnQueue) return;
notifyPutRequester->isOnQueue = true;
notifyPutQueue.push(notifyPutRequester);
}
waitForCommand.signal();
}
void PutDoneThread::run()
{
while (true) {
waitForCommand.wait();
while (true) {
bool more = false;
NotifyPutRequester* notifyPutRequester(NULL);
{
Lock lock(mutex);
if (!notifyPutQueue.empty()) {
more = true;
NotifyPutRequesterWPtr req(notifyPutQueue.front());
notifyPutQueue.pop();
NotifyPutRequesterPtr reqPtr(req.lock());
if (reqPtr) {
notifyPutRequester = reqPtr.get();
reqPtr->isOnQueue = false;
}
}
}
if (!more) break;
if (notifyPutRequester!=NULL) {
CAChannelPutPtr channelPut(notifyPutRequester->channelPut.lock());
if (channelPut) channelPut->notifyClient();
}
}
if (stopping()) {
waitForStop.signal();
break;
}
}
}
}}}

View File

@ -1,75 +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.
*/
/**
* @author mrk
* @date 2018.07
*/
#ifndef PutDoneThread_H
#define PutDoneThread_H
#include <queue>
#include <cadef.h>
#include <shareLib.h>
#include <epicsThread.h>
#include <pv/event.h>
#include <pv/lock.h>
namespace epics {
namespace pvAccess {
namespace ca {
class NotifyPutRequester;
typedef std::tr1::shared_ptr<NotifyPutRequester> NotifyPutRequesterPtr;
typedef std::tr1::weak_ptr<NotifyPutRequester> NotifyPutRequesterWPtr;
class PutDoneThread;
typedef std::tr1::shared_ptr<PutDoneThread> PutDoneThreadPtr;
class CAChannelPut;
typedef std::tr1::shared_ptr<CAChannelPut> CAChannelPutPtr;
typedef std::tr1::weak_ptr<CAChannelPut> CAChannelPutWPtr;
class NotifyPutRequester
{
public:
ChannelPutRequester::weak_pointer channelPutRequester;
CAChannelPutWPtr channelPut;
bool isOnQueue;
NotifyPutRequester() : isOnQueue(false) {}
void setChannelPut(CAChannelPutPtr const &channelPut)
{ this->channelPut = channelPut;}
};
class PutDoneThread :
public epicsThreadRunable
{
public:
static PutDoneThreadPtr get();
~PutDoneThread();
virtual void run();
void start();
void stop();
void putDone(NotifyPutRequesterPtr const &notifyPutRequester);
private:
PutDoneThread();
bool stopping() {
pvData::Lock the(mutex);
return isStop;
}
bool isStop;
std::tr1::shared_ptr<epicsThread> thread;
epics::pvData::Mutex mutex;
epics::pvData::Event waitForCommand;
epics::pvData::Event waitForStop;
std::queue<NotifyPutRequesterWPtr> notifyPutQueue;
};
}}}
#endif /* PutDoneThread_H */

View File

@ -13,17 +13,15 @@
#include <shareLib.h>
#include <pv/pvAccess.h>
struct ca_client_context;
namespace epics {
namespace pvAccess {
namespace ca {
/**
* @brief CAClientFactory is a channel provider for the ca network provider.
* @brief CAClientFactory registers a channel provider for operations over the
* CA network protocol.
*
* A single instance is created the first time CAClientFactory::start is called.
* epicsAtExit is used to destroy the instance.
*
* The single instance calls:
* ca_context_create(ca_enable_preemptive_callback);
@ -31,11 +29,8 @@ namespace ca {
* The thread that calls start, or a ca auxillary thread, are the only threads
* that can call the ca_* functions.
*
* NOTE: callbacks for monitor, get, and put are made from a separate thread.
* This is done to prevent a deadly embrace that can occur
* when rapid gets, puts, and monitor events are happening.
* The callbacks should not call any pvAccess method.
* If any such call is made the separate thread becomes a ca auxillary thread.
* NOTE: Notifications for connection changes and monitor, get, and put events
* are made from separate threads to prevent deadlocks.
*
*/
class epicsShareClass CAClientFactory