Jump to content

Non-zero default values for reference types


Recommended Posts

I debugged multiple hours today a bug in my code where for some reason I did not receive user events to my event structure. The bug happened in a class whose private data looks the following.

post-4014-0-24356000-1296690948_thumb.pn

In the code, I set the input event registration refnum within a method VI of the class. In the particular class the same VI could be called multiple times, so I only registered the events if they had not been registered earlier. I wrote a test case and the test case looked like it was deadlocking. I was looking the bug everywhere but not the class private data. Finally I find the bug by placing a probe on the wire originating from a constant of that class.

post-4014-0-23397700-1296690953_thumb.pn

The bug in my code was that the class private data had an event registration refnum, which had a default value that was non-zero. However, there is no way to see this bug by just looking the class private data. There is also no way of fixing the bug, other than creating a completely new event registration refnum constant of the same type and copying it to the class private data to replace the original one.

The bug originates from the fact that in LabVIEW you cannot drop event registration refnums fron control palette but you have to instead create one on the block diagram and then copy the event registration refnum from your diagra (front panel) to your class private data. Again there is no way of noticing if the event registration refnum has a non-zero value. So I must have ran my code once, then copied an indicator to my class private data, carrying the non-zero default value with it not seeing it as the look-and-feel of the event registration refnum is exactly the same if it is null or not.

In my code, LabVIEW did not return me an error when I reregistered a user event with an old invalid event registration refnum. They got properly registered, at least it appeared so. In some cases the code even worked properly, apparently LabVIEW created a event registration refnum to the memory address specified by the constant that was connected to the input of register for events. It was only in case of multiple instances trying to use the same event registration refnum constant when I ran into troubles, I was unable to use the constant registration refnum with multiple event structures (surprise). I am confused of the fact that I did not get an error from event registration refnum, I think that might be a bug in LabVIEW. I am quite as surprised that LabVIEW did not crash when I used invalid reference for creating an event registration. When I was debuggin the code, not-a-refnum node returned false indicating that the constant refnum was a valid refnum, which I think is another LabVIEW bug.

My question for you guys is, what would be the best way to improve the user experience to avoid this problem for occurring. One idea I have is to have refnum types to have a right click option to set them to null reference. That still would not fix the issue but would help to clear references. Another idea would be to visually change the appearance of the reference controls, indicators and constants that do have a non-zero value, so that the programmer would have a way of knowing that the reference type is non-zero. This visual difference should be rather visible, it is very seldom the case that you actually want to use a constant value for a reference type. Usually you just want your reference values to be zero.

Link to comment

My question for you guys is, what would be the best way to improve the user experience to avoid this problem for occurring.

I can't think of a single instance where I'd want a refnum constant to not have a default value other than a null refnum. There isn't really such thing as 'refnum persistence' that I know of so why should the data type even allow storing a value other than null for its default value?

Link to comment

I made a test project that demonstrates the issue I was having. Just open the Main VI, place a probe where suggested and run it. Note that all three created event registration refnums are all the same.

Event Registration Test.zip

You know, I never thought about this, but what if we just made it so that the default value for refnums was *always* Not A Refnum? I don't think there's ever a valid reason for having a non-zero default value in either the default value or in a diagram constant.

[EDIT] Ah... just saw Omar's post. :-)

Actually LabVIEW seems to have a concept of refnum persistence and it would be actually cool to have it. Think the following for example. Drop a DVR constant somewhere. Set it to have a non-default value. Then distribute the DVR across your app by wiring from the constant (or control). Now you would not need to initialize the DVR, but instead it would automagically have a default instance created upon the time the constant is read. This would be especially cool with LabVIEW class private data, that would have DVR's as private members. Now you could drop a class constant on block diagram and you would not need to call constructor to initialize the DVRs, but instead they would be automatically initialized when the class is loaded to memory. Practically you would have a singleton, unless you set your class constants to have non-default values across your app. Or maybe there could be an option to avoid the singleton issue.

EDIT: Can someone (from NI) explain to me what exactly is going on in my example code. It still doesn't make complete sense to me.

1. Why do I get a different default value for the reference from each constant each time I run the code.

2. Why do the register for events nodes not create a new event registration refnum but instead always return the same?

Link to comment

