From LabVIEW Wiki
(Redirected from Occurrences)
Jump to: navigation, search

There seem to be misconceptions about how exactly occurrences work and what exactly the "ignore previous" parameter on "Wait on Occurrence" (WoO) does. The following tries to explain, in a long and winded way, how they (functionally) behave:

SHORT STORY: The following is the advice of LabVIEW's Chief Architect to use occurrences correctly: First build a system which polls continuously for whatever it is the occurrence will signal. You need to be able to determine the state of things without an occurrence. This creates a correct, but inefficient busy waiting implementation. Once that works, add an occurrence to alleviate the inefficient waiting. Do not remove the actual polling. In other words, use the occurrence only to indicate that "it is probably a good time to check on that thing...". You can see an example of this usage in this LAVA post.

LONG STORY: How Occurrences are "generated": When a VI is loaded (!) each "Generate Occurrence" function allocates exactly one unique occurrence. When the VI is running and this function is called it simply returns this one occurrence -- no matter how many times it is called (try putting one in a loop and examining its value using the probe, it will always have the same number). If you stop the VI and run it again, you will get the same value; only removing the VI from memory and loading it again will give you a "fresh" value.

How Wait On Occurrence (WoO) works: Each WoO functions "remembers" what occurence it last waited on and what time it continued (because the occurence fired or because of a timout). When a VI is loaded (!) each WoO is initialized with a non existing occurrence. When a WoO is called and "ignore previous" is FALSE there are four potential cases:

  • The occurrence has *never* been set -> in this case WoO waits
  • The occurrence has been set since this WoO last executed -> WoO does not wait
  • The occurrence has last been set before this WoO last executed and last time this WoO was called it waited on the *same* occurrence -> WoO will wait
  • The occurrence has last been set before this WoO last executed but last time this WoO was called it waited on a *different* occurrence -> WoO will *not* wait!!!!

The first three cases are pretty clear, the last one may seem a bit strange. It will only arise if you have a WoO inside a loop (or inside a *re-entrant* VI in this loop) and it waits on *different* occurrences (out of an array, for example) or if it is inside a *non re-entrant* VI and the VI is called with different occurrences. These cases do generally not happen. The reason the WoO behaves this way is due to its implementation. Each occurrence "knows" the last time it has been fired, and each WoO remembers the occurrence it was last called with and what time it fired (or timed out). When WoO is called and "ignore previous" is FALSE, it will look at its input; if the input is the same as last time, it will look at the time of the last firing and wait depending on whether the time was later than last execution; if the input is *not* the same as last time, it will simply look at the time and wait depending on whether it has *ever* been fired.

A possible (implementation) problem: It appears that occurances are "remembering" that they have been set during previous invocations of the program. One would think that generating an occurance should create a clean "non-set" occurance. This problem can be illustrated in a program that has three parallel loops with an abortable wait in each using occurances. If the program is stopped with the stop button things are fine. But if one waits until one of the random stop conditions triggers the end of the loops (generated from the other loops), the next time the program is run, the loops will execute only once and not loop at all. (The random terminate condition in the actual program is an error occuring in some piece of equipment.) Either this is a bug, or we are completely wrong on the use of occurances. Our example here is using the occurances in the "do not clear previous" mode. We would think that we will not remember ccurances from previous runs of the program since a new clear occurance should be created with the generate occurance icon. In this instance we can not use the clear previous occurances mode since we need a single occurance to stop multiple parallel loops.

The reason for the problem: The first time the occurrence is set because of an error, the loops terminate, but the "stop button" loop is still running. When you click on the stop button, the occurrence gets triggered again (unneccessarily) and the program stops. The next time the VI runs, the WoO will not wait because of this extra trigger; and since you'll trigger again in the "stop button" loop, the VI won't work until it's reloaded from disk.

A solution to the problem: Due to this behavior of occurrences, it is clear that one cannot use the "timed out" flag to determine when the occurrence fired. You will have to maintain some global information about your state, let's say in a global boolean called "FINISHED". At the beginning of the program you would initialize it to false. If you have an error, set FINISHED to true, and then trigger the occurrence. After the WoO see if FINISHED is true (make sure you don't read the global until *after* WoO finished executing), if FINISHED is false continue with the loop. In the "Stop button" loop, also set FINISHED before you trigger the occurrence.

BTW, if you don't like globals, you could use a VI with an Uninitialized shift register (LabVIEW 2 global), but the effect would be the same.

Comments: One may think that occurrences are quirky, a pain to use and that they should be avoided. One might be right! Occurrences are very low level and you often have to add functionality to them in order to use them effectively. In other words, they are not for the faint of heart. Anything implemented with occurences can be also implemented without them, but maybe not as efficently. What occurrences do is to allow you to program synchronization in a way that does not use polling, and is thus "cheaper" in processor time.

Note: National Instruments encuorages the use of Notifier operations functions in place of the occurences for most operations.