10-11-2021 01:46 PM
Hi Guys,
I've got a quick question. I'm wanting to have an actor with a helper loop that's doing some long things in the background. These items "could" (but with only medium probability) generate something useful to the user. However, if the user sends something for processing, I'd like that handled immediately as that's highly likely to generate something useful to the user.
There are two options that I see, mind commenting on which is a better design?
Option 1 -- register the single helper loop for a high-priority user event (in addition to high-priority stop event used to close the helper loop) -- possible race conditions...
Option 2 -- instead use two helper loops, one for the background events and one for high priority user events (both loops now only have one high-priority event, being the stop event)
Any others I should consider?
Thanks,
-wavepacket
Solved! Go to Solution.
10-11-2021 03:34 PM
How exactly does your actor work? Does it receive a message, then do some processing on the data (which takes non-negligible time), then send a message somewhere else?
Since you say you're doing the grunt work in a helper loop, I don't think you need priority messages. Priority messages won't interrupt another message anyway; they just go to the front of the line. So if you have a message that takes a long time to process, a high priority message appearing won't stop the in-process one, it'll just be the NEXT one to fire when it's done.
If you are indeed doing the time consuming stuff in a helper loop, then your main message receiver (the Actor Core) will be responsive to "Hey this is an important message, process it now" messages anyway. It'll just need a way to either interrupt the helper loop to process right now or, if the helper loop only takes a little time to do each task, then perhaps it could just wait for the helper loop to be done with whatever it is it's working on.
I guess we could use a little more info about the architecture of the program. Where does the helper loop get its data if the user isn't actively sending it things? Are they automated messages or something?
You'd only need new Priority messages if, for some reason, a bunch of messages were already enqueued to your Actor and you needed to preempt them.
10-11-2021 03:35 PM
Option 3 -- when a request happens, your Caller Actor launches a new Nested Actor that does one slow item in the background. The Nested Actor stops itself when the item is complete (results returned via Last Ack). The processing probably still happens in the Actor Core (so that you can send AF Stop, if needed), but the Nested Actor can be dumb and doesn't need to manage events/priorities - it just processes and returns.
- If the slow items must be sequential, the Caller can keep an internal list. You can put UI priority messages at the front if you want.
- If the slow items can happen in parallel (sounds like it, based on Option 2), the Caller can simply launch all requested items whenever they come in, then track which ones are still processing.
10-11-2021 03:42 PM
@BertMcMahan wrote:
How exactly does your actor work? Does it receive a message, then do some processing on the data (which takes non-negligible time), then send a message somewhere else?
...
So specifically I'm creating an actor that sends queries off to a database. I'm planning on queuing up a bunch of background tasks which I'm not all that interested. However, if the user asks for something from the database, I do want to display that right now.
So my thought was to maintain two helper loops which could open two connections to the database. One of those loops would handle all the background stuff, and then the other one would run that priority query separately.
@BertMcMahan wrote:
...
If you are indeed doing the time consuming stuff in a helper loop, then your main message receiver (the Actor Core) will be responsive to "Hey this is an important message, process it now" messages anyway. It'll just need a way to either interrupt the helper loop to process right now ...
As an aside, I'm not actually sure how to interrupt an event that's in process of executing. I do now how to create a high priority user event, but that seems dangerous due to potential race conditions with my high priority stop message.
@BertMcMahan wrote:
...
You'd only need new Priority messages if, for some reason, a bunch of messages were already enqueued to your Actor and you needed to preempt them.
This is precisely my use case.
10-11-2021 04:12 PM
@OneOfTheDans wrote:
Option 3 -- when a request happens, your Caller Actor launches a new Nested Actor that does one slow item in the background...
So this suggestion interests me, particularly since my implementation is for a database which supports concurrent communication. If I have X queries to run to run, is there really a reason that I couldn't launch X copies of the nested actor and then have them all run in parallel?
I'll also have to face the question of who should be shutting down the database actor(s). Does the database actor shut itself down after sending the query result, or should the callee manage how many copies are alive. I haven't had to face the music on that type of question before.
10-11-2021 04:21 PM
Ah, good to know then!
So, cancelling operations can be done one of two ways. The first is to do it manually, and have any process periodically check to see if it needs to stop, then stop itself. For example, you could set up a Stop notifier, then at the beginning of each loop, run Wait on Notification with a timeout of 0. If there's a Stop notification there, it stops, otherwise it continues doing its thing. (There are a million ways to do this).
Option two is the nuclear one- you simply abort the VI or process. You get a reference ahead of time, then if you need to stop it, you kill it. This is generally to be avoided since you will exit in an unknown state.
In your case, you're doing database calls. I assume each call takes some finite amount of time. If that's all your actor is doing, then I think I'd process each request within the message- as in, without a helper loop at all. If you add a helper loop, then you'll need some way to enqueue the database requests between the actor core and the helper loop, then you'll need to have a regular message send a Priority message to the helper loop (as in, enqueue at front of queue) to get the results right away. This sounds like more work than just letting the AF message queue handle that for you. Each message does one database query. If the user wants something now, send it as a priority message.
This will get you everything except for interrupting a current request. How long do these background requests take? Is it possible to just wait until the current one has finished? If not, you'll have to find a way to cancel the request. I don't do much with databases but basically your toolkit will need to let you cancel an active request, which might be a little hard to do.
Thus, we get to the last option, which you did mention- dual loops. You could do this one of two ways; either two helper loops, where one is the "fast" one and one is the "background" one, but you still need to implement internal queues, or you could just launch two actors and let AF handle the queues for you. Store one enqueuer as the "background" one and one as the "user" one. They can both open handles to the same database, assuming your toolkit supports it.
How long is each database request? How many user requests do you anticipate happening at once? Would two actors work? You can make them the exact same actor, just launched twice.