From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

[BUG] Certain code paths make LabVIEW deadlock (freeze) when returning from Run mode to Edit mode

Solved!
Go to solution

Primary bug

I've attached a small project that triggers this freeze.

 

If an event callback is activated, and the callback VI in turn calls the VI which originally registered the callback, something weird happens. LabVIEW will continue running normally and respond to all kinds of user input. However, when the time comes to transition from Run mode to Edit mode, LabVIEW will freeze. Changing reentrancy did not help. Switching between direct subVI calls and VI Server calls (call by reference) did not help.

 

 

 

Secondary bug

If the call-by-reference code (see below) is replaced with a direct subVI call...

 

  • If the subVI is Shared Clone reentrant, LabVIEW complains, "Attempted recursive call"
  • If the subVI is non-reentrant, LabVIEW doesn't complain.

This seems inconsistent.

 

 

Code preview

The full project is attached, but here's a preview:

 

Main.vi

LV_Freeze_Bug - Main.png

 

Amplify Event.vi

LV_Freeze_Bug - Amplify Event.png

 

Callback.vi

LV_Freeze_Bug - Callback.png

 

Certified LabVIEW Developer
0 Kudos
Message 1 of 9
(7,505 Views)
In terms of your primary bug, you want to register another identical callback inside the callback handler? Why would you ever want to do that? Frankly I would be shocked if that did work. Callback VIs run in their own little world. For example, you also can't debug them. This is why you put as little code as possible in them. Typically I just pass in an event reference that I can use to fire a LabVIEW event. The LabVIEW event in turn does the heavy lifting.

In terms of your second problem, I'm not sure what is inconsistent.

Mike...

Certified Professional Instructor
Certified LabVIEW Architect
LabVIEW Champion

"... after all, He's not a tame lion..."

For help with grief and grieving.
0 Kudos
Message 2 of 9
(7,497 Views)

@mikeporter wrote:
you want to register another identical callback inside the callback handler? Why would you ever want to do that?

I'm experimenting with using callbacks as a messaging mechanism between "actors". The callbacks are registered during actor initialization. Some (not all) callback handlers can spawn new actors, which is why new callbacks can be registered within a callback handler. The handler is identical because its code is shared between the different actors.

 

For now, putting aside the question of whether or not this is a good design, I just want to report a reliably reproducible deadlock in LabVIEW.

 


@mikeporter wrote:
Frankly I would be shocked if that did work.

I guess you must be shocked then 🙂 It definitely works.

 

Try running the simple demo project (attached to my original post in a *.zip file) -- instructions are on the front panel.

 

My experiment works too, and my "actors" behave exactly as I want them to. There are no issues whatsoever during runtime; things only goes pear shaped when LabVIEW tries to transition back to Edit mode.

 


@mikeporter wrote:
Callback VIs run in their own little world. For example, you also can't debug them.

What do you mean? I can probe, activate breakpoints, use highlight execution, etc. in a callback VI, just like any other subVI (reentrant or otherwise).

 


@mikeporter wrote:
This is why you put as little code as possible in them. Typically I just pass in an event reference that I can use to fire a LabVIEW event. The LabVIEW event in turn does the heavy lifting.

 

There are certainly many paths to the same destination.

 

Fret not, I won't do this type of thing in production code (yet?). I'm simply experimenting with possibilities for now. I see potential in the convenience of doing event-driven programming without having to implement an event structure, especially in the case of dynamically-spawned actors.

 


@mikeporter wrote:
In terms of your second problem, I'm not sure what is inconsistent.

Sorry, perhaps "inconsistent" isn't the right word...

 

Anyway, there are 2 things wrong with this picture:

  • LabVIEW blocks the so-called "recursive call" when the VIs involved are reentrant, but doesn't block it if I make those VIs non-reentrant. At the very least, it should be the other way round OR it should block both, right?
  • I'm not actually making any recursive calls, yet LabVIEW says I am.

 

Certified LabVIEW Developer
0 Kudos
Message 3 of 9
(7,474 Views)

