Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Actor Principles, Complexity and Class Count

I'll respond to the easy comment... the rest has to wait until after I get some real work done.

jlokanis wrote:

I think another difference is I don't think of an actor as an API.

As a wise man once said to me... "How's that working out for you?" 

An actor *isn't* an API, but all actors *have* APIs.  Whether that API is defined by the messages you can send to it or the methods of the Actor subclass depend on the implementation details, but the API is there whether or not you choose to recognize it.

0 Kudos
Message 21 of 47
(1,448 Views)

Daklu wrote:

An actor *isn't* an API, but all actors *have* APIs.

I agree completely with that.  But to me, an API is a tool box.  And Actor is a carpenter who owns a tool box.  You can tell the carpenter to build a birdhouse.  The carpenter can then tell himself to cut with a saw, pound nails with a hammer, etc.  So, yes, an actor has an API so other actors can tell it to do something, but it can also tell itself to do things and it has to do those as self-messaged steps or it will ignore the boss while it is working.  An actor cannot do everything that needs to be done in one message and listen for new messages at the same time.  You may argue that the doing should be done in helper loops.  And that is fine but the actor still need to be doing multiple steps without outside (API) messages directing each step.

A good example is a test executive.  The top actor can tell the test sequencer actor to go test a DUT.  The sequencer need to load the test sequence, execute each step, store the data and determine the next step in the sequence.  All the while it must be telling the UI to update its display and be listening to the top actor for an abort command.  The sequencer is not an API, it is an engine.  It can be told things but it also can take actions on its own based on the outcome of previous actions.  You can argue that command pattern messaging makes this more complex, but even in your Agile Actors, you cannot listen for new messages while responding to a message.  You still must put the 'Do' in a case or hand it off to a helper.

-John
------------------------
Certified LabVIEW Architect
0 Kudos
Message 22 of 47
(1,448 Views)

jlokanis wrote:

So, what if you want to respond to a cancel or exit message while the initialization process is running? 

Like he said, redefine "initialized" to mean the initial state, and maybe add a ready state where it is ready to handle normal requests from the caller.

Your business requirements seem to indicate the duration of A, B, and C combined is too long for the actor to be unresponsive during initialization.  Quit trying to figure out how to fit the requirements into your model and instead change your model to fit the requirements.

jlokanis wrote:

I guess my requirements:

  • Perform a multi-step process with the ability to interrupt
  • Allow each step in the process to be re-used at a later time by the actor.

Cannot be implemented without a high degree of complexity.

For any problem there is some amount of complexity that is required to get a solution--this is inherent complexity.  Since interrupts don't exist in dataflow enabling interruptability in Labview requires more inherent complexity than in some other languages.

Whether or not it requires a "high" degree of complexity is a subjective call, but it doesn't strike me as being unnaturally high for experienced Labview programmers.  If you think your solution is excessively complex, my guess is your abstraction layers (or APIs) and responsibilities should be reviewed.

0 Kudos
Message 23 of 47
(1,448 Views)

jlokanis wrote:

So, what if you want to respond to a cancel or exit message while the initialization process is running? 