You know, I never thought about this, but what if we just made it so that the default value for refnums was *always* Not A Refnum? I don't think there's ever a valid reason for having a non-zero default value in either the default value or in a diagram constant.

We actually did discuss this in the past -

Link to comment

Yeah, and what we discussed there is apparently wrong to some degree. :-)

Can anyone describe what is happening in the example code I posted. It is a non-documented feature of LabVIEW. I would say it is a bug if the constant refnum was not different at every single run. But it is different, so LabVIEW allocates a new refnum at every single run. So it must be a designed feature, or accidentially implemented feature of refnum constants that get allocated when the class gets loaded to memory or when the refnum is dereferenced the first time.

Link to comment

Can anyone describe what is happening in the example code I posted. It is a non-documented feature of LabVIEW. I would say it is a bug if the constant refnum was not different at every single run. But it is different, so LabVIEW allocates a new refnum at every single run. So it must be a designed feature, or accidentially implemented feature of refnum constants that get allocated when the class gets loaded to memory or when the refnum is dereferenced the first time.

It's neither a bug or an undocumented feature. It's the control doing exactly what all controls do. If I have a numeric control and I say, "Make current value default", and the current value is 3, then thereafter the default is 3. With a reference number, the same thing happens. If the current value is "0x33445522", and you say "Make Current Value Default", then the default value thereafter is "0x334445522". The control does *not* allocate a reference ever. It returns that (probably invalid) refnum. I (and others) thought that a control/constant of these types were always default of Not A Refnum, which would be undocumented special behavior. But, no, it's just the same behavior as all other controls.

What you have to remember is that the controls store a reference number, not the thing that is referenced.

Link to comment

It's neither a bug or an undocumented feature. It's the control doing exactly what all controls do. If I have a numeric control and I say, "Make current value default", and the current value is 3, then thereafter the default is 3. With a reference number, the same thing happens. If the current value is "0x33445522", and you say "Make Current Value Default", then the default value thereafter is "0x334445522". The control does *not* allocate a reference ever. It returns that (probably invalid) refnum. I (and others) thought that a control/constant of these types were always default of Not A Refnum, which would be undocumented special behavior. But, no, it's just the same behavior as all other controls.

What you have to remember is that the controls store a reference number, not the thing that is referenced.

The behavior you described is not the behavior I am seeing. Follow the exact steps with the example project I posted.

1. Open Main VI

2. Put a probe to the object array wire just before the loop (probe 4)

3. Put a probe to the object array wire just after the loop (probe 5)

4. Run VI

5. Check the Event Registration Refnum values of each array element in probe 4 and probe 5

6. Run VI again

7. Check the Event Registration Refnum values of each array element in probe 4 and probe 5

The results of my test run are (only event registration refnum is displayed):

Probe 4 after step 5: [2922381312, 2922381312, 2922381312] (note all elements are same)

Probe 5 after step 5: [2923429888, 2923429888, 2923429888] (note all elements are same but different than those of probe 4)

Probe 4 after step 7: [2923429888, 2923429888, 2923429888] (note all elements are same as probe 5 values after step 5)

Probe 5 after step 7: [2924478464, 2924478464, 2924478464] (note all elements are same but different than those of probe 4 after step 7)

As probe 4 is returning values of constant we can deduce that the values of the constants are not constant as the values of probe 4 vary from run to run and are the same as those of probe 5 of the previous run. This is definitely undocumented. Second thing to notice is that when wired to the register for events node, the event registration refnum gets a new value but the new value is the same for every execution of the node within the same run of the app. I would expect either error to be returned because the input refnum is invalid or alternatively a new different refnum to be created each time. That is I would expect the values of Probe 5 array to be all different, or alternatively errors should be returned and the values should be those as the ones of Probe 4.

post-4014-0-61324700-1297067480_thumb.pn

post-4014-0-93803800-1297067583_thumb.pn

Link to comment

I see what you're seeing as well. You can also see this if you probe inside the VI (you might have to make it non-reentrant) - the second and third iteration will get the new refnum value BEFORE the reg node, even though the numeric value was different before going into the loop.

I think this has to do specifically with the event reg refnum, which behave somewhat differently from other refnums in general, but it should be noted that this happens ONLY in a class. If the class is converted to a cluster, this doesn't happen.

  • Like 1
