Jump to content
News about the LabVIEW Wiki! Read more... ×
ShaunR

LabVIEW 2017 Dynamic Event Registration Behaviour

Recommended Posts

Maybe others have seen this but I only became aware of it recently so I apologise if there is already a CAR for it.

The following demonstrates a difference in behaviour between LabVIEW 2017 and previous versions (back as far as 2009, maybe further).

The VI registers an event when it starts (in the timeout case) and generates a user event when the Increment button is pressed.

The expected behaviour is that the counter will increment by one every time the button is pressed. This is the case for LabVIEW versions prior to 2017. In 2017, the user event is never fired, nor is there an error emitted by the generate user event.

To get the VI to operate as expected in 2017; change the event refnum tunnel to a shift register. This seems to indicate that the refnum prototype is stomping on the dynamically allocated reference whereas in previous LabVIEW versions it would not. Note also, that when using the shift register, the cases do not need to be "wired through" as would be expected with similar functionality in a normal case statement.

evnt.vi

Share this post


Link to post
Share on other sites

The current behavior looks like the incoming reference on the left terminal overrides the previously "wired from the inside" right terminal. If that's the intended behavior, I feel like the the "right" terminal is now obsolete because of linked tunnels.

Edited by infinitenothing

Share this post


Link to post
Share on other sites
44 minutes ago, infinitenothing said:

The current behavior looks like the incoming reference on the left terminal overrides the previously "wired from the inside" right terminal. If that's the intended behavior, I feel like the the "right" terminal is now obsolete because of linked tunnels.

So. You can confirm the behaviour has changed between versions?

Edited by ShaunR

Share this post


Link to post
Share on other sites

I confirm the behavior in 2017 which is the only version I have at hand now, but am not sure about your intent. Certainly you're more sophisticate than me in programming and your snippet is a minimal cutdown of something really meaningful. I only observe: if I register once the event before the while loop, which is what I'd normally do, the increment is fired.

e.png

maybe the new behavior is not about registration, but about the generic event refnum constant?

Share this post


Link to post
Share on other sites
58 minutes ago, ensegre said:

I confirm the behavior in 2017 which is the only version I have at hand now, but am not sure about your intent. Certainly you're more sophisticate than me in programming and your snippet is a minimal cutdown of something really meaningful. I only observe: if I register once the event before the while loop, which is what I'd normally do, the increment is fired.

e.png

maybe the new behavior is not about registration, but about the generic event refnum constant?

That is static registration.

Yes it is about the constant refnum which is required for dynamic registration.

A real world example of dynamic registration might be registering for the mouse_move event only after the left mouse button has been clicked (register mouse_move during mouse_down) and then de-registering on the mouse up (e.g. moving a window when the left mouse button is held down). To achieve this one has to supply a prototype of the event (the refnum constant) to the left hand terminal as I have shown, otherwise the VI will be broken.

Share this post


Link to post
Share on other sites

I see. For your specific real world example though, couldn't this be a workaround? E.g. registering statically for a mouse_move event of a generic (blank) reference, turning it to the relevant reference on mouse_down, and reverting to blank on mouse_up?

evnt3.vi

Edited by ensegre

Share this post


Link to post
Share on other sites
14 minutes ago, ensegre said:

I see. For the your specific real world example though, couldn't this be a workaround? E.g. registering statically for a mouse_move event of a generic (blank) reference, turning it to the relevant reference on mouse_down, and reverting to blank on mouse_up?

 

evnt3.vi

There are several workarounds, one of which I highlighted in the example. The issue is that it is a change in behaviour from previous LabVIEW versions which breaks compatibility

Share this post


Link to post
Share on other sites

Definitely odd, revealing yet another bizarre issue in the land of user events.

I will say that technically you are doing it wrong according to the help (which did not change between 2013 and 2017):

https://zone.ni.com/reference/en-XX/help/371361P-01/lvhowto/design_case_for_registration/

To "modify" the registration you have to read the wire in from the left hand side inside the case where you modify the registration. Doing that alone with the null ref you had originally did nothing, but adding a registration for a null event did work.

My guess, based on this, is that something changed such that the null refnum 'doesn't count' as a proper registration, and so the event structure never registers events until you create one, store it in the shift register, and pass it in on the left. But obviously just a guess.

I modified your code according to the help documentation (attached) and it worked correctly on my machine:

evnt2.vi

Edited by smithd
  • Like 1

Share this post


Link to post
Share on other sites
7 hours ago, smithd said:

Definitely odd, revealing yet another bizarre issue in the land of user events.

I will say that technically you are doing it wrong according to the help (which did not change between 2013 and 2017):

https://zone.ni.com/reference/en-XX/help/371361P-01/lvhowto/design_case_for_registration/

To "modify" the registration you have to read the wire in from the left hand side inside the case where you modify the registration. Doing that alone with the null ref you had originally did nothing, but adding a registration for a null event did work.

