Post events from a thread to tango device server event loop
|
|
---|---|
Hello. as the subject indicates, I would like to write some code where a secondary thread does something and sometimes posts an event to the main thread, passing calculated data. In other terms, the posted event translates into a callback on a listener and the callback has to be called inside the main thread. Is there an easy way to do this? Another question: if I replace the main event loop with my_event_loop, as stated in the documentation: tg->server_set_event_loop(my_event_loop); does the Tango server still receive tango device server events or do I have to worry about that in my_event_loop? Thanks for Your attention best regards, giacomo. |
|
|
---|---|
Hi Giacomo, About your first question, I never implemented something like that with callback to be called from the main thread. Usually, we have cases where we have an acquisition thread which will will receive updates from the hardware and which will update some shared variables (protected with mutex) between the acquisition thread and the main thread. Then, there are polled attributes or commands which will use these variables. Maybe someone else already did what you'd like to do? giacomos It should work, the Tango server should still receive Tango events. You shouldn't have to worry about that in your event loop.
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new. |
|
|
---|---|
Hello and thanks for your reply Reynald. One more question naturally follows your observations: is it safe to share (reading and writing) objects inside my_event_loop and the Tango main event loop? Thanks. Giacomo. |
|
|
---|---|
Hi Giacomo, CORBA creates one thread per client and there is also the Tango polling thread running in parallel of your main thread. So if these threads (or other threads) could be accessing the same objects as your custom event loop, you should protect these objects in your custom event loop using Tango monitors or mutexes. Hoping this helps, Reynald
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new. |
|
|
---|---|
Hello Reynald. Not perfectly clear yet, sorry. I'll try to make it simple. Suppose I call tg->server_set_event_loop (my_event_loop) and MyEventLoop class has a variable var. Suppose I have a method in the main device server class, say void MyTangoDev::read_double_scalar(Tango::Attribute &attr) Do I have to read/write protect "var" from within read_double_scalar and MyEventLoop::some_func() ? As far as I can understand from what you've written above, I suspect so. In that case, I would rather understand how to post events to the thread where void MyTangoDev::read_double_scalar is executed. I explain why. I am trying to write a library where you can do activities in background and get a notification when they are finished. The notification arrives in the main thread together with the data. The advantage of this is not having to worry about locks mutexes and threads. If this cannot be applied due to concurrency between MyEventLoop and main server loop, than it's not useful at all. So, how to post events in the (main) MyTangoDev thread, so that I can safely access data posted from the background threads? Thanks for everything. Giacomo |
|
|
---|---|
giacomo You understood well. giacomo I don't see how you could avoid dealing with mutexes in a multithreaded environment. If you want to access to the variable var you were describing from read_double_scalar and from the main thread (in your custom event loop or from another thread in general), you need to protect this access. Well, in short, if you need to access this variable from different threads, you need protection. read_double_scalar might be invoked by your polling thread or by a thread created to handle a client request. read_double_scalar is usually not executed from the main thread. CORBA will create a thread to handle a client request and will execute read_double_scalar in this thread. You might get the illusion it is executed by the main thread because of the Tango serialization mechanism which will protect you automatically by default (using mutexes hidden in the Tango implementation) against concurrent executions of read_double_scalar method but this method is actually executed in a thread different than the main thread. Hoping this clarifies a bit. Reynald
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new. |
|
|
---|---|
Reynaldgiacomo |
|
|
---|---|
giacomo What do you mean with MyDeviceServer event loop? Do you mean your custom event loop? Or the message queue used by Tango to deal with Tango events? giacomo I don't know how you could ensure your callback is called from the same thread as MyDeviceServer::read_double_scalar() unless you are calling your callback directly from MyDeviceServer::read_double_scalar(). As I wrote before, CORBA will create a thread per client to handle requests (read or write an attribute, execute a command). So if you have 2 clients for instance, reading double_scalar attribute, MyDeviceServer::read_double_scalar() will be invoked from at least 2 different threads. So your assumption that it is safe to write a variable from the same thread as where MyDeviceServer::read_double_scalar() is invoked is wrong because this method can be invoked from several threads. You can use a message queue mechanism as you described, but you should protect access to the variables which are used in other threads or lock the Tango monitor before calling your callback and release the monitor afterwards. You can use the AutoTangoMonitor class to do that as described in the example given in the fire_archive_event documentation for instance: http://www.esrf.eu/computing/cs/tango/tango_doc/kernel_doc/cpp_doc/classTango_1_1Attribute.html#ab008123b44bdb2a13e2cd2c362617e1e
this being a pointer to your DeviceImpl device object. The AutoTangoMonitor takes into account the serialization model used by your device server as described in section 7.4.1.1 (serialization model within a device server) of the Tango Book. The Tango main thread does mainly the following: it listens for new CORBA requests, create one thread per client and dispatch requests to the different threads. It does not invoke MyDeviceServer::read_double_scalar(). Sorry if I misunderstood your use case again but maybe this will at least put a bit more light on the AutoTangoMonitor feature. Reynald
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new. |
|
|
---|---|
Hello Reynald, thanks for Your prompt replies. Maybe yes, still we don't get each other. As I wrote before, CORBA will create a thread per client to handle requests (read or write an attribute, execute a command). Take TangoTest as an example. Suppose we have 2 clients (jive) from 2 distinct machines. Let's say we read double_scalar from the two machines. I give (or have given, up to now) for granted that inside TangoTest DeviceImpl (and yes, I was referring to code inside DeviceImpl) I don't have to worry if the DevDouble class member attr_double_scalar_read is called from 2 (or N) separate clients (and so modified from 2 (or N) different threads. Actually, inside void TangoTest::read_double_scalar(Tango::Attribute &attr) I don't see any precaution taken against this. So, when I click "Read" on "double_scalar" in Jive, I expect that an event loop invokes the very same void TangoTest::read_double_scalar(Tango::Attribute &attr) in the client thread. All I want is to know how to invoke a method in this exactly very same context from a background thread when data is ready to be passed to DeviceImpl::my_callback(MyData newData), just as I imagine it is done when from the network a packet coming from Jive (with the double_scalar read request) is received and the void TangoTest::read_double_scalar(Tango::Attribute &attr) is invoked in the client thread. That's it! If it's too complicated, never mind, it doesn't make sense to write a library that makes things more complicated rather than simpler! Merci, Giacomo. |
|
|
---|---|
giacomo Indeed, because the Tango library is putting all the protections (using Tango monitor as I described) for you before these methods are called. But you should not call these DeviceImpl methods directly from another custom thread without asking for the Tango monitor first. giacomo If you take the TangoMonitor before executing the callback which will modify your class member attr_double_scalar_read for instance, you are safe. By taking the monitor, you ensure that NO OTHER REQUEST which concerns your device, devices of the same class in the same process or all devices in your process (depending on your Tango serialization model) will be executed in parallel as your callback. Simply do that in the part of the code which is invoking your callback:
Of course, you need to find a way to pass the pointer to your DeviceImpl to your thread (could be part of the data transferred from your message queue). If it's too complicated, never mind, it doesn't make sense to write a library that makes things more complicated rather than simpler! If I still didn't understand what you want to do, we could have a chat next week. It might be easier to understand each other… Have a nice week-end! Reynald
Rosenberg's Law: Software is easy to make, except when you want it to do something new.
Corollary: The only software that's worth making is software that does something new. |