Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Suggestion: Get Rid of the "Actor-to-Caller" Queue

I looked at the newest Actor Framework last night and I have a few suggestions, but this is the biggest:

Remove the "Actor-to-Caller" queue from the framework completely.

I'm not sure why it is there as I couldn't find it actually used by any of the examples, but it seems entirely out of place in an implementation of an "Actor" pattern.   An Actor needn't have a specific hardwired relationship with its caller (this is not "Master/Slave").  It certainly could be that specific Actors might want to send messages to its caller, but this is no different than sending messages to an "error logger" or to any other Actor for any other reason.  If desired in a particular child Actor, a "caller-actor" connection could be set up in the same way chosen for any other connection, such as by sending the actor self-addressed messages (command-response) or by sending a registration message to the actor to receive notifications of events (publish/subscribe or Observer pattern).  Want to know when the Actor has shut down?  Register for notification of its "Shutdown" message.  Even if a more specialised connection is required for a specific child, such as if you actually do want a Master/Slave pattern, or want a dedicated "error logger" channel, this can easily be added into the child class, where it can be given a more descriptive name such as "Master's Queue" or "Error Logging". 

Having the "Actor-to-Caller" queue in the parent implementation of "Actor", and prominent in the Actor's launch code, is very confusing because the reader (well, me) thinks it's something central to the design when actually isn't.*

-- James

