From 6055dbb983899b54ed072e8107956e6c7d733d95 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Wed, 22 Nov 2017 14:14:05 -0600 Subject: [PATCH] pvac user internal/external refs. --- src/client/client.cpp | 19 +++++++++--- src/client/clientGet.cpp | 16 ++++++---- src/client/clientMonitor.cpp | 11 ++++--- src/client/clientRPC.cpp | 13 +++++--- src/client/clientpvt.h | 59 ++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 19 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 8d014a1..598fa6e 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -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 { 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& 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"); } diff --git a/src/client/clientGet.cpp b/src/client/clientGet.cpp index 7711ef6..0790a02 100644 --- a/src/client/clientGet.cpp +++ b/src/client/clientGet.cpp @@ -25,7 +25,8 @@ typedef epicsGuardRelease UnGuard; namespace { struct GetPutter : public pva::ChannelPutRequester, - public pvac::Operation::Impl + public pvac::Operation::Impl, + public pvac::detail::wrapped_shared_from_this { 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() : ""; } + // 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 ret(new GetPutter(cb)); + std::tr1::shared_ptr ret(GetPutter::build(cb)); { Guard G(ret->mutex); - ret->op = getChannel()->createChannelPut(ret, std::tr1::const_pointer_cast(pvRequest)); + ret->op = getChannel()->createChannelPut(ret->internal_shared_from_this(), + std::tr1::const_pointer_cast(pvRequest)); } return Operation(ret); @@ -209,11 +212,12 @@ ClientChannel::put(PutCallback* cb, if(!pvRequest) pvRequest = pvd::createRequest("field()"); - std::tr1::shared_ptr ret(new GetPutter(cb)); + std::tr1::shared_ptr ret(GetPutter::build(cb)); { Guard G(ret->mutex); - ret->op = getChannel()->createChannelPut(ret, std::tr1::const_pointer_cast(pvRequest)); + ret->op = getChannel()->createChannelPut(ret->internal_shared_from_this(), + std::tr1::const_pointer_cast(pvRequest)); } return Operation(ret); diff --git a/src/client/clientMonitor.cpp b/src/client/clientMonitor.cpp index ce256d7..c5dd04d 100644 --- a/src/client/clientMonitor.cpp +++ b/src/client/clientMonitor.cpp @@ -23,7 +23,8 @@ typedef epicsGuardRelease UnGuard; namespace pvac { -struct Monitor::Impl : public pva::MonitorRequester +struct Monitor::Impl : public pva::MonitorRequester, + public pvac::detail::wrapped_shared_from_this { 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 ret(new Monitor::Impl(cb)); + std::tr1::shared_ptr ret(Monitor::Impl::build(cb)); ret->chan = getChannel(); { Guard G(ret->mutex); - ret->op = ret->chan->createMonitor(ret, std::tr1::const_pointer_cast(pvRequest)); + ret->op = ret->chan->createMonitor(ret->internal_shared_from_this(), + std::tr1::const_pointer_cast(pvRequest)); } return Monitor(ret); diff --git a/src/client/clientRPC.cpp b/src/client/clientRPC.cpp index 0b86220..9400961 100644 --- a/src/client/clientRPC.cpp +++ b/src/client/clientRPC.cpp @@ -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 UnGuard; namespace { struct RPCer : public pva::ChannelRPCRequester, - public pvac::Operation::Impl + public pvac::Operation::Impl, + public pvac::detail::wrapped_shared_from_this { 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() : ""; } + // 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 ret(new RPCer(cb, arguments)); + std::tr1::shared_ptr ret(RPCer::build(cb, arguments)); { Guard G(ret->mutex); - ret->op = getChannel()->createChannelRPC(ret, std::tr1::const_pointer_cast(pvRequest)); + ret->op = getChannel()->createChannelRPC(ret->internal_shared_from_this(), + std::tr1::const_pointer_cast(pvRequest)); } return Operation(ret); diff --git a/src/client/clientpvt.h b/src/client/clientpvt.h index eaa86ee..44a159e 100644 --- a/src/client/clientpvt.h +++ b/src/client/clientpvt.h @@ -1,9 +1,68 @@ #ifndef CLIENTPVT_H #define CLIENTPVT_H +#include + #include 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 +struct wrapped_shared_from_this { +private: + std::tr1::weak_ptr myselfptr; + + struct canceller { + std::tr1::shared_ptr ptr; + canceller(const std::tr1::shared_ptr& ptr) :ptr(ptr) {} + + void operator()(Base *) { + std::tr1::shared_ptr P; + P.swap(ptr); + P->cancel(); + } + }; + +public: + std::tr1::shared_ptr internal_shared_from_this() { + std::tr1::shared_ptr ret(myselfptr); + if(!ret) + throw std::tr1::bad_weak_ptr(); + return ret; + } + + static + std::tr1::shared_ptr build() { + std::tr1::shared_ptr inner(new Base), + ret(inner.get(), canceller(inner)); + inner->myselfptr = inner; + return ret; + } + + template + static + std::tr1::shared_ptr build(A a) { + std::tr1::shared_ptr inner(new Base(a)), + ret(inner.get(), canceller(inner)); + inner->myselfptr = inner; + return ret; + } + + template + static + std::tr1::shared_ptr build(A a, B b) { + std::tr1::shared_ptr inner(new Base(a, b)), + ret(inner.get(), canceller(inner)); + inner->myselfptr = inner; + return ret; + } +}; void registerRefTrack(); void registerRefTrackGet();