Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  <2017 Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  <2017
<== Date ==> <== Thread ==>

Subject: Re: C++ multi threaded application.
From: "Giacomo S." <giacomo.strangolino@elettra.eu>
To: Michael Davidsaver <mdavidsaver@gmail.com>, Andrew Johnson <anj@aps.anl.gov>, tech-talk@aps.anl.gov
Date: Tue, 5 Sep 2017 17:01:23 +0200
Hello and thanks for Your detailed answers.

So, summing up, suppose to recreate this scenario:

- application starts
--> client requests monitoring of  pv1 = foo:ai1

- (first request, so)   ca_context_create(ca_enable_preemptive_callback)

- ca_create_channel (...)

- (in  connection_handler invoked from EPICS foo thread: ) 
ca_create_subscription(...)


[ monitors for a while, receive event callbacks from EPICS foo thread]

--> client requests pv2  = foo:ai2

 - ca_create_channel (...)

- (in  connection_handler invoked from EPICS foo thread: ) 
ca_create_subscription(...)


---> network goes down

- (in pv1 connection callback invoked from EPICS foo thread:)  1. 
ca_clear_subscription()   BUT NOT ca_clear_channel()

- (in pv2 connection callback invoked from EPICS foo thread:)  1. 
ca_clear_subscription()      BUT NOT   ca_clear_channel()

---> network UP again


- (in pv1 connection callback invoked from EPICS foo thread:)  1. 
ca_create_subscription() 

- (in pv2 connection callback invoked from EPICS foo thread:)  1. 
ca_create_subscription()  

---> client requests pv3  = bar:ai3

 - ca_create_channel (...)

- (in  connection_handler invoked from EPICS >>> bar <<<  thread: ) 
ca_create_subscription(...)   <--- bar THREAD <----

[ receive event callbacks from 2 threads: foo for pv1 and pv2,  bar for
pv3 ]

---> client wants to disconnect everything (or application shutdown)

- foreach pv:                                                 // is this
safe to be called from the thread that initially called the ca_create* 
counterparts???
  * ca_clear_subscription();
  * ca_clear_channel();

- ca_context_destroy();                                // from same
thread where ca_context_create() was called


Is the above correct? Am I somewhere wrong?

Some more simpler questions:

1. every time I receive an event, evargs contain not only the value /
timestamp but also configuration parameters, right (HI, LO thresholds
and so on...)

2. are there utility functions to extract the value and timestamp from
the evargs and easily convert them into  the correct type (short,
double, int...)

3. If I monitor a PV, is it possible to know from the args if it's a
vector (and its dimension) or a scalar?

4. where can I find the explanation of this: Lo disp limit, hi disp
limit, Lo alarm limit.... ?

5. How to extract the timestamp of the event (as far as I've seen from
the code, there must be a timestamp from the server and one from the
client. In this case, what does the client side timestamp mean. Is it
the timestamp when the event is received on the client itself?)

Thanks for Your patience.

Is there a good tutorial for a quick start and the answers to my
questions 1 to 5?
I mean a quick start to understand how to program a client in C (C++).

One final thing: I've seen a set of C++ libraries (pvaClientCPP,
pvCommonCPP)  and in general the

"EPICS V4 C++ module"

but it looks like it's not up to date with the latest EPICS release.

Is this the "official" C++ Epics module?

Is it normal that it trails the last EPICS version?

Thanks again for everything.

Giacomo S.