Link to comment

the second and third iteration will get the new refnum value BEFORE the reg node, even though the numeric value was different before going into the loop.

For some reason the event reg refnum control is being treated as a global. That's why the constant doesn't appear to be constant. I ran this block diagram with Retain Wire Values turned on. After execution finished, I removed and reprobed one of the class constant wires. The refnum value changed to the value output by the Reg Events prim. This is definitely a case of a downstream operation changing an upstream value.

(FWIW I had all sorts of problems a while back when I was exploring using user events as an integral part of application architectures. Eventually I gave up and switched to using queues almost exclusively.)

post-7603-0-97410100-1297096508_thumb.pn

  • Like 1
Link to comment

Here's another quick test I did illustrating the global-ish nature of Event Registration Refnums:

post-7603-0-75797000-1297098604_thumb.pn

Refnum 3 and Refnum 4 match the value of Refnum 2, even though there's no reason -- according to ordinary data flow rules -- for that to occur. Now that I think about it I understand why this is happening. My guess is that it's an unintended consequence of making it easier to use dynamic event registration.

When we have an event structure with a dynamic events terminal, we don't have to propogate the event reg refnum wire through all the event cases. We only need to wire it up when dynamically registering/unregistering an event. For that to work the event reg refnum wire *has* to violate normal data flow rules. In essence, the refnum has to behave like a global, at least on any single block diagram.

I vaguely recall someone mentioning that event reg refnums are intended to be used only on block diagrams that service the event. That's part of the reason I gave up on them in my frameworks.

  • Like 1
Link to comment

I vaguely recall someone mentioning that event reg refnums are intended to be used only on block diagrams that service the event. That's part of the reason I gave up on them in my frameworks.

You must be kidding?

For that to work the event reg refnum wire *has* to violate normal data flow rules. In essence, the refnum has to behave like a global, at least on any single block diagram.

From theoretical point this is not true, it would be very simple to implement the event structure in dataflow safe way. Each time the event structure is executed, the event registration would simply flow from left to right in dataflow manner. That is each case that do not have event registration refnum wire connected within the case, would simply be identical to a event case where the internal output terminal on the left would be directly connected with a wire to internal input terminal on the right. That is pure dataflow.

Link to comment

You must be kidding?

Originally, I had a line in my previous reply about always keeping the event structure near the registration node, a line which I later removed. The reason for this is that each reg node should go to a single event structure (this is documented, albeit not very well). Since the only way of guaranteeing this is to have the reg node near the structure, that's what I do.

That said, this doesn't explain why what's happening here is happening. I'm fairly sure that if Daklu replaces the class in his last example with a cluster, he will see different behavior.

Link to comment

Originally, I had a line in my previous reply about always keeping the event structure near the registration node, a line which I later removed. The reason for this is that each reg node should go to a single event structure (this is documented, albeit not very well). Since the only way of guaranteeing this is to have the reg node near the structure, that's what I do.

Event registration is like a mailbox. User event is like a mailing address. When even is registered, the mailing address gets connected to a mailbox, that would by default be just a garbage bin. I want to register my events, that is create the mailbox, before I execute my event handler VI. This way the mailbox exists before anybody tries to send letters to the mailing address. If you register the events in the same VI where the event structure is, you need to use secondary synchronization mechanisms to guarantee that the mailbox has been created before anybody sends messages to it. I think this is unnecessary boilerplate code.

Link to comment

You must be kidding?

Nope, but let me emphasize vaguely. I could be imagining it.

From theoretical point this is not true, it would be very simple to implement the event structure in dataflow safe way.

Yes, but not without changing the current behavior and potentially breaking lots of customer applications. Currently, as near as I can tell, there's not much reason to wire to the dynamic events terminal on the right side of the structure for normal use cases. You don't need to wire it up after dynamically registering or unregistering an event. This code works.

post-7603-0-86714900-1297104220_thumb.pn

Should it work? Probably not, according to data flow rules. The event reg refnum value just inside the while loop is zero when the vi is started. After the event is registered it is magically given a valid refnum value in clear violation of data flow. How does it work? I'm not sure. Maybe there is some invisible wiring going on behind the scenes. There's definitely weird things happening. For example adding this event handler doesn't affect the behavior.

