pvac user internal/external refs.
This commit is contained in:
+15
-4
@@ -27,7 +27,8 @@ Timeout::Timeout()
|
||||
:std::runtime_error("Timeout")
|
||||
{}
|
||||
|
||||
struct ClientChannel::Impl : public pva::ChannelRequester
|
||||
struct ClientChannel::Impl : public pva::ChannelRequester,
|
||||
public pvac::detail::wrapped_shared_from_this<ClientChannel::Impl>
|
||||
{
|
||||
epicsMutex mutex;
|
||||
pva::Channel::shared_pointer channel;
|
||||
@@ -40,6 +41,15 @@ struct ClientChannel::Impl : public pva::ChannelRequester
|
||||
Impl() {REFTRACE_INCREMENT(num_instances);}
|
||||
virtual ~Impl() {REFTRACE_DECREMENT(num_instances);}
|
||||
|
||||
// called automatically via wrapped_shared_from_this
|
||||
void cancel()
|
||||
{
|
||||
// ClientChannel destroy implicitly removes all callbacks,
|
||||
// but doesn't destroy the Channel or cancel Operations
|
||||
Guard G(mutex);
|
||||
listeners.clear();
|
||||
}
|
||||
|
||||
virtual std::string getRequesterName() OVERRIDE FINAL { return "ClientChannel::Impl"; }
|
||||
|
||||
virtual void channelCreated(const pvd::Status& status, pva::Channel::shared_pointer const & channel) OVERRIDE FINAL {}
|
||||
@@ -49,7 +59,7 @@ struct ClientChannel::Impl : public pva::ChannelRequester
|
||||
listeners_t notify;
|
||||
{
|
||||
Guard G(mutex);
|
||||
notify = listeners;
|
||||
notify = listeners; // copy vector
|
||||
}
|
||||
ConnectEvent evt;
|
||||
evt.connected = connectionState==pva::Channel::CONNECTED;
|
||||
@@ -103,13 +113,14 @@ void Operation::cancel()
|
||||
ClientChannel::ClientChannel(const std::tr1::shared_ptr<pva::ChannelProvider>& provider,
|
||||
const std::string& name,
|
||||
const Options& opt)
|
||||
:impl(new Impl)
|
||||
:impl(Impl::build())
|
||||
{
|
||||
if(name.empty())
|
||||
THROW_EXCEPTION2(std::logic_error, "empty channel name not allowed");
|
||||
if(!provider)
|
||||
THROW_EXCEPTION2(std::logic_error, "NULL ChannelProvider");
|
||||
impl->channel = provider->createChannel(name, impl, opt.priority, opt.address);
|
||||
impl->channel = provider->createChannel(name, impl->internal_shared_from_this(),
|
||||
opt.priority, opt.address);
|
||||
if(!impl->channel)
|
||||
throw std::runtime_error("ChannelProvider failed to create Channel");
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@ typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
namespace {
|
||||
|
||||
struct GetPutter : public pva::ChannelPutRequester,
|
||||
public pvac::Operation::Impl
|
||||
public pvac::Operation::Impl,
|
||||
public pvac::detail::wrapped_shared_from_this<GetPutter>
|
||||
{
|
||||
mutable epicsMutex mutex;
|
||||
|
||||
@@ -42,7 +43,7 @@ struct GetPutter : public pva::ChannelPutRequester,
|
||||
{REFTRACE_INCREMENT(num_instances);}
|
||||
GetPutter(pvac::ClientChannel::PutCallback* cb) :started(false), getcb(0), putcb(cb)
|
||||
{REFTRACE_INCREMENT(num_instances);}
|
||||
virtual ~GetPutter() {cancel();REFTRACE_DECREMENT(num_instances);}
|
||||
virtual ~GetPutter() {REFTRACE_DECREMENT(num_instances);}
|
||||
|
||||
void callEvent(Guard& G, pvac::GetEvent::event_t evt = pvac::GetEvent::Fail)
|
||||
{
|
||||
@@ -69,6 +70,7 @@ struct GetPutter : public pva::ChannelPutRequester,
|
||||
return op ? op->getChannel()->getChannelName() : "<dead>";
|
||||
}
|
||||
|
||||
// called automatically via wrapped_shared_from_this
|
||||
virtual void cancel() OVERRIDE FINAL
|
||||
{
|
||||
Guard G(mutex);
|
||||
@@ -191,11 +193,12 @@ ClientChannel::get(ClientChannel::GetCallback* cb,
|
||||
if(!pvRequest)
|
||||
pvRequest = pvd::createRequest("field()");
|
||||
|
||||
std::tr1::shared_ptr<GetPutter> ret(new GetPutter(cb));
|
||||
std::tr1::shared_ptr<GetPutter> ret(GetPutter::build(cb));
|
||||
|
||||
{
|
||||
Guard G(ret->mutex);
|
||||
ret->op = getChannel()->createChannelPut(ret, std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
|
||||
ret->op = getChannel()->createChannelPut(ret->internal_shared_from_this(),
|
||||
std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
|
||||
}
|
||||
|
||||
return Operation(ret);
|
||||
@@ -209,11 +212,12 @@ ClientChannel::put(PutCallback* cb,
|
||||
if(!pvRequest)
|
||||
pvRequest = pvd::createRequest("field()");
|
||||
|
||||
std::tr1::shared_ptr<GetPutter> ret(new GetPutter(cb));
|
||||
std::tr1::shared_ptr<GetPutter> ret(GetPutter::build(cb));
|
||||
|
||||
{
|
||||
Guard G(ret->mutex);
|
||||
ret->op = getChannel()->createChannelPut(ret, std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
|
||||
ret->op = getChannel()->createChannelPut(ret->internal_shared_from_this(),
|
||||
std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
|
||||
}
|
||||
|
||||
return Operation(ret);
|
||||
|
||||
@@ -23,7 +23,8 @@ typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
|
||||
namespace pvac {
|
||||
|
||||
struct Monitor::Impl : public pva::MonitorRequester
|
||||
struct Monitor::Impl : public pva::MonitorRequester,
|
||||
public pvac::detail::wrapped_shared_from_this<Monitor::Impl>
|
||||
{
|
||||
mutable epicsMutex mutex;
|
||||
pva::Channel::shared_pointer chan;
|
||||
@@ -43,7 +44,7 @@ struct Monitor::Impl : public pva::MonitorRequester
|
||||
,seenEmpty(false)
|
||||
,cb(cb)
|
||||
{REFTRACE_INCREMENT(num_instances);}
|
||||
virtual ~Impl() {cancel();REFTRACE_DECREMENT(num_instances);}
|
||||
virtual ~Impl() {REFTRACE_DECREMENT(num_instances);}
|
||||
|
||||
void callEvent(Guard& G, MonitorEvent::event_t evt = MonitorEvent::Fail)
|
||||
{
|
||||
@@ -78,6 +79,7 @@ struct Monitor::Impl : public pva::MonitorRequester
|
||||
}
|
||||
}
|
||||
|
||||
// called automatically via wrapped_shared_from_this
|
||||
void cancel()
|
||||
{
|
||||
operation_type::shared_pointer temp;
|
||||
@@ -218,12 +220,13 @@ ClientChannel::monitor(MonitorCallback *cb,
|
||||
if(!pvRequest)
|
||||
pvRequest = pvd::createRequest("field()");
|
||||
|
||||
std::tr1::shared_ptr<Monitor::Impl> ret(new Monitor::Impl(cb));
|
||||
std::tr1::shared_ptr<Monitor::Impl> ret(Monitor::Impl::build(cb));
|
||||
ret->chan = getChannel();
|
||||
|
||||
{
|
||||
Guard G(ret->mutex);
|
||||
ret->op = ret->chan->createMonitor(ret, std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
|
||||
ret->op = ret->chan->createMonitor(ret->internal_shared_from_this(),
|
||||
std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
|
||||
}
|
||||
|
||||
return Monitor(ret);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "pv/logger.h"
|
||||
#include "pva/client.h"
|
||||
#include "clientpvt.h"
|
||||
#include "pv/pvAccess.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
@@ -23,7 +23,8 @@ typedef epicsGuardRelease<epicsMutex> UnGuard;
|
||||
namespace {
|
||||
|
||||
struct RPCer : public pva::ChannelRPCRequester,
|
||||
public pvac::Operation::Impl
|
||||
public pvac::Operation::Impl,
|
||||
public pvac::detail::wrapped_shared_from_this<RPCer>
|
||||
{
|
||||
mutable epicsMutex mutex;
|
||||
|
||||
@@ -40,7 +41,7 @@ struct RPCer : public pva::ChannelRPCRequester,
|
||||
RPCer(pvac::ClientChannel::GetCallback* cb,
|
||||
const pvd::PVStructure::const_shared_pointer& args) :started(false), cb(cb), args(args)
|
||||
{REFTRACE_INCREMENT(num_instances);}
|
||||
virtual ~RPCer() {cancel();REFTRACE_DECREMENT(num_instances);}
|
||||
virtual ~RPCer() {REFTRACE_DECREMENT(num_instances);}
|
||||
|
||||
void callEvent(Guard& G, pvac::GetEvent::event_t evt = pvac::GetEvent::Fail)
|
||||
{
|
||||
@@ -79,6 +80,7 @@ struct RPCer : public pva::ChannelRPCRequester,
|
||||
return op ? op->getChannel()->getChannelName() : "<dead>";
|
||||
}
|
||||
|
||||
// called automatically via wrapped_shared_from_this
|
||||
virtual void cancel()
|
||||
{
|
||||
Guard G(mutex);
|
||||
@@ -155,11 +157,12 @@ ClientChannel::rpc(GetCallback* cb,
|
||||
if(!pvRequest)
|
||||
pvRequest = pvd::createRequest("field()");
|
||||
|
||||
std::tr1::shared_ptr<RPCer> ret(new RPCer(cb, arguments));
|
||||
std::tr1::shared_ptr<RPCer> ret(RPCer::build(cb, arguments));
|
||||
|
||||
{
|
||||
Guard G(ret->mutex);
|
||||
ret->op = getChannel()->createChannelRPC(ret, std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
|
||||
ret->op = getChannel()->createChannelRPC(ret->internal_shared_from_this(),
|
||||
std::tr1::const_pointer_cast<pvd::PVStructure>(pvRequest));
|
||||
}
|
||||
|
||||
return Operation(ret);
|
||||
|
||||
@@ -1,9 +1,68 @@
|
||||
#ifndef CLIENTPVT_H
|
||||
#define CLIENTPVT_H
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#include <pva/client.h>
|
||||
|
||||
namespace pvac{namespace detail{
|
||||
/* Like std::tr1::enable_shared_from_this
|
||||
* with the notion of internal vs. external references.
|
||||
* External references wrap an internal reference.
|
||||
* When the last external reference is dropped,
|
||||
* then Base::cancel() is called, but the object isn't free'd
|
||||
* until all internal references are dropped as well.
|
||||
*/
|
||||
template<typename Base>
|
||||
struct wrapped_shared_from_this {
|
||||
private:
|
||||
std::tr1::weak_ptr<Base> myselfptr;
|
||||
|
||||
struct canceller {
|
||||
std::tr1::shared_ptr<Base> ptr;
|
||||
canceller(const std::tr1::shared_ptr<Base>& ptr) :ptr(ptr) {}
|
||||
|
||||
void operator()(Base *) {
|
||||
std::tr1::shared_ptr<Base> P;
|
||||
P.swap(ptr);
|
||||
P->cancel();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
std::tr1::shared_ptr<Base> internal_shared_from_this() {
|
||||
std::tr1::shared_ptr<Base> ret(myselfptr);
|
||||
if(!ret)
|
||||
throw std::tr1::bad_weak_ptr();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static
|
||||
std::tr1::shared_ptr<Base> build() {
|
||||
std::tr1::shared_ptr<Base> inner(new Base),
|
||||
ret(inner.get(), canceller(inner));
|
||||
inner->myselfptr = inner;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename A>
|
||||
static
|
||||
std::tr1::shared_ptr<Base> build(A a) {
|
||||
std::tr1::shared_ptr<Base> inner(new Base(a)),
|
||||
ret(inner.get(), canceller(inner));
|
||||
inner->myselfptr = inner;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename A, typename B>
|
||||
static
|
||||
std::tr1::shared_ptr<Base> build(A a, B b) {
|
||||
std::tr1::shared_ptr<Base> inner(new Base(a, b)),
|
||||
ret(inner.get(), canceller(inner));
|
||||
inner->myselfptr = inner;
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
void registerRefTrack();
|
||||
void registerRefTrackGet();
|
||||
|
||||
Reference in New Issue
Block a user