Why Do Your Loops Run?
One of the key architectural rules I learnt in LabVIEW is what I always call in my head the “single source of synchronisation” rule. I need to make two opening statements on this:
- Someone/Something taught me this – Huge apologies I can’t remember the source of this but thank you whoever you are!
- Its name makes something simple sound very complicated; it isn’t!
It made an appearance in my post on architectural language so I wanted to expand on it. The quote there was:
A loop within an application component. It has a single reason to run.
Why Loops Run
By “run” what I mean is what triggers an iteration of that loop. I’ve identified these sources:
- Time – Intended to run every x ms.
- Event – Either a UI or User Event, the loop contains an event structure.
- Data – Basically a queued message handler but “data” means anything that means a loop waits for data. A data interface could be queues, notifiers, streams, DAQmx etc. Some external process is forcing us to wait for new data to be available.
The rule means, if one loop is trying to use two of these methods (or multiples of one of these types) then problems may occur. Examples might be:
- Using the timeout on an event structure
(did you know it won’t fire if user events fire, even if you aren’t handling them? Thanks Chris R for this demo!)Thanks Fab for the correction: This was a bug that has been fixed. - Using the timeout of a queue to perform a repetitive action.
As you can see – time is usually the conflict because the others are kind of obvious to avoid having in the same loop.
Why This Rule Exists
Put simply, in most cases where two of these exist there is a conflict which is hard to resolve consistently 100% of the time, mostly because the event and data drivers depend on external components which aren’t predictable.
Take the timeout case of the queued message handler. If you want to check a state about every 1 second, then the best case is a new external message once every 2 seconds. Then 1 second after that message arrives you perform the check. Maybe you will get a second check before the next message, maybe not. Most likely you will end up performing the check every 2 seconds. Perhaps that is acceptable?
However then an external component generates messages every 0.1 seconds. Now that check doesn’t happen ever, through no fault of yours! (In the context of the code in this loop).
But I Need To…
Perhaps there is a case where you want to break this rule. I have two solutions:
Proxy Loops
Use a second loop as a “proxy”. For example in the case above have a second loop which runs every 1 seconds and enqueues a message for the check.
Another example is when you have an API which generates messages on an event but you need it in a queue. If you have used the actor framework, this how you extend actor core for GUIs.
Break The Rule… And Be Very Careful
If you don’t see a way to design around this rule, then you need to know you are breaking it and be very careful. Most likely by adding some management code to a loop.
The example I could think of was if you needed data from two loops. In this case, you would write code to read from each queue. If one read timed out, perhaps you store the successful data and then try again, reading only the failed queue.
I don’t think there are many cases, but rules are about knowing when you break them and why!
Fabiola De la Cueva
May 17, 2018Hi James,
Starting with LabVIEW 2013, this statement is no longer true: “Using the timeout on an event structure (did you know it won’t fire if user events fire, even if you aren’t handling them?”
Back in CLA Summit 2011 in Austin, Justin Goeres was presenting Beyond State Machines. While presenting he showed how the code was registering for a cluster of events. Steen pointed out your quote above and half the room thought it was a bug and the other half thought it was a feature. There is no video of this but there is audio.
I believe it was a bug because it was fixed on LabVIEW 2013 (also in this version NI added fitting events as priority, flushing the event queue and the event inspector window). Now, the event structure has to register and handle an event for it to reset the event timer.
Regarding the second part of your statement ”
Using the timeout of a queue to perform a repetitive action.”
I just want to clarify that this is OK if as you said, the only thing that makes a loop execute is that timeout.
Next week at NI week we can discuss this. I will make sure to load a pre LabVIEW 2013 version, LabVIEW 2013 and LabVIEW 2017. We need to make sure NI did not reintroduce the bug.
If we don’t have time, I will make sure to post a video after NI week.
Regards,
Fab
James McNally
May 17, 2018Hi Fab,
That bug was from memory rather than testing so it may be that is incorrect and I shall correct the post. Thanks for the update!
Cheers,
James
Fabiola De la Cueva
May 17, 2018There is an alternative to this example: “The example I could think of was if you needed data from two loops. In this case, you would write code to read from each queue. If one read timed out, perhaps you store the successful data and then try again, reading only the failed queue.”
Use events instead. An event structure is like a queue that can have different types of data in it. If you have two separate loops generating data and for whatever reason you have to receive them in the same loop, send the data via user events and the event structure will handle the data in the order it receives it.
James McNally
May 17, 2018You are right – in this example events would be a much neater solution! I’m increasingly leaning on events as a core part of my architectures for many reasons, this is a nice example of one great benefit. Plus the proper typing as you have described is a huge bonus over trying to push multiple data types through one queue.