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
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?

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
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.

You understood well.

giacomo
So, how to post events in the (main) MyTangoDev thread, so that I can safely access data posted from the background threads?

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.
Reynald
giacomo
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.

You understood well.

giacomo
So, how to post events in the (main) MyTangoDev thread, so that I can safely access data posted from the background threads?

I don't see how you could avoid dealing with mutexes in a multithreaded environment.

Of course you need them!
But when two threads exchange data, they can use message queues, which are mutex protected. Then one thread notifies the other that there's data in the queue. This is what I usually do when dealing with GUIs and background activities. For this I'm asking you how to access the message queue of the device server event loop so that the attributes (variables) declared in MyTangoDev class can be accessed without protection.

Let's have

MyDeviceServer class with a DevDouble var;

MyBackgroundActivity, that generates a DevDouble at random every 3 seconds.

I want this, in practise:

/* 1 */
MyBackgroundActivity::make_random()
{
DevDouble rand = random_bla_bla()…;

/* HERE I NEED A WAY TO PUT THIS DOUBLE IN A QUEUE AND NOTIFY MyDeviceServer event loop */
my_device_server->postEvent(rand);
}

/* 2 */
a callback MyDeviceServer::resultEvent(DevDouble data) to be invoked in the MyDeviceServer thread (same as where MyDeviceServer::read_double_scalar() is called) so that it is safe to write:

var = data;



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
giacomo
/* 1 */
MyBackgroundActivity::make_random()
{
DevDouble rand = random_bla_bla()…;

/* HERE I NEED A WAY TO PUT THIS DOUBLE IN A QUEUE AND NOTIFY MyDeviceServer event loop */
my_device_server->postEvent(rand);
}

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
/* 2 */
a callback MyDeviceServer::resultEvent(DevDouble data) to be invoked in the MyDeviceServer thread (same as where MyDeviceServer::read_double_scalar() is called) so that it is safe to write:

var = data;

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

{ 
    Tango::AutoTangoMonitor synch(this);
    att_temp_seq.set_value (temp_seq, 100);
    att_temp_seq.fire_archive_event (); 
}

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).
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.

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
Hello Reynald, thanks for Your prompt replies.
Maybe yes, still we don't get each other.

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.

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
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.

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:

// Get the data from the message queue
MyData data = getDataInAThreadSafeWayFromMessageQueue();
// Call the callback
{ 
    Tango::AutoTangoMonitor synch(dev); // dev is the DeviceImpl pointer to your device
    MyCallback(data);
}

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.
 
Register or login to create to post a reply.