My guess, based on this, is that something changed such that the null refnum 'doesn't count' as a proper registration, and so the event structure never registers events until you create one, store it in the shift register, and pass it in on the left. But obviously just a guess.

I modified your code according to the help documentation (attached) and it worked correctly on my machine:

evnt2.vi

So you can confirm the change in behaviour between all previous versions and 2017?

Share this post


Link to post
Share on other sites
8 hours ago, smithd said:

I will say that technically you are doing it wrong according to the help (which did not change between 2013 and 2017):

https://zone.ni.com/reference/en-XX/help/371361P-01/lvhowto/design_case_for_registration/

To "modify" the registration you have to read the wire in from the left hand side inside the case where you modify the registration. Doing that alone with the null ref you had originally did nothing, but adding a registration for a null event did work.

Lets assume I have cheated to save space and complexity by using an event refnum and have inadvertently exploited a bug - which has been "fixed".

Using your "technically correct method" should I not be able to register and unregister events at will?

evnt4.vi

Edited by ShaunR

Share this post


Link to post
Share on other sites

I think I once discovered this strange behavior (which I guess someone decided to "fix").  The code Shaun posts works even though it intuitively shouldn't, since the constant he feeds in is null (refnum=zero), and not an actual active Event Registration.  Yet it does work.   In fact, if I remember right, one could "retain all wire values" and then probe the null Event Reg outside the loop, and it would actually show the refnum of the EventReg created inside the loop!  

BTW, I do such delayed-registration this way, which does work still:

Screen Shot 2018-03-02 at 10.37.10 AM.png

This creates the Event Reg outside the loop, but with a null User Event constant, then substitutes the actual User Event later.

Share this post


Link to post
Share on other sites
8 hours ago, smithd said:

but adding a registration for a null event did work.

Sorry, I missed that you had already mentioned this method.

Share this post


Link to post
Share on other sites
11 minutes ago, ShaunR said:

Using your "technically correct method" should I not be able to register and unregister events at will?

Deregistering an event is like this:

Screen Shot 2018-03-02 at 10.52.47 AM.png

Note that this leave existing events in the event queue (one can Flush if one wants).

Share this post


Link to post
Share on other sites
23 minutes ago, drjdpowell said:

The code Shaun posts works even though it intuitively shouldn't, since the constant he feeds in is null (refnum=zero)

It intuitively should. It is not a null event reference. It is a constant event reference with a null user event refnum (but could be a panel, control or any other) which is obtained by right clicking on the event registration wire and "create constant". It is syntactically identical to the refnum created by the register function. You may argue that it is a "constant" and therefore immutable but that is a different argument altogether - especially when it has behaved that way for a decade.

Share this post


Link to post
Share on other sites

It is a constant event reference with a null user event refnum

It's a constant Event Registration Refnum (refnum=0 aka null).  Calling the Register function creates an event queue (refnum<>0); just using a constant does not.

Share this post


Link to post
Share on other sites
9 minutes ago, drjdpowell said:

It's a constant Event Registration Refnum (refnum=0 aka null).  Calling the Register function creates an event queue (refnum<>0); just using a constant does not.

The register is called later (and should create the queue later if that is internal behaviour). Your synopsis doesn't explain why a shift register works, either since that should also fail. It also doesn't explain why the unregister all events is unrecoverable. It also isn't documented in the 2017 bug fixes, if it is a "bug fix".

You have just found a work-around to yield to the new behaviour. I argue that they have broken compatibility, introduced complexity and anomalous behaviour-they broke user space which is rare and unusual for NI.

Share this post


Link to post
Share on other sites
2 minutes ago, ShaunR said:

The register is called later (and should create the queue later if that is internal behaviour). Your synopsis doesn't explain why a shift register works, either since that should also fail. It also doesn't explain why the unregister all events is unrecoverable.

Try imagining the equivalent Queue operations.   I found Dynamic Events confusing until I realized that the Event Registration Refnum is like a Queue, and the User Events are like a reference to an array of registered Queues.

If I drop a Queue constant before a loop, then have "Obtain Queue" inside the loop, then if I want to enqueue or dequeue in another case of the loop I must feedback the obtained Queue refnum.  I can't do anything with the original Queue constant (refnum=0).  Similarly, if I feed in an active Queue and then destroy it with "Release Queue", I can never use that Queue refnum again but must instead feedback a newly created Queue's new refnum. 

This is standard dataflow-with-references behavior that Event Registration Refnums used to allow the breaking of.  This was a mistake on their part, but "fixing" it is also a mistake as some prior code will be broken (worse, broken without an error message anywhere).

Share this post


Link to post
Share on other sites
16 minutes ago, ShaunR said:

You have just found a work-around to yield to the new behaviour.

No, that's the way I've done it for several years.  That's the dataflow-consistent way to do it.  Your way of acting on a zero-refnum tunneled in that "magically" becomes an active non-zero refnum breaks the dataflow paradigm. 

