This commit is contained in:
Michael Davidsaver
2017-06-16 17:47:41 +02:00
parent 3a190cb572
commit a453dd75fe
9 changed files with 2739 additions and 12 deletions

2
.gitignore vendored
View File

@ -5,8 +5,6 @@ include/
db/
dbd/
html/
documentation/html
documentation/*.tag
envPaths
configure/*.local
!configure/ExampleRELEASE.local

5
documentation/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*.tag
*.db
*.tmp
html/
latex/

2408
documentation/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
digraph clientowner {
ChannelProvider;
Channel;
ChannelRequester [shape=box];
Channel;
ChannelRequester [shape=box];
ChannelGet;
ChannelGetRequester [shape=box];
ChannelPut;
ChannelPutRequester [shape=box];
Monitor;
MonitorRequester [shape=box];
ChannelRPC;
ChannelRPCRequester [shape=box];
ChannelProcess;
ChannelProcessRequester [shape=box];
ChannelPutGet;
ChannelPutGetRequester [shape=box];
ChannelArray;
ChannelArrayRequester [shape=box];
# Operation -> Requester strong ref
Channel -> ChannelProvider [color=red, style=dashed];
Channel -> ChannelRequester [color=red, style=dashed];
ChannelGet -> ChannelGetRequester [color=red, style=dashed];
ChannelPut -> ChannelPutRequester [color=red, style=dashed];
Monitor -> MonitorRequester [color=red, style=dashed];
ChannelRPC -> ChannelRPCRequester [color=red, style=dashed];
ChannelProcess -> ChannelProcessRequester [color=red, style=dashed];
ChannelPutGet -> ChannelPutGetRequester [color=red, style=dashed];
ChannelArray -> ChannelArrayRequester [color=red, style=dashed];
# Channel -> Operation weak ref
Channel -> ChannelGet [color=green, style=dashed];
Channel -> ChannelPut [color=green, style=dashed];
Channel -> Monitor [color=green, style=dashed];
Channel -> ChannelRPC [color=green, style=dashed];
Channel -> ChannelProcess [color=green, style=dashed];
Channel -> ChannelPutGet [color=green, style=dashed];
Channel -> ChannelArray [color=green, style=dashed];
# Operation -> Channel strong ref
ChannelGet -> Channel [color=red, style=dashed];
ChannelPut -> Channel [color=red, style=dashed];
Monitor -> Channel [color=red, style=dashed];
ChannelRPC -> Channel [color=red, style=dashed];
ChannelProcess -> Channel [color=red, style=dashed];
ChannelPutGet -> Channel [color=red, style=dashed];
ChannelArray -> Channel [color=red, style=dashed];
}

17
documentation/mainpage.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef MAINPAGE_H
#define MAINPAGE_H
/**
@mainpage pvAccess C++ docs
The epics::pvAccess namespace.
See pv/pvAccess.h
@code
#include <pv/pvAccess.h>
@endcode
See the @ref providers page.
*/
#endif /* MAINPAGE_H */

View File

@ -0,0 +1,8 @@
digraph ownership {
MyClient [shape=box];
Channel;
ChannelRequester [shape=box];
MyClient -> Channel [color=red];
Channel -> ChannelRequester [color=red, style=dashed];
ChannelRequester -> MyClient [color=green];
}

224
documentation/providers.h Normal file
View File

