Jump to content

Dynamic Dispatch: Bug or Expected Behavior


Recommended Posts

I ran into an awful bug in my software today, where my application would start then just randomly stop. No crash of LabVIEW; it just stopped running. I traced it back to the following:

 

A parent class's Dynamic Dispatch method had an instance of itself on its block diagram. The method was not set to reentrant execution, but also did not give me a broken run arrow. This caused the VI to recursively call itself until the application died. 

 

Is there a fundamental reason this is allowed (something to do with DD perhaps?), or is this an oversight in the compiler? I haven't tested with static methods, but I assume that would cause a broken run arrow as regular VIs do.

 

post-15770-0-69189100-1391044445.png

DynamicDispatch.zip

Edited by for(imstuck)
Link to comment

Funny, I had the reverse situation yesterday and I started wondering which is worse:  instead of an unbroken-broken VI I had a broken-unbroken VI.  Simple parsing VI with a set of sibling classes which parse different object types.  All of the sibling VIs are basically the same except for a different cluster in unflatten from string, in fact, they were all made by copying the first child class.  I end up with a broken VI which claims that (only) one of the sibling VIs is broken.  Clicking the broken Run Error in the VI gives the ever-helpful empty error message.  Ctrl-Run, Close-Open, Change Private Data and Apply, still broken.  Diagram Disable the entire diagram, still broken.  Save a copy of the class, add the new sibling to the parsing VI unchanged and it works....  Finally LV12 did its thing, by which I mean crash, and when I restarted the broken VI was now fine....

 

My problem is that I am stubborn, nine times out of ten problems like this are LV's fault and yet nine times out of ten I still assume it must be my fault.

 

No definite opinion on whether I prefer broken-unbroken or unbroken-broken VIs other than they both suck.

Link to comment

Darin, I have had similar issues as well (also yesterday). But, I can't be sure they weren't attributed to the "crash" itself. The problem seems to have gone away after I fixed it.  This is a program running on RT and my top level VI would be unbroken, I'd click run, it would error in deployment and a static dependency VI would now be broken. However, the top level VI still wasn't! When clicking the broken run arrow, the dialog was blank as well. Closing the project and reopening resolved the issue, as well as rebooting the RT. Again, this may have been due to initially deploying with this recursive VI and crashing, as I haven't seen the problem since. However, I have seen this sort of behavior in other scenarios which always, yes, sucks.

Edited by GregFreeman
Link to comment

The last time I had such an issue, it was related to the 'Seperate Compiled Code' option in combination with revision control: I reverted one of my components in SVN and for some reason it has not been recompiled automatically (broken-unbroken issue)... After hours of searching, an NI engineer suggested to clear the compiled object cache, which solved the problem entirely.

Not sure if that's related.

Link to comment

Oh, LVOOP and Real-Time.

 

I seem to be having problems with this also.  I get lots of warnings when exiting LV that are related to a certain class (with only static inlined VIs).  I've also been seeing completely different code running ont he RT than in the IDE.  That makes debugging kind of hard.  I've had VIs running on the RT but not marked as reserved on the host and vise versa.

 

Apparently adding the line "RTCloseVIsOnDocMod = True" to the rt.ini in the root directory of your RT system can help this problem.  It certainly helped me.  Clearing my source code cache made no effect fo rmy problem.

Link to comment
Not a bug. It's called recursion and it is totally legal.
A parent class's Dynamic Dispatch method had an instance of itself on its block diagram. The method was not set to reentrant execution, but also did not give me a broken run arrow. This caused the VI to recursively call itself until the application died.

 

I assume from reading this that the OP is aware of recursive functions, including the prerequisite(?) reentrency settings.  If I quote from the NI OOP FAQ:

You can configure a dynamic dispatch member VI to allow recursion by setting it to be reentrant and to share clones between instances.

I assume this means a DD VI is like all other VIs in this regard.

 

So am I to understand that DD VIs are in fact different from other VIs and support recursion even in the absence of the usual settings for reentrency?  That would be interesting, and probably very inefficient, and does seem to contradict the documentation I have come across over the years.

 

Don't write a recursive function without a base case... you'll use up all of memory. 

 

This also runs counter to my experience (in LV).  The "normal" LV recursion mechanism places a limit (32000?) on the recursion depth.  I do not run out of memory in the normal case, I simply get an error message about too many nested VI calls (no crash). The fact that a do-nothing VI here called recursively runs until crashing hints (to me, at least) that it is operating outside of the normal LV recursive mechanism.

  • Like 1