Share this post


Link to post
Share on other sites
On 02/03/2018 at 0:06 PM, drjdpowell said:

This is standard dataflow-with-references behavior that Event Registration Refnums used to allow the breaking of.

If that were true, then in your example you would also need to pass back the refnum to the right hand event terminal for it to magically appear on the left hand terminal (as Smithd showed) or use a shift register as I did. You also don't need to "pass-through" from the left terminal to the right like you would if it where a queue in a case statement.....and lets not forget "Named Queues". I appreciate your argument but events are a category on their own and your solution still breaks dataflow, by your definition,

The fact that they may use queues internally is a hidden implementation detail and kind of irrelevant. It may explain why a decision was made to change the behaviour but doesn't mean it was the right decision.

To quote Linus Torvalds

Quote

They say "it was a bug. Look, here was the standard. It says you can't rely on that"......nobody cares! If it's a bug that people rely on; it's not a bug. It's a feature.

I know for certain that I'm not the only one that relie[d/s] on this behaviour. I think I learnt it from Jack Dunnaway in his events presentation.

Rule #1. You don't break user space.

Edited by ShaunR
changed "everyone relies on" to "people rely on" as the former was a misquote.

Share this post


Link to post
Share on other sites

I agree with the others.  This should never have worked, and was clearly a bug that got fixed.  In every iteration of the while loop you are passing in the reference to a refnum equal to 0.  All design patterns I've seen from others have this as a shift register, usually uninitialized, which gets it's value set on the first chance it can.  I tested this in 2015 SP1, and 2016 and both still increment.  I only have 2017 SP1, so am not sure if this was a SP1 fix, or a 2017 fix.  Still you suggesting that everyone relies on this (based on your quote of Linus) might be a bit exaggerated.

Share this post


Link to post
Share on other sites
1 hour ago, ShaunR said:

If that were true, then in your example you would also need to pass back the refnum to the right hand event terminal for it to magically appear on the left hand terminal (as Smithd showed) or use a shift register as I did. You also don't need to "pass-through" from the left terminal to the right like you would if it where a queue in a case statement

No, one can build that exact structure with Queues.  The Queue is created outside the loop and never changes, so it can be passed in via tunnel and doesn't need to be "passed-through".   The confusing issue is that the "Register for Events" node acts like "Obtain Queue" when called with no active Event Reg Refnum as input (as outside the loop in the example), but doesn't if an active refnum is supplied (as inside the loop).  Unfortunately, the Events API is quite confusing.

Share this post


Link to post
Share on other sites
1 hour ago, hooovahh said:

I agree with the others.  This should never have worked, and was clearly a bug that got fixed.  In every iteration of the while loop you are passing in the reference to a refnum equal to 0.  All design patterns I've seen from others have this as a shift register, usually uninitialized, which gets it's value set on the first chance it can.  I tested this in 2015 SP1, and 2016 and both still increment.  I only have 2017 SP1, so am not sure if this was a SP1 fix, or a 2017 fix.  Still you suggesting that everyone relies on this (based on your quote of Linus) might be a bit exaggerated.

I didn't say everyone. I said I was not the only one. You may be OK with breaking interfaces because of anal specmanship, but I am not. Behavioural changes have an enormous cost downstream.

53 minutes ago, drjdpowell said:

No, one can build that exact structure with Queues.  The Queue is created outside the loop and never changes, so it can be passed in via tunnel and doesn't need to be "passed-through".   The confusing issue is that the "Register for Events" node acts like "Obtain Queue" when called with no active Event Reg Refnum as input (as outside the loop in the example), but doesn't if an active refnum is supplied (as inside the loop).  Unfortunately, the Events API is quite confusing.

It wasn't confusing at all and, to be honest, elegant and compact. Not so much now. You chose a bad analogy with queues. Just because there happens to be one, doesn't necessarily mean events should behave like one.

A better analogy from my perspective is DVRs. Would you consider this a "bug"?

dvr.png

dvr.vi

For the purpose of the analogy, the feedback node is hidden.

Edited by ShaunR

Share this post


Link to post
Share on other sites

So you weren't implying this is a bug that everyone relies on, by quoting someone saying:

Quote

If it's a bug that everyone relies on; it's not a bug.

It appeared to break the rules of data flow.  I'd consider that a bug worth fixing, and a feature that shouldn't be relied on.

Share this post


Link to post
Share on other sites
8 minutes ago, ShaunR said:

A better analogy from my perspective is DVRs. Would you consider this a "bug"?

dvr.vi

This isn't the same issue as your event example above.  In the DVR case you actually made a method for the next iteration to use the newly created reference.  In the event case there is no shift register or feedback node.  You register for something, but then the next iteration of the loop has the event structure getting that null refnum again.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×

Important Information

By using this site, you agree to our Terms of Use.