From 80db41f1c4eea01bf0fac58f1a05b7e08dba754a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 29 Jun 2018 11:19:24 -0700 Subject: [PATCH] pva/client.h Allow callbacks to unref Operations Keep a local (internal) shared_ptr whenever user callbacks are run. This allows user callbacks to drop (reset()/dtor) Operations or Monitors safely. Ref must to captured before mutex is locked, which is safe as the internal weak_ptr is not changed after build() (despite being non-const) --- src/client/clientGet.cpp | 9 +++++++-- src/client/clientMonitor.cpp | 7 +++++++ src/client/clientRPC.cpp | 4 ++++ src/client/clientpvt.h | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/client/clientGet.cpp b/src/client/clientGet.cpp index 54051fd..546bd1d 100644 --- a/src/client/clientGet.cpp +++ b/src/client/clientGet.cpp @@ -39,9 +39,9 @@ struct GetPutter : public pva::ChannelPutRequester, static size_t num_instances; - GetPutter(pvac::ClientChannel::GetCallback* cb) :started(false), getcb(cb), putcb(0) + explicit GetPutter(pvac::ClientChannel::GetCallback* cb) :started(false), getcb(cb), putcb(0) {REFTRACE_INCREMENT(num_instances);} - GetPutter(pvac::ClientChannel::PutCallback* cb) :started(false), getcb(0), putcb(cb) + explicit GetPutter(pvac::ClientChannel::PutCallback* cb) :started(false), getcb(0), putcb(cb) {REFTRACE_INCREMENT(num_instances);} virtual ~GetPutter() {REFTRACE_DECREMENT(num_instances);} @@ -73,6 +73,8 @@ struct GetPutter : public pva::ChannelPutRequester, // called automatically via wrapped_shared_from_this virtual void cancel() OVERRIDE FINAL { + // keepalive for safety in case callback wants to destroy us + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(started && op) op->cancel(); callEvent(G, pvac::GetEvent::Cancel); @@ -89,6 +91,7 @@ struct GetPutter : public pva::ChannelPutRequester, pva::ChannelPut::shared_pointer const & channelPut, epics::pvData::Structure::const_shared_pointer const & structure) OVERRIDE FINAL { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(started) return; if(!putcb && !getcb) return; @@ -146,6 +149,7 @@ struct GetPutter : public pva::ChannelPutRequester, const epics::pvData::Status& status, pva::ChannelPut::shared_pointer const & channelPut) OVERRIDE FINAL { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(!putcb) return; @@ -164,6 +168,7 @@ struct GetPutter : public pva::ChannelPutRequester, epics::pvData::PVStructure::shared_pointer const & pvStructure, epics::pvData::BitSet::shared_pointer const & bitSet) OVERRIDE FINAL { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(!getcb) return; diff --git a/src/client/clientMonitor.cpp b/src/client/clientMonitor.cpp index 6e19abe..01fdc2b 100644 --- a/src/client/clientMonitor.cpp +++ b/src/client/clientMonitor.cpp @@ -84,6 +84,9 @@ struct Monitor::Impl : public pva::MonitorRequester, { operation_type::shared_pointer temp; { + // keepalive for safety in case callback wants to destroy us + std::tr1::shared_ptr keepalive(internal_shared_from_this()); + Guard G(mutex); last.reset(); @@ -111,6 +114,7 @@ struct Monitor::Impl : public pva::MonitorRequester, pva::MonitorPtr const & operation, pvd::StructureConstPtr const & structure) OVERRIDE FINAL { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(!cb || started || done) return; @@ -140,6 +144,7 @@ struct Monitor::Impl : public pva::MonitorRequester, virtual void channelDisconnect(bool destroy) OVERRIDE FINAL { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(!cb || done) return; event.message = "Disconnect"; @@ -149,6 +154,7 @@ struct Monitor::Impl : public pva::MonitorRequester, virtual void monitorEvent(pva::MonitorPtr const & monitor) OVERRIDE FINAL { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(!cb || done) return; event.message.clear(); @@ -158,6 +164,7 @@ struct Monitor::Impl : public pva::MonitorRequester, virtual void unlisten(pva::MonitorPtr const & monitor) OVERRIDE FINAL { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(!cb || done) return; done = true; diff --git a/src/client/clientRPC.cpp b/src/client/clientRPC.cpp index 5028586..04cfab2 100644 --- a/src/client/clientRPC.cpp +++ b/src/client/clientRPC.cpp @@ -83,6 +83,7 @@ struct RPCer : public pva::ChannelRPCRequester, // called automatically via wrapped_shared_from_this virtual void cancel() { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(started && op) op->cancel(); callEvent(G, pvac::GetEvent::Cancel); @@ -98,6 +99,7 @@ struct RPCer : public pva::ChannelRPCRequester, const epics::pvData::Status& status, pva::ChannelRPC::shared_pointer const & operation) { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(!cb || started) return; @@ -117,6 +119,7 @@ struct RPCer : public pva::ChannelRPCRequester, virtual void channelDisconnect(bool destroy) OVERRIDE FINAL { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); event.message = "Disconnect"; @@ -128,6 +131,7 @@ struct RPCer : public pva::ChannelRPCRequester, pva::ChannelRPC::shared_pointer const & operation, epics::pvData::PVStructure::shared_pointer const & pvResponse) { + std::tr1::shared_ptr keepalive(internal_shared_from_this()); Guard G(mutex); if(!cb) return; diff --git a/src/client/clientpvt.h b/src/client/clientpvt.h index 44a159e..61f3c11 100644 --- a/src/client/clientpvt.h +++ b/src/client/clientpvt.h @@ -16,6 +16,7 @@ namespace pvac{namespace detail{ template struct wrapped_shared_from_this { private: + // const after build() std::tr1::weak_ptr myselfptr; struct canceller {