On 09/04/2017 06:14 PM, Michael Davidsaver wrote:
> On 09/04/2017 04:08 AM, Giacomo S. wrote:
>> On 09/01/2017 06:31 PM, Andrew Johnson wrote:
>> ...
>>>> - the background thread implements a wait condition so that it sleeps
>>>> until a new event arrives (for example, exit event or new variable to be
>>>> monitored is added)
>>> You should be aware that the Channel Access client library creates a
>>> background thread for each separate IOC it connects to, and it calls
>>> your callback routines from those threads. You have no control over
>>> that, if you talk to PVs on 2 IOCs you will get callbacks from 2
>>> threads. 
>> That's a relevant information. Thus, monitoring 100 PVs means at least
>> 100 threads in the application.
> Only if each PV is served by a separate IOC.  If all 100 are served by
> one IOC, then there is only 1 socket and 2 threads to service it.
>
> ...
>> Inside the connection_handler() i call
>> ca_create_subscription()
>>
>>
>> // Code is taken from camonitor's.
>> //
>> void CuMonitorActivity::connection_handler(connection_handler_args args)
>> {
>>
> ...
>>             /* ---------------- */
>>             /* install monitor once with first connect */
>>             ppv->status = ca_create_subscription(ppv->dbrType,
>>                                                  ppv->reqElems,
>>                                                  ppv->ch_id,
>>                                                  eventMask,
>>                                                 
>> CuMonitorActivity::event_handler_cb,
>>                                                  (void*)ppv,
>>                                                  NULL);
>>         }
>>     }
>>     else if ( args.op == CA_OP_CONN_DOWN ) {
>>         nConn--;
>>         ppv->status = ECA_DISCONN;
>>         print_time_val_sts(ppv, reqElems);
> Subscriptions are not automatically cleaned up on disconnect.  You'll
> want to make sure you call ca_clear_subscription() here, and on exit, to
> avoid the situation Andrew mentions.
>
> You'll also want to look at ca_clear_channel() which must be paired with
> ca_create_channel().
>
>
>>     }
>> }
>>
>>
>>>> From within connection_handler_cb I extract the current
>>>> CuMonitorActivity instance (stored in pv's monitor_activity field) and I
>>>> invoke the object's extraction method. Data is posted in the main thread.
>>>>
>>>> NOTE: I notice that connection_handler_cb is invoked from ANOTHER
>>>> thread, not my main thread, not my secondary thread. This should be as
>>>> expected I guess for things to work and given the ca_
>>>> enable_preemptive_callback option.
>>> Right, see my remarks about threading above. Note that you are allowed
>>> to make calls to other CA routines from within your callbacks, and in
>>> fact that is the recommended way to set up your subscriptions. However
>>> you should be careful to only subscribe for data from a PV only once; if
>>> the IOC gets restarted your connection handler will be called again on
>>> reconnect, and unless you're careful that code might create a second
>>> subscription to the same PV, which will result in your event callback
>>> being run twice or more for each data update from the IOC.
>>>
>>>> * when I post an "exitEvent" on my secondary thread from the main one
>>>> (e.g. the GUI is closed),  "ca_context_destroy();" is invoked in my
>>>> secondary thread (not the Epics thread that calls connection_handler_cb
>>>> during monitoring - is this OK? )
>>> Yes, as long as the thread was attached to the CA context. You should
>>> attach all threads that you want to make CA calls from to your original
>>> context using ca_current_context() and ca_attach_context().
>>>
>>>> * Is there a way to ADD more PVs to monitor WHILE others are being
>>>> monitored (for example, is it legit to call create_pvs() again ) ?
>>> Yes, although you didn't show us your create_pvs() routine. You can add
>>> and remove channels and subscriptions whenever you like; about the only
>>> thing you can't call from within a callback routine is ca_pend_event().
>> OK, now it should be clear how I create the pvs.
> A client does not create PVs.  From the prospective of a CA client a
> "PV" is the address string provided when creating a Channel (analogous
> to a URL).
>
>> Could you tell me how to add and remove channels and subscriptions?
> You are already doing this by calling ca_create_channel() and
> ca_create_subscription().  Removal is ca_clear_channel() and
> ca_clear_subscription().
>
>
>
>> As to threading, all channel/monitor setup is made up in a secondary thread.
>> The idea is thus to add and remove channels within that very secondary
>> thread.
>> So, as you stated, the callbacks would be invoked from the N different
>> threads on the event callback within the
>> CuMonitorActivity object.
>> Data, from within the event callback, is then queued and posted on the
>> main thread.
>> I have now to check whether I need to lock guard the code within the
>> event callback or not.
>>
>> Nonetheless, if my observations are correct, that makes a good starting
>> point.
>>
>> Thanks for Your help.
>>
>> Giacomo, Elettra, Trieste, Italy
>>
>>
>>>> * Are there any observations/corrections to my approach and to my
>>>> statements above?
>>> Go check out epicsQt before you spend more time reinventing the wheel.
>>> There is a separate mailing list for EPICS users programming with Qt and
>>> you might want to subscribe to that:
>>>     http://www.aps.anl.gov/epics/qti-talk/index.php
>>>
>>> - Andrew
>>>


Replies:
Re: C++ multi threaded application. Andrew Johnson
References:
C++ multi threaded application. Giacomo S.
Re: C++ multi threaded application. Andrew Johnson
Re: C++ multi threaded application. Giacomo S.

Navigate by Date:
Prev: data refresh and add pv 梁雅翔
Next: Re: C++ multi threaded application. Andrew Johnson
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  <2017
Navigate by Thread:
Prev: Re: C++ multi threaded application. Michael Davidsaver
Next: Re: C++ multi threaded application. Andrew Johnson
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  <2017
ANJ, 05 Sep 2017 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·