post-7603-0-75786400-1297105852_thumb.pn

But changing it to this prevents the refnum at the left side event terminal input from changing to a valid refnum, even though this event isn't invoked...

post-7603-0-66407100-1297105854_thumb.pn

...unless I add shift registers, which fixes the behavior again.

post-7603-0-49308000-1297106248_thumb.pn

And through all of these changes the event reg refnum here was never wired through.

post-7603-0-86714900-1297104220_thumb.pn

---------------------

To add a little more to this puzzle, I created a typedef for the event reg refnum and this code follows data flow rules. Refnums 1, 3, and 4 match and remain constant. Refnum 2 changes every time the vi is run.

post-7603-0-28936500-1297106818_thumb.pn

Unfortunately, making the same change in the Event accessor didn't change the behavior you're seeing in Main VI.

post-7603-0-77945200-1297106819_thumb.pn

Event Registration Test.zip

Link to comment

I'm fairly sure that if Daklu replaces the class in his last example with a cluster, he will see different behavior.

Nope. Same problem with a cluster.

post-7603-0-24764500-1297108523_thumb.pn

The refnum value appears to be intrinsically linked to a specific block diagram constant. Change the value downstream and the constant value upstream changes. Interestingly, if I change the cluster constant to a cluster control, Refnum 1 always returns 0 while the rest continue to match and change with each execution.

Link to comment

Yes, but not without changing the current behavior and potentially breaking lots of customer applications. Currently, as near as I can tell, there's not much reason to wire to the dynamic events terminal on the right side of the structure for normal use cases. You don't need to wire it up after dynamically registering or unregistering an event. This code works.

post-11742-0-74724400-1297111656_thumb.p

Should it work? Probably not, according to data flow rules. The event reg refnum value just inside the while loop is zero when the vi is started. After the event is registered it is magically given a valid refnum value in clear violation of data flow. How does it work? I'm not sure. Maybe there is some invisible wiring going on behind the scenes. There's definitely weird things happening. For example adding this event handler doesn't affect the behavior.

