From 2e8d4beb02469fabb7803b5a7c28f2d0011ed5d2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 7 Mar 2020 14:12:22 -0800 Subject: [PATCH] doc --- documentation/client.rst | 43 ++++++++++++++++++++++++++++++++++++++++ documentation/index.rst | 13 +++++++++++- src/pvxs/client.h | 13 ++++++++++-- src/pvxs/data.h | 11 ++++++++++ src/pvxs/source.h | 11 +++++++--- 5 files changed, 85 insertions(+), 6 deletions(-) diff --git a/documentation/client.rst b/documentation/client.rst index ab1a689..9554b08 100644 --- a/documentation/client.rst +++ b/documentation/client.rst @@ -146,6 +146,49 @@ the event queue .. doxygenstruct:: pvxs::client::Subscription :members: +Threading +^^^^^^^^^ + +A client Context will invoke user callback functions from one or more internal worker threads. +However, it is guaranteed that callbacks relating to a given Channel (PV name + priority) will never be executed concurrently. +This implies that callbacks for a single operation will also never be executed concurrently. + +User code must avoid doing unnecessary work from within a callback function as this will +prevent other callbacks from be executed. + +Ownership +^^^^^^^^^ + +User provided callbacks are in the form of std::function which may, +directly or indirectly, store shared_ptr<> instances. +The returned Operation and Subscription instances may be treated as +storing the std::function instance(s) and thus any shared_ptr<> captured in them. + +Therefore, in order to avoid a resource leak, +it is advisable to consider whether a returned Operation or Subscription +may participate in a reference loop. + +For example, the following creates a reference loop between the Operation instance and the "mystruct" instance. + +.. code-block:: c++ + + struct mystruct { + std::shared_ptr op; // <-- Danger! + }; + auto myptr = std::make_shared(); + + Context ctxt(...); + myptr->op = ctxt.get("pv:name") + .result([ctxt](Result&& result) { + }) + .exec(); + +While such loops can be explicitly broken (eg. by NULLing 'myptr->op') it is strongly +recommended to avoid such situations as unexpected (exceptional) conditions can easily +lead to resource leaks which are quite difficult to detect and isolate. + +Where possible it is recommended to capture weak_ptr<> instances. + pvRequest --------- diff --git a/documentation/index.rst b/documentation/index.rst index d53eb7b..1950545 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -3,7 +3,18 @@ PVXS client/server for PVA Protocol This module is under active development and is only suitable for experimental usage. -The canonical source can be found at https://github.com/mdavidsaver/pvxs +The canonical version of this page is https://mdavidsaver.github.io/pvxs + +Versioned source can be found at https://github.com/mdavidsaver/pvxs + +This module provides a library (libpvxs.so or pvxs.dll) and a set of +CLI utilities acting as PVAccess protocol client and/or server. + +Dependencies + +* A C++11 compliant compiler (eg. GCC >= 4.9) +* EPICS Base >=3.15.1 +* libevent >=2.0.1 .. toctree:: :maxdepth: 3 diff --git a/src/pvxs/client.h b/src/pvxs/client.h index 8f3edfb..a4ff000 100644 --- a/src/pvxs/client.h +++ b/src/pvxs/client.h @@ -99,6 +99,7 @@ struct PVXS_API Operation { virtual ~Operation() =0; //! Explicitly cancel a pending operation. + //! Blocks until an in-progress callback has completed. virtual void cancel() =0; }; @@ -108,6 +109,7 @@ struct PVXS_API Subscription { virtual ~Subscription() =0; //! Explicitly cancel a active subscription. + //! Blocks until any in-progress callback has completed. virtual void cancel() =0; //! Ask a server to stop sending updates to this Subscription @@ -355,7 +357,8 @@ class GetBuilder : public detail::CommonBuilder { std::shared_ptr _exec_get(); public: GetBuilder(const std::shared_ptr& ctx, const std::string& name, bool get) :CommonBuilder{ctx,name}, _get(get) {} - //! Callback through which result Value or an error will be delivered + //! Callback through which result Value or an error will be delivered. + //! The functor is stored in the Operation returned by exec(). GetBuilder& result(std::function&& cb) { _result = std::move(cb); return *this; } /** Execute the network operation. @@ -392,12 +395,16 @@ public: * Once the PV type information is received from the server, * this function will be responsible for populating a Value * which will actually be sent. + * + * The functor is stored in the Operation returned by exec(). */ PutBuilder& build(std::function&& cb) { _builder = std::move(cb); return *this; } /** Provide the operation result callback. * This callback will be passed a Result which is either an empty Value (success) * or an exception on error. + * + * The functor is stored in the Operation returned by exec(). */ PutBuilder& result(std::function&& cb) { _result = std::move(cb); return *this; } @@ -418,7 +425,8 @@ class RPCBuilder : public detail::CommonBuilder { std::function _result; public: RPCBuilder(const std::shared_ptr& ctx, const std::string& name, Value&& arg) :CommonBuilder{ctx,name}, _argument(std::move(arg)) {} - //! Callback through which result Value or an error will be delivered + //! Callback through which result Value or an error will be delivered. + //! The functor is stored in the Operation returned by exec(). RPCBuilder& result(std::function&& cb) { _result = std::move(cb); return *this; } /** Execute the network operation. @@ -440,6 +448,7 @@ class MonitorBuilder : public detail::CommonBuilder { public: MonitorBuilder(const std::shared_ptr& ctx, const std::string& name) :CommonBuilder{ctx,name} {} //! Install event callback + //! The functor is stored in the Subscription returned by exec(). MonitorBuilder& event(std::function&& cb) { _event = std::move(cb); return *this; } //! Include Connected exceptions in queue (default false). MonitorBuilder& maskConnected(bool m = true) { _maskConn = m; return *this; } diff --git a/src/pvxs/data.h b/src/pvxs/data.h index 92905d5..d58f52c 100644 --- a/src/pvxs/data.h +++ b/src/pvxs/data.h @@ -609,8 +609,19 @@ public: } }; + /** Depth-first iteration of all decendent fields + * + * @code + * Value top(...); + * for(auto fld : top.iall()) { + * std::cout< iall() { return Iterable{this, false, true}; } + //! iteration of all child fields Iterable ichildren() { return Iterable{this, false, false}; } + //! Depth-first iteration of all marked decendent fields Iterable imarked() { return Iterable{this, true , true}; } Iterable iall() const { return Iterable{this, false, true}; } diff --git a/src/pvxs/source.h b/src/pvxs/source.h index e566060..ccb9956 100644 --- a/src/pvxs/source.h +++ b/src/pvxs/source.h @@ -92,9 +92,14 @@ public: virtual void stats(MonitorStat&) const =0; - //! Set flow control levels. - //! onLowMark callback will be invoked when nFree()<=low becomes true, and not again until it has been false. - //! onHighMark callback will be invoked when nFree()>high becomes true, and not again until it has been false. + /** Set flow control levels. + * + * Flow control operations against an outbound "window" size, which is the number of updates which may + * be sent before a client ack. must be received. By default both high and low levels are zero. + * + * onLowMark callback is not currently implemented and the 'low' level is not used. + * onHighMark callback will be invoked when a client ack. increases the window size above (>) 'high'. + */ virtual void setWatermarks(size_t low, size_t high) =0; //! Callback when client resumes/pauses updates