* This is not helped by some of the terminology such as the "Message Queue Pair" which I assumed was a pair of queues for two-way communication, but is actually a single queue with a pair of possible packagings (a design that also seems unnecessarily complex, but that's another post).

Message 1 of 24
(11,902 Views)

James: It took me a while to see this post. For some reason, I don't get community e-mails for top-level messages, only for replies. I'm still trying to figure out why that is.

As for your comments...

> An Actor needn't have a specific hardwired relationship with its caller (this is not "Master/Slave").

Depends upon your definition of "need". True, an actor doesn't strictly need a caller, but the Actor Framework is not just about making it easier to have free-running VIs. It is about making *provably correct* systems of free-running VIs. The actors are not meant to be lone VIs running in space. They are a tree of free-running VIs, with one root VI that spawns the rest. The actor-to-caller queue is used within the framework for the communication of the Last Ack message, and the correct exchange of queues is what makes possible some of the more complicated messaging scenarios.

The tree of actors creates the isolation of each actor so that it can only talk to the actor above it and to its own nested actors. No one outside of the actor knows about the existence of its nested actors unless that actor chooses to reveal that existence. That private encapsulation is a key part of the system that allows us to make stronger statements about whether a given actor will behave correctly within a system. If any actor can be messaged at any time by any other actor, you have the same problem that arises with global variables in any programming environment: no controls over who does what. With the tree restrictions, especially if you respect the messaging hierarchy and never share the queues around, we can make statements like "this actor will be able to do its work every five seconds because we know no one is ever going to give him a task that takes more than that to complete" because we can check all the possible callers and know what messages they send. In an environment of freely shared queues, such statements are impossible.

Far from removing that terminal, we are strongly considering upgrading it to a required input and making Launch Actor.vi return an error if it is Not A Refnum. By doing that, we think we can make much stronger asserts in the code about the existence of the caller, reducing the functions that have to worry about producing errors.

> thinks it's something central to the design when actually isn't.*

It is central if you accept that the tree is a desirable restriction upon communications.

0 Kudos
Message 2 of 24
(3,612 Views)

Ah, I was not aware you were trying to restrict the framework to only creating simple trees of actors (what I referred to as "Master/Slave").  This restriction is not part of the "Actor Model".   Personally, I don't think this is a good restriction as I would imagine more complex networks of actors might be sometimes needed, even if a simple tree would be most common.

> The actor-to-caller queue is used within the framework for the communication of the Last Ack message, and the correct exchange of queues is what makes possible some of the more complicated messaging scenarios.

What more complex scenarios?  I've been working with the "Observer pattern" and so far it seems quite flexible and powerful.  And one can always send a "This is your Master's queue" message to a Slave actor if needed.   The "Last Ack" could easily be incorporated as a reply to the Shutdown message.

> The tree of actors creates the isolation of each actor so that it can only talk to the actor above it and to its own nested actors. No one outside of the actor knows about the existence of its nested actors unless that actor chooses to reveal that existence.

But you already have that.  When a process creates an actor, only that process has the "address" of that actor, and it controls whether to supply that address to select other processes/actors, or whether to keep it private.   Thus, you already have encapsulation without a "Send-to-Caller" queue. 

>  If any actor can be messaged at any time by any other actor, you have the same problem that arises with global variables in any programming environment: no controls over who does what. With the tree restrictions, especially if you respect the messaging hierarchy and never share the queues around, we can make statements like "this actor will be able to do its work every five seconds because we know no one is ever going to give him a task that takes more than that to complete" because we can check all the possible callers and know what messages they send. In an environment of freely shared queues, such statements are impossible.

I'm not talking about globally-shared queues (which, I believe, does violate the "Actor Model"), just more complex networks than a simple tree.  An example: suppose Actor A is written to send the current temperature reading to its Master on the "Actor-to-Caller" queue, but then it turns out that Actor B also needs to know the temperature.  The Master can share the address of A with B, but it can't meaningfully share the "Actor-to-Caller" queue.   The only option would be to have the Master act as a repeater and forward the information on to B.  That certainly works, but there are other options.  With the Observer pattern, the Master could just register B to receive temperature updates direct from A; with replyable messaging, the Master could give A's address to B and B could query A periodically for the temperature. 

I don't think structures such as these prevent one from verifying the correct operation of an actor, as you still have limited and well defined access to it.  If the Master registers B as observer of A's temperature updates, actor B doesn't even have the ability to send a message to A, and A has no ability to send any message other than the temperature update.  A very limited interaction.

> It is central if you accept that the tree is a desirable restriction upon communications.

Leaving aside my claim that it isn't a desirable restriction, don't you have a problem with the replyable message (I forget what you call it exactly)? This makes it easily possible to break out of the restrictions of a simple tree into complex structures.  If you're going to have an "Actor-to-Caller" queue, shouldn't you restrict all outgoing communication to go through this queue? 

-- James

0 Kudos
Message 3 of 24
(3,612 Views)

My reply that follows is not a rejection of alternate architectures. It is an explanation of why this one was chosen. It is very possible to build stable systems around other actor arangements. I do not want to imply this is the only way that works, merely one way that appears to work.

The actor-to-caller queue is the basis of the system under the theory that "he who reserved the resource is responsible for cleaning it up." The caller spawned the actor; it's responsible for handling the shutdown of that actor. At minimum, the caller needs notification if the nested actor quits unexpectedly so it knows it is no longer responsible for the cleanup of that nested actor.

> If you're going to have an "Actor-to-Caller" queue, shouldn't you restrict all outgoing communication to go through this queue?

The tree is not a strict restriction, it's a starting point. In other words, the actors start as a tree and an individual app may choose to share the queue refnums around in a particular manner, establishing shortcuts across the tree. Such shortcuts make for more efficient messaging, but reduce the code reuse of any particular subtree. Since an actor initially has no connections other than its caller, that restriction is already in place, and the app may choose to weaken that wall by sharing the refnums around.

> don't you have a problem with the replyable message

As a matter of fact, I do have a problem with Reply Msg. 🙂 But others have convinced me that

a) there's no way to prevent a user from creating such a message and

b) there are a couple of times when such messages are useful implementations.

We do talk about the problems of such messages in the whitepaper. I've avoided using them entirely in my apps, but they do exist.

0 Kudos
Message 4 of 24
(3,612 Views)

I think, then, that you should make the desired master/slave tree structure more explicit in the documentation of the Framework.  I just reread "Using the Actor Framework" and though I can now see the intension, it is more implicit and found in later sections like "Implementing Actors Composed of Multiple Actors".  I didn't realize this before. Admittedly, this may be partly just me, as I've been working on a different actor/message framework that doesn't use an explicit caller/callee relationship. 

> As a matter of fact, I do have a problem with Reply Msg. 🙂

BTW, I didn't mean the synchronous nature of your reply message (aside: why sync only, surely it would be easy to use such messages asynchronously also?).  Instead, I meant the ability of processes other than the Caller to interact with the Actor.  So both "Reply Msg" and "Self-Addressed Msg" are equally problematic, as one can make arbitrarily complex non-tree structures with them. 