First, consider NOT doing that.  If you want a robust actor, you'll need to make sure that ANY message can be consistantly handled, not just cancel or exit.  Even cancel or exit can be problematic; you'll need to verify proper operation on receiving cancel at each stage of initialization.  That's a big test burden that I wouldn't automatically take on.  If you do need, realize that at the first point where your actor can receive an external message it must be fully initialized (with valid vales for all internal state variables), even if you consider later steps to be "initializating" (they aren't, because they are not initial).

0 Kudos
Message 24 of 47
(1,448 Views)

The following is sometimes a good idea, IMHO:

Why not just expose A, B, and C and make the calling actor responsible for invoking that sequence of messages?  Or invoke A, B, and C when you create the actor prior to spinning up the main message handling loop?  (FWIW, my actors almost never expose public "Init" messages.)

Suppose I have Actor Alpha that takes a long time to handle message X. The first question I ask is, "Can the handling be broken into separate steps?" If so, then the natural step to me is to create a nested actor that can handle messages X1, X2, X3, ..., Xn, and then to have Alpha send each of those messages in sequence. The nested actor is now churning through those messages and is interruptable if Alpha sends it a high priority Stop message.  Alpha is back to listening to messages from its own caller and from the nested actor. The nested actor will tell Alpha when it is finished.

This works great as long as Alpha doesn't need to block for the duration of the handling of message X. In other words, if there are more messages waiting in Alpha's queue and it needs to be done with X before it handles those messages, then you need a more complex architecture... something that dequeues messages, checks if it is something that can be done now, and, if not, holds onto them as internal Alpha state data until the nested actor reports that it is done. In LV 2013, you can put such a "I'm in a state where I need to dequeue and defer" into your Receive Message.vi override.

If you follow this solution, strongly consider making the nested actor a private class inside the same library as Alpha. That removes a lot of the objections about making the caller have knowledge of the nested actor. The nested actor is a part of the caller and is justifiably intimately connected. This is also a scenario where I would have the nested actor send a specific message meant for the caller (the full coupling version of response messaging).

0 Kudos
Message 25 of 47
(1,448 Views)

jlokanis wrote:

So, what if you want to respond to a cancel or exit message while the initialization process is running?

If that's true, then your initialization process is probably doing more work than just initializing the *actor*. It's probably initializing hardware or a network system or something. At that point, it is no different than interrupting any other message with a long handling period... see my previous post for one way to solve that.

Somewhere, lost in the early days of AF development, someone made a suggestion about giving an actor the ability to only wait on its high priority queue when it as in a particular state, in order to prevent the need to create an internal buffer of normal priority messages while still listening for high priority stop. My memory is that the community felt this was not needed. Is it worth reconsidering now that we've had a few years of using AF?

0 Kudos
Message 26 of 47
(1,448 Views)

In my specific case, I am reading data from a database.  There are 3 separate API calls that I need to perform.  These can take a varying amount of time depending on the size of the data and the speed of the network/database.  Between each call, I want to update the UI Actor to give the user a progress indication.

Later, I may wish to reload some of this data from the database, replacing the data I read and stored in the actor at init time.

I am leaning toward converting the sub-actor into a helper loop and controlling it with a simple queue.  I can then eliinate the need for a class for the helper loop and message classes to control it.  Also, since it is part of the parent actor, I can send out messages to the UI actor directly from the helper loop instead of having to message the parent's message handler and then have it in turn update the UI.

I will only need a single message in the parent to return the read data and a variable that indicates what data is being returned.

As I stated in the first post of this thread. I am really trying to reduce complexity and message class count here.  There are multiple places in my application where I need to do simular things and need to avoid class bloat.

I think my takeaway from this discussion is some complexity is inevitiable and my design turned out to be just as good as others have tried.  And don't create actors with init messages, instead have the actor init itself on startup without any message classes being needed.  (still need to work out error handing in this case).

Thanks everyone for the good feedback.  It has been helpful!

-John

-John
------------------------
Certified LabVIEW Architect
0 Kudos
Message 27 of 47
(1,448 Views)

jlokanis wrote:

(still need to work out error handing in this case).

If you do it in Pre-Launch Init.vi, if you return an error from that function, the actor will never launch in the first place and the caller will get an error coming out of Launch Actor.vi. That's the easiest place to bottleneck initialization.

0 Kudos
Message 28 of 47
(1,448 Views)

John,

Regarding Init. I put setting initialial values and that sort of thing in Pre Launch Init. I override actor core and place messages to that actor for initializing hardware that needs to happen first and with error handling because the message is dequeued in the core.

I use a template where I send the messages, some of the methods that get called are dymanic dispatch and some static. I actually have an initialize method that is DD and I send that message to all of my components upon there actor core being run.

Casey

Casey Lamers


Phoenix, LLC


casey.lamers@phoenixwi.com


CLA, LabVIEW Champion


Check Out the Software Engineering Processes, Architecture, and Design track at NIWeek. 2018 I guarantee you will learn things you can use daily! I will be presenting!

0 Kudos
Message 29 of 47
(1,448 Views)

Casey: Be very careful with that approach. It is a race condition! Once you are in Actor Core, your caller can already have sent you messages... those would be in the queue *ahead* of your init messages. If you're going to send init messages to yourself, you need to send those *inside* Pre-Launch Init.vi.

Pre-Launch Init.vi is the only place where you can guarantee that you are ahead of any other system. No one but yourself has the ability to send you messages right then. But leave that safe zone and you have no guarantees.

0 Kudos
Message 30 of 47
(1,448 Views)