The only think I can think is the dynamic event registration terminal forces a valid refnum when it operates (despite the fact that you've supplied a constant which should not be valid). Hence when your event frame runs, you have a valid refnum, and once the register for events primitive operates, everything works as expected. In fact I believe this might be the case with most/all of the dynamic event primitives, though obviously I'm just guessing.

As for the rest, weird stuff. I don't have time to download and play, but:

And through all of these changes the event reg refnum here was never wired through.

I never expect to have to wire through the event reg refnum explicitly to the right side because it is a refnum. Once operated on all your left with is the numeric reference which should not change and doesn't need to be written back (except the aforementioned bit of voodoo).

Link to comment

I never expect to have to wire through the event reg refnum explicitly to the right side because it is a refnum.

I agree. As long as I'm operating on the thing referred to and not the refnum itself, I shouldn't have to wire it through. But when changes to the refnum itself are propogated upstream? I attribute it to magic. Not sure yet whether it's black magic or white magic...

I don't have time to download and play...

Me either, I gotta get back to work. But first....

Here's some more evidence suggesting the value on the Event Registration Refnum wire depends on which refnum constant/control instance in memory it ultimately maps back to.

In case 2 I changed the accessor to a preallocated clone and used a Refnum constant as the input to the Reg Events prim to guarantee different instances of the constant. The refnums returned were all different. Case 4 (not shown) is the same thing, except I unbundle the refnum constant from the class instead of using a constant on the bd. It returned identical refnums since all three object instance are default and all default objects point to the same instance in memory. Cases 1 and 3 were the same test using shared clones instead of preallocated clones. They returned identical refnums.

post-7603-0-03015700-1297113813_thumb.pn

In case 5 I forced LV to allocate independent memory space for each object by changing one of the values. It returned different refnums.

post-7603-0-52903400-1297113817_thumb.pn

In case 6 I "set" a class value to the default value, then branched the wire. In principle this is still a default object, but (if my theory is correct) LV doesn't recognize it as a default object and allocates space for it. Once the object is no longer default the accessor method seems to operate as expected. This returned different refnums.

post-7603-0-89467800-1297113818_thumb.pn

And finally, wrapping around again to Tomi's original example, making this change causes the refnums to be different.

post-7603-0-72603600-1297114857_thumb.pn

At this point I'll go out on a limb and say even if my memory is incorrect in this particular case, it's probably not a good idea to pass Event Registration Refnums as parameters.

Event Registration Test.zip

Link to comment

You must be kidding?

No, he's not. It's a design limitation that I've wished were fixed on multiple occasions.

The problem is the one-to-one relationship of the registration refnum to the event structure. You should never share registration refnums with two different event structures -- that way leads to DOOM. As such, it makes little sense for the event registration refnum to be stored anywhere or passed around very much.

The one-to-one relationship of that refnum to a given event structure is an invariant that can't be solved well... I barely understand the rationale, so I'm not going to try to explain it here. I accept it as proven by those with more comprehension of parallelism than I. So what is likely needed is an intermediary "event cluster" type, that packages all the event information together but without actually creating an event registration refnum, and could be passed to a Register node directly. Most approaches seem like a pretty hefty complexity jump for what is already a complex use case.

PS: You should also not change which dynamic refnum a given event structure is using during the run of the VI. You can change which events are registered, but you should use the same registration refnum.

Link to comment

No, he's not. It's a design limitation that I've wished were fixed on multiple occasions.

The problem is the one-to-one relationship of the registration refnum to the event structure. You should never share registration refnums with two different event structures -- that way leads to DOOM. As such, it makes little sense for the event registration refnum to be stored anywhere or passed around very much.

I have no problem with the one-to-one relationship, after all event registration acts like a mailbox and people don't share mailboxes with their neighbors either. However, what I was confused of is if the event registration should actually happen in the same VI as the event structure.

My typical program works as follows:

  1. Create user event refnum
  2. For each process receiving the user event create event registration refnum for that particular user event
  3. Launch all parallel processes that generate user events and receive them

Executing step 2 before step 3 guarantees that the event registration exists before anybody generates events of that type. It would be very bad programming not to somehow guarantee that the event is registered before generating the events. The easiest way to do it is simply do the registration outside the process VI that handles the events. Of course the 1-to-1 relationship still holds, a single event registration is created for a single process VI with a event structure.

Second model I have been thinking of and playing around but not used yet in real life is using multiple event structures with one event registration refnum, but in such a way that only one event registration refnum is executing at any single time. This way you can switch the way how a certain event is handled by a certain event listener on the fly.

Should these two use cases work as I expect them to?

PS: You should also not change which dynamic refnum a given event structure is using during the run of the VI. You can change which events are registered, but you should use the same registration refnum.

If I have a subVI that has an event structure and a event registration refnum input terminal that is connected to the event structure register for events terminal, are you saying that this will fail if the same instance (clone) of the subVI is executed in different places of the application. Say it was a re-entrant VI but with share clonses option selected. Then one of the event handler VIs has done its job and exits gracefully. Now LabVIEW reused the VI instance for another event handler VI instance and another event registration refnum is connected to the event registration input terminal of the VI.

Maybe I understand everything better when someone from NI is ready to explain all the weirdness that has been discussed elsewhere in this thread.

Link to comment

My typical program works as follows:

  1. Create user event refnum
  2. For each process receiving the user event create event registration refnum for that particular user event
  3. Launch all parallel processes that generate user events and receive them

I used to do that a lot as well, but the strictness of the event registration refnums led me to abandon that route since any change to what was being observed resulted in a hierarchy of VIs needing to be updated. What I usually do now is.

  1. Create user event refnum.
  2. Create some kind of callback object, an occurrence will do, but I usually use notifiers to return some sort of signal.
  3. Launching process will start the asynchronous VI then block on the callback.
  4. Asynchronous VI registers for the user event, then signals the callback.
  5. Launching process is signaled and continues on its way.

More steps, but I swore a long time ago to never use an event registration refnum in a connector pane ever again. The code that depends on it is just too fragile.

-m

Link to comment

I used to do that a lot as well, but the strictness of the event registration refnums led me to abandon that route since any change to what was being observed resulted in a hierarchy of VIs needing to be updated.

You know you can cluster event registration refnums and wire the cluster to the input of event structure.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

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