Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Time-Delayed Send Message; notifier still alive after message sent?

Hi,

 

I like using Time-Delayed Send Message (TDSM) a lot. It is really handy to ask someone to do something in a bit, not now. I like to use TDSM when I want to check if some state has been achieved. States don't change much and I don't need to know the moment it does, just in a little while is fine, when you're ready, take your time. I have found that doing infinite copies on TDSM is a bit risky though; if the responder is slower than the asker then the system gets... slow, plus I need to manually kill the TDSM.

 

So I have taken to sending only 1 message using TDSM and then determining if another needs sending. For example, if the state is not met, ask again, in a bit. This is great, but it opens me up to more than one parallel cycle of checking state if I ask again while the first TDSM is waiting. This shouldn't happen, but I am paranoid! So to avoid parallel cycles, I implemented a 'check if you're already waiting to TDSM' using a Check Status call on the TDSM notifier, see snippet. An error 1 from the Check Status means there is no notifier so no waiting TDSM, so go ahead and TDSM. The no error case does not send the TDSM and reports to me, because I am paranoid.

 

delayed check.png

 

It turns out that that sometimes (mean time to happen being 10s of s) the notifier is alive but the message is not received. This means that the notifier must still be alive after the message has been sent (given that I do not do any 'Stop further copies' malarkey).

 

So, my questions are these; is the lifetime of notifiers wrt to Check Status longer than one would expect? Presumably Destroy Notifier acts quickly, but is there a better way to check the status of a TDSM? I could do a Stop further notifications and then call the TDSM again, overwriting the old TDSM with the new one. Is this better? I figured that preserving the old one was the way to go.

 

I figure a Publish-Subscribe model could be used, but I have not spent much time looking at that yet.

CLA - Kudos is how we show our appreciation for comments that helped us!
0 Kudos
Message 1 of 9
(1,345 Views)

I have done some playing and found that putting a Stall Data Flow.vim for 5ms before the Check Status is sufficient to mean that the issue effectively doesn't. I think 5ms is small on the scale of how I use TDSM, so I think I will stick with that for now. I don't like including stalls as a rule though as it locks Actors when they might be supposed to be doing something else.

CLA - Kudos is how we show our appreciation for comments that helped us!
0 Kudos
Message 2 of 9
(1,335 Views)

@MaxJoseph wrote:

I like using Time-Delayed Send Message (TDSM) a lot. It is really handy to ask someone to do something in a bit, not now. I like to use TDSM when I want to check if some state has been achieved. States don't change much and I don't need to know the moment it does, just in a little while is fine, when you're ready, take your time. I have found that doing infinite copies on TDSM is a bit risky though; if the responder is slower than the asker then the system gets... slow, plus I need to manually kill the TDSM.

Not sure what you are implying by 'infinite copies on TDSM being risky'. Under the hood, it is just a looping mechanism to send the same message periodically until your code chooses to stop the loop by sending 'Stop all further copies' notification. Can you elaborate on the risk you found?

 

In any case, you could simply have the TDSM configured to send a single copy of 'self-message' to checkWhatEverIsToBeChecked.vi. This VI would check for its actor's state change; and if not detected, enable a new identical TDSM for the next check. It is prudent to save the Notifier refnum in the actor's state, so that 'Stop all further copies' can be issued when the actor stops.

 

You should not need to check the TDSM's notifier status at all. That could lead to a race condition between when the TDSM loop ends and before it invokes its 'Release Notifier' internally, even if this gap is in the order of nanoseconds.

0 Kudos
Message 3 of 9
(1,317 Views)

Ah, risky as in messages get queued even if they aren't being read. So I can end up with a lot of messages in a queue which take a long time to clear.

 

I did look at using self-addressed messages in this context. I thought it wouldn't work here, but I may have gotten in wrong. I have two actors; A (requester) and B (receiver). A wants to know the state of B so that A can decide what happens next to A. I want A to send a message to B and B to respond with the state of B by sending a message to A. I want A to check B's state periodically.

 

Ah, do I make a child of Self Addressed Message who's do.vi calls the Check method. Then A sends the Self Addressed Message child to B who then opens the message and sends a specific message to the child?

CLA - Kudos is how we show our appreciation for comments that helped us!
0 Kudos
Message 4 of 9
(1,313 Views)

I think TDSMs are fine for what you're trying to do, but I agree that not letting them run unattended is better. We also have created a mechanism so that you can't generate a lot of unprocessed messages.

 

It looks like you're using LV2022 - any reason you aren't using interfaces to send messages between actors A and B? What's the relationship between A and B in the actor launch tree?

CLA CLED AF Guild
0 Kudos
Message 5 of 9
(1,299 Views)

@MaxJoseph wrote:

Ah, risky as in messages get queued even if they aren't being read. So I can end up with a lot of messages in a queue which take a long time to clear.

 

I did look at using self-addressed messages in this context. I thought it wouldn't work here, but I may have gotten in wrong. I have two actors; A (requester) and B (receiver). A wants to know the state of B so that A can decide what happens next to A. I want A to send a message to B and B to respond with the state of B by sending a message to A. I want A to check B's state periodically.

