/* * Copyright information and license terms for this software can be * found in the file LICENSE that is included with the distribution */ #include #include #include #include #include #include #include #define epicsExportSharedSymbols #include "pv/logger.h" #include "clientpvt.h" #include "pv/pvAccess.h" namespace pvd = epics::pvData; namespace pva = epics::pvAccess; typedef epicsGuard Guard; typedef epicsGuardRelease UnGuard; namespace { struct GetPutter : public pva::ChannelPutRequester, public pvac::Operation::Impl, public pvac::detail::wrapped_shared_from_this { mutable epicsMutex mutex; bool started; operation_type::shared_pointer op; pvac::ClientChannel::GetCallback *getcb; pvac::ClientChannel::PutCallback *putcb; pvac::GetEvent event; static size_t num_instances; explicit GetPutter(pvac::ClientChannel::GetCallback* cb) :started(false), getcb(cb), putcb(0) {REFTRACE_INCREMENT(num_instances);} explicit GetPutter(pvac::ClientChannel::PutCallback* cb) :started(false), getcb(0), putcb(cb) {REFTRACE_INCREMENT(num_instances);} virtual ~GetPutter() {REFTRACE_DECREMENT(num_instances);} void callEvent(Guard& G, pvac::GetEvent::event_t evt = pvac::GetEvent::Fail) { if(!putcb && !getcb) return; event.event = evt; if(putcb) { pvac::ClientChannel::PutCallback *cb=putcb; putcb = 0; UnGuard U(G); cb->putDone(event); } if(getcb) { pvac::ClientChannel::GetCallback *cb=getcb; getcb = 0; UnGuard U(G); cb->getDone(event); } } virtual std::string name() const OVERRIDE FINAL { Guard G(mutex); return op ? op->getChannel()->getChannelName() : ""; } // 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); } virtual std::string getRequesterName() OVERRIDE FINAL { Guard G(mutex); return op ? op->getChannel()->getRequesterName() : ""; } virtual void channelPutConnect( const epics::pvData::Status& status, 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; if(!status.isOK()) { event.message = status.getMessage(); } else { event.message.clear(); } if(!status.isSuccess()) { callEvent(G); } else if(getcb){ channelPut->get(); started = true; } else if(putcb){ pvac::ClientChannel::PutCallback *cb(putcb); pvd::BitSet::shared_pointer tosend(new pvd::BitSet); pvac::ClientChannel::PutCallback::Args args(*tosend); try { UnGuard U(G); cb->putBuild(structure, args); if(!args.root) throw std::logic_error("No put value provided"); else if(*args.root->getStructure()!=*structure) throw std::logic_error("Provided put value with wrong type"); }catch(std::exception& e){ if(putcb) { event.message = e.what(); callEvent(G); } else { LOG(pva::logLevelInfo, "Lost exception in %s: %s", CURRENT_FUNCTION, e.what()); } } // check putcb again after UnGuard if(putcb) { channelPut->put(std::tr1::const_pointer_cast(args.root), tosend); started = true; } } } virtual void channelDisconnect(bool destroy) OVERRIDE FINAL { Guard G(mutex); event.message = "Disconnect"; callEvent(G); } virtual void putDone( 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; if(!status.isOK()) { event.message = status.getMessage(); } else { event.message.clear(); } callEvent(G, status.isSuccess()? pvac::GetEvent::Success : pvac::GetEvent::Fail); } virtual void getDone( const epics::pvData::Status& status, pva::ChannelPut::shared_pointer const & channelPut, 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; if(!status.isOK()) { event.message = status.getMessage(); } else { event.message.clear(); } event.value = pvStructure; event.valid = bitSet; callEvent(G, status.isSuccess()? pvac::GetEvent::Success : pvac::GetEvent::Fail); } virtual void show(std::ostream &strm) const { strm << "Operation(Get/Put" "\"" << name() <<"\"" ")"; } }; size_t GetPutter::num_instances; } //namespace namespace pvac { Operation ClientChannel::get(ClientChannel::GetCallback* cb, epics::pvData::PVStructure::const_shared_pointer pvRequest) { if(!impl) throw std::logic_error("Dead Channel"); if(!pvRequest) pvRequest = pvd::createRequest("field()"); std::tr1::shared_ptr ret(GetPutter::build(cb)); { Guard G(ret->mutex); ret->op = getChannel()->createChannelPut(ret->internal_shared_from_this(), std::tr1::const_pointer_cast(pvRequest)); } return Operation(ret); } Operation ClientChannel::put(PutCallback* cb, epics::pvData::PVStructure::const_shared_pointer pvRequest) { if(!impl) throw std::logic_error("Dead Channel"); if(!pvRequest) pvRequest = pvd::createRequest("field()"); std::tr1::shared_ptr ret(GetPutter::build(cb)); { Guard G(ret->mutex); ret->op = getChannel()->createChannelPut(ret->internal_shared_from_this(), std::tr1::const_pointer_cast(pvRequest)); } return Operation(ret); } namespace detail { void registerRefTrackGet() { epics::registerRefCounter("pvac::GetPutter", &GetPutter::num_instances); } } }//namespace pvac