Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Unable to message another actor (can message self)

Solved!
Go to solution

Hi all

I'm in the process of learning the Actor Framework and after studying the "Evaporative Cooler"-example I've gone ahead and tried to make a test/example my self. I am however unable to send messages to other actors. I can send messages from a given actor to the same actor. Below is the headlines of what I'm doing and the problem:

I have to actors: actor A and actor B. Both actors have task which only purpose is to launch a one button dialog with a message - actor A's method is called Task A.vi and actor B's method is called Task B.vi. Messages for these tasks have been created using the Actor Framework Message Maker and I've changed nothing compared to what the message maker made.

The purpose of my test/example is to have actor B be able to invoke Task A.vi (and get a dialog saying "Task A: <included message>") and invoke Task B.vi (getting a dialog saying "Task B: <included message>"). To do this actor B needs the message enqueuer of actor A.

To handle this "sharing" of actor A's enqueuer Actor B has as it's private data a Message Enqueuer. After launching the two actors in my main.vi I use an method of Actor B called Bundle A Message Enquere To Self.vi. This method simply takes an message enquere as input and bundles it by name into actor B ('s private data). This is an exact copy (as far as I can tell, but I could be mistaken since something is not working) of what is done in the "Evaporative Cooler"-example.

Actor B overwrites it's Actor Core.vi. This overriding vi calls it's parent method and presents two buttons ([Send to self] and [Send to A]) for triggering the sending of Task B to B itself or Task A to actor A. When sending to B itself ("Send Task B.vi") I use B's own message enqueuer - obtaining it by calling Read Self Enqueuer.vi. When sending to actor A ("Send Task A.vi") I unbundle B's private data called "A Message Enqueuer", which is (or should be) A's message enqueuer, and use that for seding "Send Task A.vi".

However the only thing that works is seding messages to B itself. If I press [Send to self] I get a nice popup with the message "Task B: <included message>". If I press [Send to A] nothing happens. I've tried to put int some dialogs in Do.vi of both Task B and Task A (saying "Hello from Task A do.vi" and "Hello from Task B do.vi") and only the dialog of Task B gets shown. So it's like Do.vi of Task A never gets executed.

Please find attached screenshots of important VIs and the VIs themselves. To run the project simply run main.vi it launches Actor B's actor core front panel which is the gui to send messages from. To stop it again press [Stop] on main.vi's frontpanel. The project is made in LabView 2012 with the bundled Actor Framework.

Thank you for reading through and hopefully helping me.


Best regards
Jonas CJ

0 Kudos
Message 1 of 20
(7,271 Views)

I didn't look at the code itself, but looking at the image of B's actor core, it looks like you have a race condition bug*. Remember that your bundle VI sends a message which contains A's NQR to B. That message is processed in actor core. Until that message is processed, the NQR B has (which is the one you actually use) doesn't point to A, but is null (and the send is probably throwing an error which you can't see because you have auto error handling disabled). Note that you have no way of getting the NQR that B received directly in your actor core override, because the "real" actor is inside the loop in the original actor core. What you can do (and you can see one example of it in the override) is create a user event which will send the NQR to your override.

* Edit - It's not actually a race condition, because the result is always the same. That was just poor phrasing. It's simply a misunderstanding of the by value nature of the actor wire.


___________________
Try to take over the world!
Message 2 of 20
(4,394 Views)

Thank you for your reply.

What does your abbreviation NQR mean? Something with queue i assume.

I have Automatic "Enable Automatic Error Handling" checked for Send Task A.vi and no error is thrown.

I can see your point about the time at which I unbundle A Message Enqueuer. I potentially do this before the Send Bundle A Enqueuer To Self is actually processed. I can also see that it's a misunderstanding of by value vs. by reference. If it was by refernece I should be able to introduce a 10sec wait and then unbundle, but that does not work (I have tried ). So I need to get a copy of the B actor at a later point and then unbundle. I'll look into this.

0 Kudos
Message 3 of 20
(4,394 Views)

NQR=Enqueuer.

JonasCJ wrote:

I have Automatic "Enable Automatic Error Handling" checked for Send Task A.vi and no error is thrown.

But you probably don't have it enabled for B:actor core (that probably comes from the original actor core), and that's where the unwired error terminal is. That's why you should handle errors.

JonasCJ wrote:

If it was by refernece I should be able to introduce a 10sec wait and then unbundle

And then you would create a race condition, which is bad.

You probably could send a copy of the entire actor (although you might create a recursive data type which LV would not allow), but I would not recommend that, because that's another source for many bugs. Just create a special event for that data or (and this is better) add another message to B which will send the data to A. Then, use the event to send that message to B and then the Do VI of that message will send the actual message to A.


___________________
Try to take over the world!
0 Kudos
Message 4 of 20
(4,394 Views)

tst wrote:

NQR=Enqueuer.

JonasCJ wrote:

I have Automatic "Enable Automatic Error Handling" checked for Send Task A.vi and no error is thrown.

But you probably don't have it enabled for B:actor core (that probably comes from the original actor core), and that's where the unwired error terminal is. That's why you should handle errors.

Point taken about handling errors. I just didn't go into it because I hoped this would be a fun little 20min test. I'll see if I can get Auto Error Handling to work or lean how to handle them properly myself.

tst wrote:

JonasCJ wrote:

If it was by refernece I should be able to introduce a 10sec wait and then unbundle

And then you would create a race condition, which is bad.

Nice of you to point this out if I had not know about race conditions. Thankfully I'm a minor in computer science and know that race conditions exists and that they should be avoided. Some times I just find my self taking a calculated risk for a small test like this (e.g. trying to test/prove your suggestion before replying for the first time).

0 Kudos
Message 5 of 20
(4,394 Views)

Another option is to launch actor A first, then give A's queue to actor B before launching B. Because actors are just objects before launching them, you can call any methods on them needed for setup. After calling the "bundle a message enqueuer", actor B can now be launched safely because the actor A queue is already in actor B's private data.

Message 6 of 20
(4,394 Views)
Solution
Accepted by topic author JonasCJ

tst wrote:

You probably could send a copy of the entire actor

Noooooooooo! Bad Tst! They'll think you're serious!

JonasCJ: There are two possible answers:

1) In B's Actor Core, it should send a message to itself that says, "Please send this to A." Then when B handles that message, it has the enqueuer for A and can send the requested message. This requires double staging the message, but it allows B to correctly handle the situation where it does not yet have A's enqueuer and can decide to save the message (inside itself) and send it later once A's enqueuer does arrive.

2) Create an event in B's Actor Core that is of type Message Enqueuer. When A's enqueuer is given to B, fire that event. In B's Actor Core, listen for that event and store the resulting enqueuer in a shift register (a shift register that is initialized to Not A Refnum).