I understand now - This is the classic 'push' vs 'pull' type of messaging. If your design allows for it, you could set up Actor B own the responsibility of notifying Actor A when its state changes. Actor B then pushes a 'state-changed' message to Actor A initially and on change. The message's payload could include whatever data Actor A needs. In the meantime, Actor A could be doing other things; and process accordingly when it receives the aforementioned 'pushed' message. There should be no need for polling at all, TDSM or otherwise.

 

Re. my reference to a 'self-message' via TDSM, I should have clarified... That is a technique for when Actor B is designed to periodically send its state-laden messages to Actor A (which updates Actor A's class private data), and another check.vi monitors for the state change when invoked via a TDSM. (In this case, the TDSM is configured to send the message to self, i.e. Actor A of your example. The self-messages above do not refer to the Actor Framework's Self-Addressed Message.)

 

As implied in the above two paragraphs, it's generally cleaner to let Actor B to decide when its state changed; and accordingly send a message to Actor A. Depending on your implementation of state, sometimes the name of the message itself may be meaningful enough, without a need for payload. E.g. Send 'engineHasStopped', 'tankIsEmpty', 'sequenceHasCompleted', etc.

0 Kudos
Message 6 of 9
(1,299 Views)

I gather that you are having an actor A send a time delayed request to actor B, with B responding to A?  In that case, B's response tells you that your previous TDSM cycle has completed and you are free to send the next delayed request.  You just need a Boolean in A to track if a request is in flight.

0 Kudos
Message 7 of 9
(1,283 Views)

TL;DR: What I strongly suspect is happening is that the Get Notifier Status node is checking the notifier status is good and, before the code can even evaluate the ? terminal of the Case Structure, the notifier is being killed, so you aren't restarting the notification. 

Get rid of the status check and find a different way to do your operation. 

 

Details: 

In my experience of years and years of owning notifiers/queues and getting bug reports about them, any code that uses the Get Notifier Status for anything other than pure display of status has a race condition in it. THIS IS NOT A BUG WITH THE NOTIFIERS. It is a fact of multithreading that you can get an absolutely valid answer in one clock tick and have that answer be false in the very next clock tick before you ever act on the information. 

 

The only safe way to decide whether or not to do a given action on a notifier is to do the action and check for an error afterward. Same applies to a queue or any other refnum that you use "Is Not A Refnum" to check. I wrote all this up in detail years ago: 

https://lavag.org/topic/15546-are-you-misusing-the-not-a-refnum-function-and-putting-your-app-at-ris...

 

Get rid of the status check and find a different way to do your operation. 

 

I'm not sure what you mean by "given that I do not do any 'Stop further copies' malarkey". That "malarkey" is the thread-safe way to do what you're wanting to do. If you don't want to do that, then you should only send one message and construct your own mechanism for sending a second message rather than using the built-in mechanism. 

Message 8 of 9
(1,275 Views)

Many thanks for the interesting replies, all. I will reply in turn...

 

CaseyM, A is one of several potential controllers for B, which is a common tool. All of the controllers are siblings of A, and unrelated to B except that A and B are tightly coupled. I do like using interfaces (although I did like abstract messages too), but have found them redundant here as I put the A-B interaction methods in the parent classes of A-Parent and B-Parent. I think of the parent classes as being effectively interfaces.

 

Dhakkan, I took another look at self-addressed messages (by the by) and decided that I may have missed the need to overwrite the do.vi with a call to Check XYZ on B. I think I struggled with working out how self addressed message was supposed to call a method on B when it appeared to only provide a way for B to call the response method on A.

 

I get the pattern of B telling A when state has changed. I can see how this would save a lot of back and forth messages. I didn't do this because I didn't want B to know when the state was 'correct', and therefore when the message should be sent, I wanted A only to be able to determine that. This may have been overcomplicating the matter! I will look again at having B report state to A, but since there are a lot of potential states I might be interested in, and I want to avoid unnecessary coupling of processes within B, maybe I can get A to tell B which state A is interested for the time being, and B can report that state until A says otherwise.

 

DrJDPowell, yes, you are right. I don't want to bomb B with requests from A, so I use a TDSM so both A and B can use the interim to carry out other processes. I do like the simplicity of storing the in-flight status of the TDSM in A. This should be easy to implement in the A-parent. Thanks!

 

AristosQueue, yes, I think the Stall Data Flow.vim fixes the 'issue' because it ensures the TDSM has fully closed out the destroy process. I can see how this is a feature of the notifier rather than a bug. I think DrJDPowell has the right solution here, store the in-flight status locally in A rather than ask the TDSM whether it has completed.

 

On malarkey, I mean only that I never do any sneaky 'Stop further copies' in my code that would mean that the notifier could both be valid and no messages would be sent/received out of TDSM. If the TDSM was waiting to send a message, I checked the notifier status (getting no error) and I sent 'Stop further copies', then the above situation would be achieved. I thought it relevant to mention because someone might point out that perhaps I had just misdiagnosed my problem!

CLA - Kudos is how we show our appreciation for comments that helped us!
0 Kudos
Message 9 of 9
(1,260 Views)