This commit is contained in:
Michael Davidsaver
2020-03-07 14:12:22 -08:00
parent b33ea5df31
commit 2e8d4beb02
5 changed files with 85 additions and 6 deletions
+43
View File
@@ -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<Operation> op; // <-- Danger!
};
auto myptr = std::make_shared<mystruct>();
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
---------
+12 -1
View File
@@ -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
+11 -2
View File
@@ -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<GetBuilder> {
std::shared_ptr<Operation> _exec_get();
public:
GetBuilder(const std::shared_ptr<Context::Pvt>& 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<void(Result&&)>&& 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<Value(Value&&)>&& 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<void(Result&&)>&& cb) { _result = std::move(cb); return *this; }
@@ -418,7 +425,8 @@ class RPCBuilder : public detail::CommonBuilder<GetBuilder> {
std::function<void(Result&&)> _result;
public:
RPCBuilder(const std::shared_ptr<Context::Pvt>& 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<void(Result&&)>&& cb) { _result = std::move(cb); return *this; }
/** Execute the network operation.
@@ -440,6 +448,7 @@ class MonitorBuilder : public detail::CommonBuilder<MonitorBuilder> {
public:
MonitorBuilder(const std::shared_ptr<Context::Pvt>& 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<void(Subscription&)>&& cb) { _event = std::move(cb); return *this; }
//! Include Connected exceptions in queue (default false).
MonitorBuilder& maskConnected(bool m = true) { _maskConn = m; return *this; }
+11
View File
@@ -609,8 +609,19 @@ public:
}
};
/** Depth-first iteration of all decendent fields
*
* @code
* Value top(...);
* for(auto fld : top.iall()) {
* std::cout<<top.nameOf(fld)<<" = "<<fld<<"\n";
* }
* @endcode
*/
Iterable<Value> iall() { return Iterable<Value>{this, false, true}; }
//! iteration of all child fields
Iterable<Value> ichildren() { return Iterable<Value>{this, false, false}; }
//! Depth-first iteration of all marked decendent fields
Iterable<Value> imarked() { return Iterable<Value>{this, true , true}; }
Iterable<const Value> iall() const { return Iterable<const Value>{this, false, true}; }
+8 -3
View File
@@ -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