Link to comment
So am I to understand that DD VIs are in fact different from other VIs and support recursion even in the absence of the usual settings for reentrency?  

Yes, because that isn't necessarily a recursive call at run time. 

 

Suppose Square and Line inherit from Shape. Shape has a dynamic dispatch method "Draw".

 

Square.Draw might make four calls to Line.Draw. Is that recursion? No.

 

Now suppose you add Collection which inherits from Shape and contains an array of Shapes in its private data. For Collection.Draw, it calls Shape.Draw for every item in itself. Is that recursive? Maybe yes, maybe no... it depends upon whether Collection contains nested Collections. If Collection can only include other basic shapes then there is no possibility of recursion when the dynamic dispatch call to  Shape.Draw because it will never dispatch back to Collection.Draw. For that reason, the method can be used on itself without marking the VI as Shared Reentrant. 

 

This might also go some way to explaining why DD calls are actually very expensive compared to standard VI calls.....?

 

 

It does explain why they are more expensive, but not why they are (at the moment) very more expensive. Or, at least, that code hasn't changed and did not previously account for a substantial increase. What exactly is accounting for the substantial increase is at this time unknown, so I'll leave the door open to this being part of the problem. 

Link to comment

Quick update:  The first time I tried to run the posted code I got a crash, but now I suspect that was LV12 doing its thing and not directly related to the code.  When I try it this time I actually get a quite helpful error message:

 


post-26690-0-56746800-1391451632.png

 

This is a good start but still bugs me a little bit.  When you move from the world of strict, static typing into the world of dynamic typing then you expect a lot of errors to move from compile time to run time.  This is the price of doing business sometimes.  What I wonder is if it is really necessary to stop everything when this happens, could this be an error you could recover from?

 

This is mostly curiosity, I find recursion and DD to be a bit slow and avoid both as much as I can, combining the two is getting into slow-squared country, population unknown but not me.

 

Edit:  That is a great explanation by AQ, but it did not address what seemed to be happening at the time.  I understand what is and is not recursive and agree it is best to wait until run-time.  The OPs description was that real, honest-to-goodness recursion was happening and your reply was not a bug, that had me confused.  The error window I see makes all of the difference.  If the OP did not see it for some reason, ouch.

Edited by Darin
  • Like 1
Link to comment
Not a bug. It's called recursion and it is totally legal. Don't write a recursive function without a base case... you'll use up all of memory. 

 

This was a programming mistake, not something intended. I am assuming the wrong method got pulled out of the class and dropped on the block diagram accidentally. I know we can only protect ourselves from ourselves so much. I just wanted to be sure that this was expected behavior because it seems counter-intuitive to the "standard" expectations. I assumed the reason everything compiled fine had to do with DD and some sort of specific case (such as the one you mentioned) so it is good to have that clarified.

Quick update:  The first time I tried to run the posted code I got a crash, but now I suspect that was LV12 doing its thing and not directly related to the code.  When I try it this time I actually get a quite helpful error message:

 

attachicon.gifDD Recurse Error.png

 

This is a good start but still bugs me a little bit.  When you move from the world of strict, static typing into the world of dynamic typing then you expect a lot of errors to move from compile time to run time.  This is the price of doing business sometimes.  What I wonder is if it is really necessary to stop everything when this happens, could this be an error you could recover from?

 

This is mostly curiosity, I find recursion and DD to be a bit slow and avoid both as much as I can, combining the two is getting into slow-squared country, population unknown but not me.

 

Edit:  That is a great explanation by AQ, but it did not address what seemed to be happening at the time.  I understand what is and is not recursive and agree it is best to wait until run-time.  The OPs description was that real, honest-to-goodness recursion was happening and your reply was not a bug, that had me confused.  The error window I see makes all of the difference.  If the OP did not see it for some reason, ouch.

Darin, are you running this on a PC? I'm curious if not seeing an error dialog was due to it running on RT.

Link to comment
Yes, because that isn't necessarily a recursive call at run time.

 

Well it IS recursive (guaranteed) when the input terminal for the top-level DD call is directly connected to the DD sub-VI (as in the pic above).  Or am I overseeing something?

 

We already have type propagation checks for other LVOOP aspects (whether output wire has a direct connection to input wire for type retainment).  Should this not be another check?  I agree that the presence of the VI in itself does not automatically mean it's reentrant but having the input terminal wired directly to the VI does.

 

I think this could be an option to break the run button if the DD Vi is under these circumstances NOT set to be reentrant.

 

Otherwise I actually like LV leaving us to decide whether something is safe or not!

 

Shane.

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.