0 Kudos
Message 7 of 20
(4,394 Views)

lukepike wrote:

Another option is to launch actor A first, then give A's queue to actor B before launching B.

Yes. This also works. My two previous solutions assume that these two actors are being launched indepenently and have to be sync'd up later.

0 Kudos
Message 8 of 20
(4,394 Views)

AristosQueue wrote:

tst wrote:

You probably could send a copy of the entire actor

Noooooooooo! Bad Tst! They'll think you're serious!

The only reason I mentioned that was because it was suggested as a possible solution. By "probably could" I meant "it's probably technically possible" (although, like I said, that might create a recursive data type), but you're right that I could probably have put more of an emphasis on how this could go wrong. You know it's sometimes hard to gauge exactly what you should or shouldn't say.

Incidentally, I don't want anyone to discount the idea out of hand. It's a bad idea for something like this, but that doesn't mean that there aren't places where it might serve as a legitimate solution. Quick example - you have an actor and you want to have another loop which will have immediate access to a recent copy of the actor. You could do that by occasionally sending a copy of the entire actor to that loop, with the knowledge that the value might be stale. If everything in the actor is by-value, that might be acceptable. Of course, there are other ways of doing this, but this is always a cost/benefit analysis.


___________________
Try to take over the world!
0 Kudos
Message 9 of 20
(4,394 Views)

tst wrote:

Incidentally, I don't want anyone to discount the idea out of hand. It's a bad idea for something like this, but that doesn't mean that there aren't places where it might serve as a legitimate solution.

I cannot fathom any situation in which this is a good idea. None. The methods on an actor -- which comprise the sole interface to that actor's data and behaviors -- are designed with the intention of having the actor run asynchronously and receive information via messages. Calling those methods directly is likely to circumvent the intended design of the actor. Alternatively, overloading the actor's interface with another set of methods intended for this purpose sidesteps one of the principles of good class design: that the class serve a single, cohesive purpose (bad rephrasing of the original principle).

If you want to share information about an actor with another actor, bundle that information in to a type of its own (a class or cluster) and insert that.

0 Kudos
Message 10 of 20
(4,394 Views)