> The actor-to-caller queue is the basis of the system under the theory that "he who reserved the resource is responsible for cleaning it up." The caller spawned the actor; it's responsible for handling the shutdown of that actor.  At minimum, the caller needs notification if the nested actor quits unexpectedly so it knows it is no longer responsible for the cleanup of that nested actor.

Shouldn't an Actor clean up itself on shutdown?  Also, if one VI creates a resource (a queue, say) it doesn't get a notification if that queue is destroyed by another VI, until it tries to use the resource.   And, to push the Observer Pattern again, you can alway register to receive the shutdown message.

0 Kudos
Message 5 of 24
(3,612 Views)

> Shouldn't an Actor clean up itself on shutdown?

An actor should clean up its own pieces, including telling any other actors that it created to shut down.

How does an actor know when to shut down? If I have a Car actor that spawns both a Traction Control actor and a Motor actor, both of those should shut down when I shut down the Car. No one outside of the Car even knows that Motor and Traction Control even exist. Moreover, if Traction Control fails and shuts down early, who does it tell? It tells Car, which could then decide to shut itself down, institute emergency driving procedures, or possibly spin up the back up Traction Control (neat trick), or perhaps ignore the failure and allow the user to proceed with a more exciting ride.

0 Kudos
Message 6 of 24
(3,612 Views)

Ah, but what if the government brings in regulation requiring all car subsystems send direct status messages to an automobile equivalent of a flight data recorder?   Or that there be an independent "Backup Car" actor running that is able to seamlessly take control of Motor and Traction Control the instant Car develops a fault condition?  A simple tree is a great design, but what happens when you find a need to do something different?  Since "Actor-to-Caller" queue cannot be used to notify multiple processes, you will have to complicate the code of your subsystems with alternate comunication channels.  If the framework were instead based on an Observer Pattern, then the previously written and tested code for Motor and Traction Control could be used unmodified, as the higher-level code would just register Car, Backup Car, and Recorder to receive the notifications that each requires. 

-- James

0 Kudos
Message 7 of 24
(3,612 Views)

I haven't worked through the details, but I think you could add an observer layer as a decorator to an actor.  If you do it right, I think you could get away with writing a generic observer that can be used with any actor.

Yes, adding lateral messaging between actors (as opposed to up and down the tree) adds complexity, but so does allowing arbitrary messaging between observers.  I think it's a case of picking your poison.  Good encapsulation is going to impose some hiearchy and limit the scope of your messages. In our car example, Motor and Traction Control are going to be composed of object hiearchies that need to be isolated from each other.  Motor and TC may need to talk to each other, but Fuel Injector (part of Motor) and ABS (part of Traction Control) do not.

In the end, we do not claim that Actor Framework is the ultimate solution to all problems, though we do think it solves a lot of common architectural challenges in the systems we see.  If a straight observer pattern makes more sense in your problem domain, then by all means, use one.

0 Kudos
Message 8 of 24
(3,612 Views)

> Moreover, if Traction Control fails and shuts down early, who does it tell?

Looking at this again, I think I can better explain why I have a negative intuitive reaction to the "Actor-to-Caller" queue.  It is NOT Traction Control's responsibility to tell anybody it has shut down.  It's Car's responsibility to know if Traction Control shuts down.  Traction Control is only responsible for making this information available to be observed by Car, should Car choose to

In fact, Traction Control shouldn't even know Car exists! Traction Control is a reusable component that should respond to commands received and should publish information about itself.  It shouldn't know anything at all about the structure of the program at a higher abstraction level, such as the fact that it is on a branch of a tree with only one observing process.  In fact, it shouldn't know or depend on being observed at all.  Nor should it ever send a command to it's "caller"; it should reply to commands received, or publish information, but it should never expect a response from the higher-level code that is calling it.

0 Kudos
Message 9 of 24
(3,612 Views)

Just a couple of supporting comments:

1.  If you stick to the zero-coupling solution for messaging between actors, you can minimize the reuse hit in lateral messages.  You can make the reply message an attribute, to be set by the actor's owner.  That message can be a self-addressed message.  The actor then knows only that it is sending a message with a certain set of attributes, but knows nothing about the recipient, preserving encapsulation.

2.  I used the synchronous Reply Message in an application that supported web services, where query-response is required.  I'm not sure what else you would use in that instance.

0 Kudos
Message 10 of 24
(3,612 Views)