Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Is sending message to a nested actor from helper loop of root bad code?

Solved!
Go to solution

Hi Guys,

 

I've got a style (maybe substance?) question. When I do some process in a root actor's helper loop, which produces some result that a nested actor should know about, is it poor code to send a message to that nested actor directly from root's the helper loop?

 

While writing this out, my feeling is becoming that such code is poor, because in root actor core I'd have to split the root actor's class out to the helper loop, and then unbundle the nested enqueuer out. Later on this nested enqueuer's reference could be changed? In that case, the nested enqueuer's reference might be different in root's main loop vs helper loop unless it was specifically maintained. Sounds like a recipe for issues...probably better just to use send to self from root's helper and forward the message to nested from their. 

 

Thoughts?

 

Thx,

 

-wavepacket  


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 1 of 18
(3,511 Views)

I think your intuition is right.  You identify a practical reason (synchronizing the potentially volatile enqueuer) I also feel like the very generic "single responsibility principle" of SOLID design is applicable here.  Your helper loop should have one mission which more or less always boils down to watching for some sort of an event (user interaction, timeout, polling something, ...).   When that event occurs, the proper response is to let the actor (self) know that the event has occurred.  What to do when that event occurs (e.g. forwarding it to nested actors) is the domain of the message handler for that event.  Yes you can get a minor performance optimization by cutting out that middleman, but, you should probably only do that if the chain of message passing has been shown to be a performance bottleneck.

0 Kudos
Message 2 of 18
(3,499 Views)

I consider the helper loop(s) to be 100% coupled with their Actor, which means it's fine to share enqueuers & implementation details between the two. As such, there's no problem with the helper loop sending messages directly to a Nested (or the Caller). However, you still need to ensure the code works; if you expect your Nested queue will change at runtime, you need to account for that. You can either synchronize the new enqueuer down to the helper somehow, or route the helper's messages through the Actor, like Thomas said.

0 Kudos
Message 3 of 18
(3,487 Views)

In general, I have no problems doing this. The nested actor's enqueuer is already generally on the block diagram of the Actor Core -- that's where Launch Nested Actor is commonly called, so there's no splitting the root actor wire.

 

Unless there's some state condition in the actor itself that gates whether or not to talk to the nested actor (and that's rare), I often go all the way and just let the helper loop manage the nested actor entirely for everything except Stop message.

0 Kudos
Message 4 of 18
(3,485 Views)

@AristosQueue (NI) wrote:

In general, I have no problems doing this. The nested actor's enqueuer is already generally on the block diagram of the Actor Core -- that's where Launch Nested Actor is commonly called, so there's no splitting the root actor wire.

 

Unless there's some state condition in the actor itself that gates whether or not to talk to the nested actor (and that's rare), I often go all the way and just let the helper loop manage the nested actor entirely for everything except Stop message.


Question from my naivety, Actor core itself is different from the "main actor loop" maintaining root's state right? I have the understanding that they are separate, which leads to my worry that splitting the actor wire to the helper loop (so the helper loop has nested's enqueuer) will lead to copy and possibility of stale actor data. 

 

Perhaps I'm missing something low level.


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 5 of 18
(3,474 Views)

@OneOfTheDans wrote:

...if you expect your Nested queue will change at runtime, you need to account for that. You can either synchronize the new enqueuer down to the helper somehow, or route the helper's messages through the Actor, like Thomas said.


^I think we're in agreement - the possibility for stale actor data exists so if the enqueuer could change at runtime the helper loop will have to get the new queue somehow. For example, perhaps root will fire an event which updates a shift register in the helper loop. This still feels like I have multiple sources of truth though (the queue in root, and the copy in the helper loop) -- my code smell is going off a bit if I'm honest. 


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 6 of 18
(3,468 Views)

@WavePacket wrote:
Question from my naivety, Actor core itself is different from the "main actor loop" maintaining root's state right?

Yes. Actor Core has a Call Parent Method node on its diagram. The main actor loop is inside that subVI. The helper loops are directly on the diagram of Actor Core (I suppose you could put them also into subVIs, but that rarely happens).

 


@WavePacket wrote:
I have the understanding that they are separate, which leads to my worry that splitting the actor wire to the helper loop (so the helper loop has nested's enqueuer) will lead to copy and possibility of stale actor data. 

Forking the whole actor wire would be bad. Your instincts are good there. But we aren't talking about that. We're talking only about sharing the nested actor's enqueuer reference with both the actor and the helper loop. All you need in the helper loop is the enqueuer object and not the caller actor object.

 

0 Kudos
Message 7 of 18
(3,467 Views)
Solution
Accepted by WavePacket

@WavePacket wrote:

^I think we're in agreement - the possibility for stale actor data exists so if the enqueuer could change at runtime the helper loop will have to get the new queue somehow. For example, perhaps root will fire an event which updates a shift register in the helper loop. This still feels like I have multiple sources of truth though (the queue in root, and the copy in the helper loop) -- my code smell is going off a bit if I'm honest. 


Sure, if you're doing dynamic launching of nested actors and respawns and whatnot, then you almost certainly need to have the helper loops route their requests through the caller actor instead of talking to the nested actor directly (generally that's way better than trying to update the helper loop with a new nested enqueuer). But that's a highly specialized scenario and not common. Do not use a corner case to rule out a useful tool for the common case. 🙂

 

Remember that any class is supposed to be a coherent (in the technical sense) whole -- it is designed as one piece, with its various methods having awareness of what the other methods are doing. For example, we commonly write private methods that don't sanity check their inputs because the public API of the class is responsible for doing those sanity checks. That principle is important in this discussion:

  • If you've designed your caller actor to maintain a highly dynamic nested actor, you'll write the caller actor's Actor Core to pass messages through the caller actor.
  • But if you've designed a fairly static relationship, particularly one where Launch Nested Actor is right there on the block diagram of Actor Core, then you'll write your helper loops accordingly.

In both cases, you do maintain one source of truth, but you can put that source of truth in the most convenient place without worrying that an element that is independently designed is going to mess with you. The caller actor is coherently designed.

 

Does that make sense?

0 Kudos
Message 8 of 18
(3,461 Views)

Edit to remove as AQ replied with more details.


------------------------------------------------------------------------------------

Please join the conversation to keep LabVIEW relevant for future engineers. Price hikes plus SaaS model has many current engineers seriously concerned...

Read the Conversation Here, LabVIEW-subscription-model-for-2022
0 Kudos
Message 9 of 18
(3,457 Views)
Solution
Accepted by WavePacket

@WavePacket wrote:

This still feels like I have multiple sources of truth though (the queue in root, and the copy in the helper loop) -- my code smell is going off a bit if I'm honest. 


I think the Nested lifetime is the deciding factor here. If your Nested will be starting/stopping and will have different enqueuers at any given moment, it's easier to forward the messages through your Actor. But if your Nested has the same lifetime as your Actor (for example, a GUI subpanel that exists until the app shuts down), then there's no harm letting the helper send messages to Nested directly. And if there are many Nested subpanels, for example, this can be much cleaner code than using a dozen forwarding messages in your Actor.

Message 10 of 18
(3,454 Views)