@ -0,0 +1,224 @@
#ifndef PROVIDERS_H
#define PROVIDERS_H
/**
@page providers ChannelProvider API
@tableofcontents
@section providers_roles Roles
The Client and Server APIs revolve around the epics::pvAccess::ChannelProvider class.
In the following discussion the @ref providers_client calls methods of ChannelProvider and associated classes.
The @ref providers_server implements ChannelProvider and associated classes and is called by a "client.
By convention, instances of ChannelProvider are registered through a
epics::pvAccess::ChannelProviderRegistry
Typically the global instance
epics::pvAccess::getChannelProviderRegistry()
@subsection provider_roles_requester Operation and Requester
The classes associated with ChannelProvider come in pairs.
eg.
- epics::pvAccess::Channel and epics::pvAccess::ChannelRequester
- epics::pvAccess::ChannelGet and epics::pvAccess::ChannelGetRequester
- epics::pvAccess::ChannelPut and epics::pvAccess::ChannelPutRequester
- epics::pvAccess::Monitor and epics::pvAccess::MonitorRequester
- epics::pvAccess::ChannelRPC and epics::pvAccess::ChannelRPCRequester
- epics::pvAccess::ChannelProcess and epics::pvAccess::ChannelProcessRequester
- epics::pvAccess::ChannelPutGet and epics::pvAccess::ChannelPutGetRequester
- epics::pvAccess::ChannelArray and epics::pvAccess::ChannelArrayRequester
In the following discussions the term "Operation" refers to eg. Channel, ChannelGet, or similar
while "Requester" refers to ChannelRequester, ChannelGetRequester, or similar.
For convenience each Operation class has a member typedef for it's associated Requester, and vis. versa.
For example ChannelGet::requester_type is ChannelGetRequester
and ChannelGetRequester::operation_type is ChannelGet.
The "Requester" classes are implemented by the Client role
and called by the Server role to give notification to the client of certain events.
For example,
epics::pvAccess::ChannelRequester::channelStateChange()
is called when a Channel becomes (dis)connected.
A "Requester" sub-class must be provided when each "Operation" is created.
This Requester then becomes bound to the Operation.
@subsection providers_ownership shared_ptr<> and Ownership
"Operations" and "Requesters" are always handled via std::tr1::shared_ptr.
In the following dicussions an instance of std::tr1::shared_ptr is referred to as a "reference",
specifically a strong reference.
The associated std::tr1::weak_ptr is referred to as a weak reference.
shared_ptr instances can exist on the stack (local variables) or as
struct/class members.
Situations where a object contains are reference to itself, either directly or indirectly,
are known as "reference loops".
As long as a reference loop persists any cleanup of resources associated with
the shared_ptr (s) involved will not be done.
A reference loop which is never broken is called a "reference leak".
In order to avoid reference leaks, required relationships between
various classes will be described, and some rules stated.
In discussing the usage of an API diagrams like the following will be used to illustrate roles and ownership requirements.
The distinction of what is "user" code will depend on context.
For example, when discussing the Client role, epics::pvAccess::Channel will not be implemented by "user code".
When discussing the Server role, user code will implement Channel.
@subsubsection providers_ownership_unique Uniqueness
A shared_ptr is said to be unique if it's method shared_ptr::unique() returns true.
The general rule is that functions which create/allocate new objects using shared_ptr must yield unique shared_ptr.
Yielding a non-unique shared_ptr is a sign that an internal reference leak could occur.
@dotfile ownership.dot "shared_ptr relationships"
- A box denotes a class implemented by user code
- An oval denotes a class not implemented by user code
- A red line is a shared_ptr<>
- A green line is a weak_ptr<>
- A dashed line indicates a relationship which is outside the control of user code
@code
struct MyClient {
epics::pvAccess::Channel::shared_ptr channel;
};
struct MyChannelRequester : public epics::pvAccess::ChannelRequester
{
std::tr1::weak_ptr<MyClient> client;
virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) {
std::tr1::shared_ptr<MyClient> C(client.lock());
if(!C) return;
...
}
};
@endcode
In this example user code implements a custom MyClient class and a sub-class of ChannelRequester in order
to make use of a Channel.
In order to avoid a reference loop, the sub-class of ChannelRequester uses a weak_ptr
to refer to MyClient during channelStateChange() events.
@section providers_client Client Role
A client will by configured with a certain provider name string.
It will begin by passing this string to
epics::pvAccess::ChannelProviderRegistry::createProvider()
to obtain a ChannelProvider instance.
Custom configuration of provider can be accomplished by
passing a
epics::pvAccess::Configuration
to createProvider().
A Configuration is a set of string key/value parameters
which the provider may use as it sees fit.
If a Configuration is not provided (or NULL) then
the provider will use some arbitrary defaults.
By convention default Configuration use the process environment.
@code
#include <pv/pvAccess.h>
#include <pv/clientFactory.h>
// add "pva" to registry
epics::pvAccess::ClientFactory::start();
// create a new client instance.
epics::pvAccess::ChannelProvider::shared_pointer prov;
prov = epics::pvAccess::getChannelProviderRegistry()->createProvider("pva");
// createProvider() returns NULL if the named provider hasn't been registered
if(!prov)
throw std::runtime_error("PVA provider not registered");
@endcode
@subsection providers_client_channel Client Channel
The primary (and in many cases only) ChannelProvider method of interest is
epics::pvAccess::ChannelProvider::createChannel()
from which a new
epics::pvAccess::Channel
can be obtained.
Each call to createChannel() produces a "new" std::shared_ptr<Channel>
which is uniquely owned by the caller (see @ref providers_ownership_unique).
As such, the caller must keep a reference to to the Channel or it will be destroyed.
This may be done explicitly, or implicitly by storing a reference to an Operation.
A Channel can be created at any time, and shall succeed as long as
the provided name and address are syntactically valid, and the priority is in the valid range.
When created, a Channel may or may not already be in the Connected state.
On creation epics::pvAccess::ChannelRequester::channelCreated will be called
before createChannel() returns.
Notification of connection state changes are made through
epics::pvAccess::ChannelRequester::channelStateChange()
as well as through the *Connect() and channelDisconnect() methods
of Requesters of any Operations on a Channel (eg.
epics::pvAccess::MonitorRequester::channelDisconnect()
).
@subsection providers_client_operations Client Operations
This section describes commonalities between the various Operation supported:
Get, Put, Monitor, RPC, PutGet, Process, and Array.
An Operation is created/allocated with one of the create* methods of Channel.
All behave in a similar way.
- epics::pvAccess::Channel::createChannelGet()
- epics::pvAccess::Channel::createChannelPut()
- epics::pvAccess::Channel::createMonitor()
- epics::pvAccess::Channel::createChannelRPC()
- epics::pvAccess::Channel::createChannelPutGet()
- epics::pvAccess::Channel::createChannelProcess()
- epics::pvAccess::Channel::createChannelArray()
The created Operation is unique (see @ref providers_ownership_unique).
The \*Connect() method of the corresponding Requester will be called when
the Operation is "ready" (underlying Channel is connected).
This may happen before the create* method has returned, or at some time later.
- epics::pvAccess::ChannelGetRequester::channelGetConnect()
- epics::pvAccess::ChannelPutRequester::channelPutConnect()
- epics::pvAccess::MonitorRequester::monitorConnect()
- epics::pvAccess::ChannelRPCRequester::channelRPCConnect()
- epics::pvAccess::ChannelPutGetRequester::channelPutGetConnect()
- epics::pvAccess::ChannelProcessRequester::channelProcessConnect()
- epics::pvAccess::ChannelArrayRequester::channelArrayConnect()
When the underlying Channel becomes disconnected or is destroyed,
then the channelDisconnect() method of each Requester is called (eg.
see epics::pvAccess::ChannelBaseRequester::channelDisconnect()
).
@subsubsection providers_client_operations_lifetime Operation Lifetime and (dis)connection
An Operation can be created at any time regardless of whether a Channel is connected or not.
An Operation will remain associated with a Channel through (re)connection and disconnection.
@subsection provides_client_ownership Client Ownership
Implicit ownership in classes outside the control of client code.
@dotfile client_ownership.dot Client implicit relationships
- Channel hold strong refs. of ChannelProvider and ChannelRequester
- Channel holds weak refs to all Operations
- Operation holds strong refs to the corresponding Requester, and Channel
@section providers_server Server Role
*/
#endif /* PROVIDERS_H */

