01-23-2014 04:31 PM
AristosQueue wrote:
Having said that... if you tell people it is not a state machine and merely a message handler, although this may help with them understanding the message transmission part, so well described in your objection, you deny them a deeper understanding of the stateful actor object at the heart of the loop.
I didn't say it's not a state machine, just that it's not a queued state machine. Actors almost always have some aspect of data statefulness, and some actors are also behavioral state machines. I don't see how explaining an actor's MHL as "not a QSM" implies the actor is also stateless, nor do I see how it precludes one from explaining or using the state pattern in an actor implemented as a behavioral state machine.
AristosQueue wrote:
There's a favorite koan of mine that applies here:
Forgive me for not waiting a whole day to speak about the koan... you know I'm not afraid to disregard tradition. The student discovered the object "is" whatever it is being (or can be) used for. It is a walking stick, and a club, and a lever, and fuel for a fire, etc. It is the imprecision of language that gave the master the opportunity to enlighten the student. (i.e. What do we mean when we say something "is...?")
I've explained what I consider to be a QSM, but I'm rarely able to get others to explain what they consider a QSM to be. In your opinion, what are the necessary requirements for a loop to be considered a QSM?
AristosQueue wrote:
PS: Daklu, any need for atomic handling of messages "a then b then c without allowing an intervening z" can be achieved with the Batch Msg class.
Yeah, I've seen it before. Functionally it solves the problem, though I believe it encourages poor design. An actor's API is defined by the messages it exposes. APIs are easiest to understand and use when there's no overlapping functionality across functions. Each function does one and only one thing. Sometimes APIs have multiple levels--a low level set of functions for discrete operations, and a higher level set of functions for common use cases. Regardless, no functions should (imo) overlap the functionality of the other functions on that level.
If I see an actor that exposes a, b, and c, as discrete messages, but which also requires the sender to send a, b, and c as a batch message in certain situations, I'm going to take a long hard look at the design. I won't go so far as to say it should never be done, but my gut says that's a code smell and it's likely the result of not clearly defining the responsibilities of each actor.
01-23-2014 05:20 PM
I pointedly decline to recognize comments about the koan for now. *grin*
Daklu wrote:
I've explained what I consider to be a QSM, but I'm rarely able to get others to explain what they consider a QSM to be. In your opinion, what are the necessary requirements for a loop to be considered a QSM?
A message handling loop (MHL) is the consumer of a producer/consumer pair. In a MHL, data comes in and gets processed. An MHL does not imply any memory from one iteration to the next. They can be plain pipelining operations ("a string comes in, the handler removes all the punctuation from it, then passes it along") or it can be a more sophisticated data type (A search engine is a message handling loop: a search query comes in, the handler looks up the relevant web pages, sends back the results, waits for the next query to come in).
A queue-driven state machine is a specific kind of MHL -- it is an MHL that has memory of previous iteratoins. Specifically:
That is a queue-driven state machine to me. The AF actors meet every point of my criteria. A few actors are just message handling loops with no state behind them (the proxy actors that shuttle messages meant for other actors across the network, for example). But most are level-one sentient algorithms that have memory of past messages received.
Daklu wrote:
An actor's API is defined by the messages it exposes.
Oh, well, there's a marvelous point for debate. In my opinion, no, the actor's API is defined by the public methods of the class -- the same as for ANY OTHER CLASS. The message is the mechanism by which those methods are invoked by a caller. The presence or absence of a message class implies NOTHING about the API.
The starting point to my argument is the queue API in LabVIEW. There is an Enqueue Element primitive. There is no Enqueue Multiple primitive. If a user wants a thread safe way to enqueue multiple elements, they need me (personally, in my role in R&D) to write a new primitive -- something I've never gotten around to doing -- or they have to build some sort of meta-layer by pairing the queue refnum with a semaphore refnum.
AF actors escape one of the biggest problems of by-reference architectures: they provide a locking mechanism around multiple operations without the class having to create an explicit lock. That means that the designer of the actor can specify "this is what you can do with me" and leave it to the caller to decide on the atomicity. That is a massive benefit. Otherwise, the caller is at the mercy of the class designer, which often does not sufficiently take into account the locking needs of callers -- something I observe all the time with hardware APIs. This leads to people having to wrap a reference class in another layer in order to supply the locking desired. It creates a lot of junk code that simply isn't needed.
An actor shouldn't have to be designed any different from any other class. It creates its public methods. Those methods are then callable. It creates the message classes as a convenience so that if there are N modules that will use the actor class, they don't each have to create the commonly needed message classes. For the most part, messages and methods have a one-to-one relationship. But there's no reason they have to and many good reasons that some won't. And even those that do have a one-to-one relationship may be doing additional work in the Do.vi (like validating inputs or logging the message) that the method itself doesn't perform.
In that sense, the Batch Msg class is just a sometimes convenient way to avoid creating a new message class to call all N functions when the smaller message classes already exist.
01-23-2014 10:05 PM
AristosQueue wrote:
I pointedly decline to recognize comments about the koan for now. *grin*
I eagerly await my beating, honorable master.
AristosQueue wrote:
A message handling loop (MHL) is the consumer of a producer/consumer pair. In a MHL, data comes in and gets processed. An MHL does not imply any memory from one iteration to the next.
A queue-driven state machine is a specific kind of MHL -- it is an MHL that has memory of previous iterations. Specifically:
- A block of state data that is in a shift register on the loop and is potentially modified by each iteration.
- A queue (regular or priority queue) that allows communication with the loop by outside parties.
- An evaluation function that evaluates the incoming data from the queue to decide what action to take.
- The potential for the data in the shift register to alter how the system responds to that incoming data.
That is a queue-driven state machine to me. The AF actors meet every point of my criteria. A few actors are just message handling loops with no state behind them (the proxy actors that shuttle messages meant for other actors across the network, for example). But most are level-one sentient algorithms that have memory of past messages received.
I agree in principle with your definition of an MHL, and even agree an MHL does not automatically imply memory across iterations, though I will point out memory across iterations doesn't mean it is no longer a MHL. I also agree AF actors meet every point of your definition of a QSM. I disagree actors in general meet every point of your definition.
As you noted, not all actors require state data be maintained on a shift register. If an actor is a QSM, and a QSM requires state data on a shift register, what would you call an actor without data on a shift register? Your definition of an MHL is the closest match, but an actor could avoid maintaining any internal data on a shift register, yet still consist of a MHL and helper loops, so simply calling it a MHL doesn't seem right either.
Furthermore, saying an AF actor "is" a QSM implies the QSM encompasses the entirety of the actor, which we know isn't the case. Helper loops are part of the actor, but they are not part of the QSM. The only resolution I see is to say an actor has an MHL, and the MHL may be implemented as a QSM (as you have defined.)
(I also dispute #2 is a requirement for actors, but it's not important right now.)
AristosQueue wrote:
Oh, well, there's a marvelous point for debate. In my opinion, no, the actor's API is defined by the public methods of the class -- the same as for ANY OTHER CLASS. The message is the mechanism by which those methods are invoked by a caller. The presence or absence of a message class implies NOTHING about the API.
Wait... what?
Your comment confused me so I used LV13's project wizard to create an AF project. I think I see what you mean. The AF allows developers to construct new messages consisting of an arbitrary number and order of public methods exposed by the actor class, without modifying the actor class' code. Very clever, but I'll have to think on that for a while before I decide if it's a good idea or not. (As if my opinion mattered. ) Off the top of my head the same functionality can be accomplished by wrapping the actor in a facade or using a mediator, but that is likely a bit more work.
One of the tenants of actor-oriented design is actors only interact with other actors via messages. A component's API is defined as the set of entities (function calls, methods, messages, etc.) used to interact with the component. In the end I still maintain an actor's API is solely defined by the messages it sends and receives, simply because if there is no message or combination of messages that issues the requests you want, you're out of luck. Creating custom messages effectively extends the actor's API. I understand what you mean when you say an Actor Framework actor's API is defined by the class methods, but I think it is more accurate to say an AF actor's potential API is constrained by the class methods. The API itself is still defined by the messages.
I believe part of our disagreement relates to how we define actors. You define an actor as a class inheriting from the AF Actor class. If an operation occurs that is not contained in an actor method, it is not part of the actor. For example, assume I have created a custom message that invokes method A, does some calculations with the result, and then invokes method B. My guess is you don't consider the calculations part of the actor. It's message handling code that uses the actor and executes on the actor's thread, but doesn't belong to the actor, correct?
In my head all the logical threads an actor uses are encapsulated within the actor itself. Users aren't allowed to execute arbitrary code on the actor's thread (unless the actor exposes a message that specifically allows it, such as with plugins.) As an actor developer, I'm not sure how I can ensure an actor I have written will behave appropriately when users have the ability to add arbitrary computations to the thread my actor is executing on. Tradeoffs...
AristosQueue wrote:
AF actors escape one of the biggest problems of by-reference architectures: they provide a locking mechanism around multiple operations without the class having to create an explicit lock. That means that the designer of the actor can specify "this is what you can do with me" and leave it to the caller to decide on the atomicity. That is a massive benefit.
I understand the convenience benefit, but this locking shortcut can also be a massive risk. Blocking the message handling loop while multiple methods (and any additional arbitrary code) execute in sequence can also disrupt the actor's responsiveness to new messages, and depending on the system that may have a significant impact. I'm not opposed to giving users the ability to create custom messages for those situations where there is no other solution, but I'm not convinced it is a best practice or should be encouraged.
If a user wants a thread safe way to enqueue multiple elements, they need me (personally, in my role in R&D) to write a new primitive -- something I've never gotten around to doing -- or they have to build some sort of meta-layer by pairing the queue refnum with a semaphore refnum.
I want to personally thank you for never getting around to implementing this. It's hard enough deciphering legacy code with all the weird things people do. I can't imagine what kind of code people would write when given a false sense of security from a native batch enqueue primitive.
01-23-2014 10:19 PM
testingHotAir wrote:
Could you expand on how it's "dirt simple to avoid" sending self messages? I have a whole class of UI actors that send messages to themselves to handle front panel events and to update controls with calculation results, and I really can't think of an easy way to avoid doing that.
My response to this question was branched to this thread.
01-24-2014 03:06 AM
After reading trough the discussion here I am starting to wonder how I should implement the actor framework in my project. In my project I want to send a command to the hardware and process the response.
However since the hardware isn't any from NI (but in-house development) the response isn't always instantly and timeouts aren't uncommon (no card connected to that location.)
What I would like to do is to collect data from my hardware and send the collected data to the main UI. The collecting of the data takes about 10-20 seconds. In it's current form my function blocks it's actor. According to the presentation (linked in the start post) this is not done.
At a later time I'll also need to implement to a basic QSM.
I think the best action I can take is to use a helper loop that handles all the data collection and will post back as soon as it's finished. However that triggers the 2nd part of my problem. How to create a helper loop using the actor framework using the template shipped with LV2013?
Tim
01-24-2014 07:54 AM
Daklu wrote:
AristosQueue wrote:
Oh, well, there's a marvelous point for debate. In my opinion, no, the actor's API is defined by the public methods of the class -- the same as for ANY OTHER CLASS. The message is the mechanism by which those methods are invoked by a caller. The presence or absence of a message class implies NOTHING about the API.
Wait... what?
I only appreciated this just recently. Not sure what I think of it yet. The Actor doesn't encapsulate the thread it runs in.
01-24-2014 07:57 AM
A link to an older conversation on this.
01-24-2014 09:51 AM
TimBotsM wrote:
How to create a helper loop using the actor framework using the template shipped with LV2013?
I'm not an expert on the AF and can't give you specific details, but generally speaking...
01-24-2014 10:34 AM
AristosQueue wrote:
A queue-driven state machine is a specific kind of MHL -- it is an MHL that has memory of previous iteratoins. Specifically:
- A block of state data that is in a shift register on the loop and is potentially modified by each iteration.
- A queue (regular or priority queue) that allows communication with the loop by outside parties.
- An evaluation function that evaluates the incoming data from the queue to decide what action to take.
- The potential for the data in the shift register to alter how the system responds to that incoming data.
That is a queue-driven state machine to me.
I was thinking about this last night, and I don't think your definition matches up with the commonly-understood meaning. Your definition can be summarized as "a message handler with state." Stated like that, it is a component, or a building block, much like a while loop or event structure is a component.
Nobody ever says, "I'm building an application with a while loop architecture." That statement doesn't convey any useful information. Yet you frequently see people make similar claims with respect to QSMs. And while I can infer an application with a "QSM architecture" uses messages, case structures, and while loops, it doesn't give me any useful information beyond that. (No useful information about the application anyway... it does give me some useful information about the developer.) It's apparent to me that in common use the term "QSM" means more than the strict definition you provided above.
"QSM" (neither your nor my definition) places no restrictions whatsoever on how data is shared between loops, message chaining, or message handling loops. These things are critically important when creating a robust multi-threaded system. It is those contraints that make actor-oriented systems so much better than QSM-oriented systems. Actors are more than just a QSM, and I think users are best served when the differences between actors and QSMs are identified and explained.
01-24-2014 10:43 AM
drjdpowell wrote: I only appreciated this just recently. Not sure what I think of it yet. The Actor doesn't encapsulate the thread it runs in.
Yeah, I'm in the same boat. On the one hand it seems to give developers an opportunity to accidentally open a big can of worms. On the other hand, I tend to favor flexibility and be critical of design decisions that are overly protective.
Currently I'm leaning towards not liking it, since custom messages are primarily a convenience--they don't provide any functionality that users couldn't create on the own--and running arbitrary code on an actor's thread is an awfully big hole to drive bugs through.
I dunno... it's an interesting implementation, that's for sure.