Why don't you just call the "Amplify Event" VI in the "Callback.vi" instead of dynamically calling?

0 Kudos
Message 4 of 9
(7,368 Views)

On a different level, I struggle, like others, to understand why you would register a callback within a callback..... You have no method of retaining the output wire of the callback registration in order to do clean-up afterwards.  Surely this should be part of an initialisation of whatever actor requires the communications pathway as opposed to being part of a callback itself.

 

Maybe you can outline a situation where it should really be a callback setting up another callback (which I understand will be a DIFFERENT event in your actual code).

0 Kudos
Message 5 of 9
(7,353 Views)

Having looked at the code and played with it, It would certainly seem to be buggy.

 

I even added a FIFO in the code to collect all registered callback refnums inorder to close them when stopping the code but to no avail, as soon as a single callback registers a callback, the code freezes upon finishing.

 

EDIT: And in true rubber duck fashion, I realised after posting that I would need to REVERSE the order of the callback reference array when closing and voila, it seems to work.

Message 6 of 9
(7,345 Views)

No feedback twhether my change solves the problem?

0 Kudos
Message 7 of 9
(7,284 Views)

Hi Intaris, thank you for taking the time to investigate this, and for your insight into the issue. Sorry for taking a while to reply.


@Intaris wrote:

 

 

EDIT: And in true rubber duck fashion, I realised after posting that I would need to REVERSE the order of the callback reference array when closing and voila, it seems to work.


You've hit the nail on the head!

 

I extended your code some more and found that the freeze occurs when closing the very first Event Callback Refnum. In the code below, "Closing" will never go above 1, and "Closed" will never go above 0 if there are multiple cascaded callbacks:

 

 Callback Closure Freeze.png

 

So, I'm guessing that when I didn't explicitly close the refnums, LabVIEW tries to auto-close them when I stop the VI. However, it tries to auto-close them in the order they were created, hence triggering the freeze.

 

This behaviour reminds me a bit of how a DVR becomes invalidated when the VI which created it leaves memory. Perhaps, by closing the first Event Callback refnum, LabVIEW tries to "unload" the callback VI, which in turn tries to close the next Even Callback Refnum, and so on.

 

Would you consider this a bug? (I know I'm using callbacks in an unorthodox fashion, but I don't think the whole IDE should go down like this.)

 


@Intaris wrote:

Why don't you just call the "Amplify Event" VI in the "Callback.vi" instead of dynamically calling?


I did; the end result was the same. (I actually switched from a direct subVI call to a dynamic call because I wondered if the static VI reference was causing issues.)

 


@Intaris wrote:

Maybe you can outline a situation where it should really be a callback setting up another callback (which I understand will be a DIFFERENT event in your actual code).


Sure.

 

I wanted to dynamically create nested GUI components (using a 3rd party GUI toolkit). A button click triggers a callback to create a new, user-specified component. Some of these components have the ability to generate their own nested components, which is why they have their own callbacks.

 

I've published my experiment at https://lavag.org/topic/19611-utf-8-text-svg-images-inheritable-gui-components-dynamically-composed-...

In the demo video, my use of nested/cascaded callbacks is shown from 2:05 onwards. That page also contains links to the library/example code.

 

Currently, I'm relying on LabVIEW to auto-close the refnums; I'll simply have to explicitly close them in the right order. I know it's good practice to clean up dynamic references even if there's no freezing issue, but I haven't fully polished the code in this proof-of-concept stage.

Certified LabVIEW Developer
0 Kudos
Message 8 of 9
(7,273 Views)
Solution
Accepted by JKSH

This is now being tracked under CAR 690115.

 

Issue summary: Suppose a callback VI registers another callback. LabVIEW will hang if:

  • We try to unregister the first refnum before the second refnum, OR
  • We we stop our code without explicitly unregistering anything (which causes LabVIEW to do automatic cleanup behind-the-scenes)

 

Workaround: Always ensure that our code performs the un-registration in reverse to the registration order.

 

Certified LabVIEW Developer
0 Kudos
Message 9 of 9
(6,554 Views)