View File

@ -20,6 +20,7 @@
namespace epics { namespace pvAccess {
class MonitorRequester;
class MonitorElement;
typedef std::tr1::shared_ptr<MonitorElement> MonitorElementPtr;
typedef std::vector<MonitorElementPtr> MonitorElementPtrArray;
@ -57,6 +58,8 @@ class epicsShareClass MonitorElement {
class epicsShareClass Monitor : public virtual epics::pvData::Destroyable{
public:
POINTER_DEFINITIONS(Monitor);
typedef MonitorRequester requester_type;
virtual ~Monitor(){}
/**
* Start monitoring.

View File

@ -31,6 +31,7 @@
#include <shareLib.h>
namespace epics {
//! Holds all PVA related
namespace pvAccess {
class Configuration;
@ -120,6 +121,7 @@ class ChannelPutRequester;
class ChannelPutGetRequester;
class ChannelRPCRequester;
//! Base for all Requesters (callbacks to client)
struct epicsShareClass ChannelBaseRequester : virtual public epics::pvData::Requester
{
POINTER_DEFINITIONS(ChannelBaseRequester);
@ -176,6 +178,8 @@ public:
class epicsShareClass MonitorRequester : public ChannelBaseRequester {
public:
POINTER_DEFINITIONS(MonitorRequester);
typedef Monitor operation_type;
virtual ~MonitorRequester(){}
/**
* The client and server have both completed the createMonitor request.
@ -1084,19 +1088,19 @@ public:
/**
* Request a Channel.
*
* May call ChannelListRequester::channelCreated() before returning, or at some time later.
* If an exception is thrown, then channelCreated() will never be called.
* Channel creation is immediate.
* ChannelRequester::channelCreated() will be called before returning.
* The shared_ptr which is passed to channelCreated() will also be returned.
*
* The returned Channel will hold a strong reference to the provided ChannelRequester
* Failures during channel creation are delivered to ChannelRequester::channelCreated() with Status::isSuccess()==false.
*
* Returned shared_ptr<Channel> includes only external references.
* eg. the first call to createChannel() with a particular name, priority, and address
* must return a unique()==true pointer.
* Subsequent calls with the same triple must either return the same reference or
* another with unique()==true.
* @post The returned Channel will hold a strong reference to the provided ChannelRequester
* and to the ChannelProvider through which it is created.
*
* @post The shared_ptr passed to ChannelRequester::channelCreated() is unique. See @ref providers_ownership_unique
*
* @param name The name of the channel.
* @param requester The Requester.
* @param requester Will receive notifications about channel state changes
* @param priority channel priority, must be <code>PRIORITY_MIN</code> <= priority <= <code>PRIORITY_MAX</code>.
* @param address Implementation dependent condition. eg. A network address to bypass the search phase. Pass an empty() string for default behavour.
* @return A non-NULL Channel unless channelCreated() called with an Error
@ -1104,8 +1108,11 @@ public:
virtual Channel::shared_pointer createChannel(std::string const & name,ChannelRequester::shared_pointer const & requester,
short priority, std::string const & address) = 0;
//! @deprecated Changing of Configuration after start is not supported
virtual void configure(epics::pvData::PVStructure::shared_pointer /*configuration*/) EPICS_DEPRECATED {};
//! @deprecated No function
virtual void flush() EPICS_DEPRECATED {};
//! @deprecated No function
virtual void poll() EPICS_DEPRECATED {};
};
@ -1269,10 +1276,16 @@ private:
providers_t providers;
};
//! never returns NULL
/** Access to the global ChannelProviderRegistry instance.
*
* Never returns NULL
*/
epicsShareFunc ChannelProviderRegistry::shared_pointer getChannelProviderRegistry();
//! Shorthand for getChannelProviderRegistry()->add(channelProviderFactory);
epicsShareFunc void registerChannelProviderFactory(ChannelProviderFactory::shared_pointer const & channelProviderFactory);
//! Shorthand for getChannelProviderRegistry()->remove(channelProviderFactory);
epicsShareFunc void unregisterChannelProviderFactory(ChannelProviderFactory::shared_pointer const & channelProviderFactory);
//! Shorthand for getChannelProviderRegistry()->clear();
epicsShareFunc void unregisterAllChannelProviderFactory();