Jump to content
Aristos Queue

If LV could detect race conditions, should we break the VI?

Recommended Posts

Let me start by saying what I mean by "race condition": two processes (or threads or execution paths or <your favorite term here>) both simultaneously read some bit of data representing system state, then modify the system state accordingly, and when both are finished, the new state of the system depends upon which one executed first.

 

That is a very broad definition. Most users know the trivial case: you have a global variable representing some sort of counter, and you have two processes, Alpha and Beta, both wanting to increment the value. If the starting value is "1", then the ending value is expected to be "3". The race occurs here:

  • Process Alpha reads the counter: value =1.
  • Process Beta reads the counter: value =1.
  • Alpha adds 1 to the value it read and gets 2.
  • Beta adds 1 to the value it read and gets 2.
  • Alpha writes to the counter: value = 2.
  • Beta writes to the counter: value = 2.

Oops. But there can be much more subtle bugs. For example:

  • Alpha is a Producer Loop
  • Beta is a Consumer Loop
  • Beta checks "Is the queue empty?" to decide whether it has finished processing all the data.
  • At some point, Beta is just a bit faster than Alpha and the queue empties, so Beta decides to quit, thinking it is done with all the data.
  • Alpha adds more items to the queue, but Beta never processes them.

Race conditions are notoriously hard for users to catch. They have historically been just as hard for compilers to notice in all but the most trivial conditions. As the LabVIEW compiler has gotten better at its analysis over the years, I see some potential for the compiler to recognize when a bit of memory is potentially being used by multiple execution paths in "a race condition like manner". I'm using that deliberately vague term because the definition of what exactly constitutes a race condition may be part of the conversation here.

 

If the LV compiler knows beyond a shadow of a doubt that some egregious behavior, such as an unprotected global VI or shared variable being used for read-modify-write in multiple execution paths, should LabVIEW break the VI? Is that a compiler error? Or is it something more like a coercion dot that should be highlighted but not ruled out? To put it another way: Are there race conditions that you have in code that are desirable conditions? Is that because it is just too hard to program the right way? Or do you have some where the correct program really is one where execution order should matter to program output? If the robot's right hand grabs the apple a microsecond before its left hand grabs the orange, are there cases where it is correct for that to affect which one it chooses to serve you for lunch?

Share this post


Link to post
Share on other sites

 If the robot's right hand grabs the apple a microsecond before its left hand grabs the orange, are there cases where it is correct for that to affect which one it chooses to serve you for lunch?

 

You mean a random lunch generator?

Share this post


Link to post
Share on other sites

Are there race conditions that you have in code that are desirable conditions? Is that because it is just too hard to program the right way?

This. I've never seen any deliberate implementation of race condition (at least not before Shauns post ;) ), but sometimes it's just too hard to code without them. Especially if something has to be done quickly ("customer wants it for tomorrow"-type). I'd be very, like VERY, unhappy if LV would break my quick workaround code and disallow its compilation because of possible race, which I'm fully aware of. And sometimes I might even have some clever anti-race chunk of code, which compiler might not understand.

On the other hand, I'd be very happy to have helping LV hand which would tell me "hey, there might be race condition here, check it", but without the VI-breaking part. Generally, I don't think that breaking the code is good idea to fight any of bad programming practices. Imagine that some begginer, who just started using LV, or even started programming, see error about some "possible race condition due to wrong usage of global variables". He'd just be confused. Or even worse - he'd blame LabVIEW that it does not allow him to run his programs and just throws errors at him, and we all know where he would go from this point. 

Edited by PiDi

Share this post


Link to post
Share on other sites

As far as breaking VIs when such a condition is detected, I need to give it some thought.

I know I've been guilty of leaving one particular race in production code simply because if the race was ever to be "lost" it really wouldn't matter but fixing the race would be a very risky and time consuming bit of work. The corollary to that is if the outcome doesn't matter is it really a race condition or more an undefined order of execution? I'd tend to argue the later, but could still see some small bit of misguided merit to the race side of that coin.

Regardless of what action is taken, I think the possibility of having the compiler detect even the most primitive of such conditions a great advancement.

Nice work.

Given how easy it is to leverage LabVIEW to make parallel processes I think educating users into pointing out resulting races a very good step into making LabVIEW a more mature development environment.

  • Like 1

Share this post


Link to post
Share on other sites

mje: I don't actually know if I can do anything to improve this area, I just noted that there was an increasing amount of analysis in the compiler that might be useful and I decided the time had come to ask, "if I did start working on something like that, what vector should I be on?"

 

 

Imagine that some begginer, who just started using LV, or even started programming, see error about some "possible race condition due to wrong usage of global variables". He'd just be confused.

On the other hand, he's even more confused if he gets his program running, tests it on three systems, deploys it, and it fails once every three days out on his client's site. :-)

 

We break VIs when you forget to wire required terminals. We put coercion dots when you have a type change. The race condition is somewhere between these two -- the former being "this isn't ever going to do what you think it will" and the latter being "this will be good enough most of the time but maybe could be more efficient but at least it does what you intended". But I'm not sure which one it is closer to.

Share this post


Link to post
Share on other sites

mje: I don't actually know if I can do anything to improve this area, I just noted that there was an increasing amount of analysis in the compiler that might be useful and I decided the time had come to ask, "if I did start working on something like that, what vector should I be on?"

 

 

On the other hand, he's even more confused if he gets his program running, tests it on three systems, deploys it, and it fails once every three days out on his client's site. :-)

 

We break VIs when you forget to wire required terminals. We put coercion dots when you have a type change. The race condition is somewhere between these two -- the former being "this isn't ever going to do what you think it will" and the latter being "this will be good enough most of the time but maybe could be more efficient but at least it does what you intended". But I'm not sure which one it is closer to.

 

I've though for a long time we've needed another option apart from just Error and Warning. Errors break VIs, but  warnings are just overwhelming in number but completely trivial. So much so that they are usually ignored and/or turned off-ineffective.. I think most warnings should be regarded as "hints" and things like your race condition are actual warnings but  they shouldn't break the VI - mainly for Mje's reason that it may not matter but also for "out there" edge cases like the RNG.

 

Personally, I'd love to see warnings (my definition of them) about race conditions. If you could pull it off, it would prevent quite a few of us stepping in those bear traps.

Edited by ShaunR

Share this post


Link to post
Share on other sites

I've though for a long time we've needed another option apart from just Error and Warning. 

That's essentially what coercion dots are. We could certainly do more in this area -- annotating diagrams with extra info to emphasize "this is what is going to happen". 

Share this post


Link to post
Share on other sites

I don't think I would want a break either, if only for two reasons:

  1. The "nanny state" argument given by PiDi. Sometimes you might want the race conditions.
  2. Possible mistakes by NI. We have seen cases where there bugs in the inplacer or other low level parts, and having something like this that breaks your VI and the only way to fix it is to find some way to write the code such that the compiler doesn't see it as a race condition is not something I would appreciate. I agree it's better than a bug in the inplacer, but I still wouldn't want to run into such a mistake.

A warning of some sort, on the other hand, is definitely welcome.

Share this post


Link to post
Share on other sites

I'm with Shaun here: warnings in their current state are ineffective due to their overwhelming nature. I can't remember the last time I paid attention to the warning list.

 

Perhaps if we were given a way of disabling classes of warnings such that the list became manageable, but otherwise I doubt most would ever see a race condition warning.

Share this post


Link to post
Share on other sites

I'm not aware of any intentional race conditions in my code.  The race conditions you describe can be hard to isolate and debug, but hopefully this just means developers will learn the right way of doing things.  One thing that comes to mind is performing a floating point "Is Equal" function.  This is one of those mistakes just about every developer makes once, then they are aware of it and write code in a more stable manor.  This example is different from a race condition because it is easy to debug and test but the outcome in my mind is the same.  Developers learning from their mistakes and write better code.  I'm not sure I need LabVIEW to do more hand holding so developers don't learn these important life lessons.  Is this going to become a form of LabVIEW coddling?

 

I don't think I would want a break either, if only for two reasons:

  1. The "nanny state" argument given by PiDi. Sometimes you might want the race conditions.
  2. Possible mistakes by NI. We have seen cases where there bugs in the inplacer or other low level parts, and having something like this that breaks your VI and the only way to fix it is to find some way to write the code such that the compiler doesn't see it as a race condition is not something I would appreciate. I agree it's better than a bug in the inplacer, but I still wouldn't want to run into such a mistake.

I worry about this too, but AQ made it clear this will never happen...

 

If the LV compiler knows beyond a shadow of a doubt that some egregious behavior...

I wouldn't trust that the compiler would never make a mistake in detecting this type of issue.  For this reason I would also be in favor of not breaking the VI, but informing the developer with some other means, like a warning.

Share this post


Link to post
Share on other sites

Coming from a different point of view, I have spent a lot of time helping out with FRC.  Yes, the code base helps greatly and takes care a lot of issues to minimize what high school kids need to know about labview.  The code base tends to break dataflow with heavy reliance on Globals.  In addition, I've seen many many times people try to implement code with local variables and have it perform poorly.  A simple latch for a button press is an example.  The point is that new programmers have a pretty big learning curve jumping into g-code, despite our best efforts they will try to apply their text-based variable-centric strategies, and that shouldn't stop them from getting something to work (i.e. a mysteriously broken VI).  A warning would be fantastic.  It also needs to be accurate.

 

From the point of view of an experience programmer I think the tool would need to be very intelligent for it to be helpful.  Without knowing the details about how you envision this working, I imagine it would be quite good at catching your alpha/beta loop counter races, but so am I.  Experienced programmers are quite good at creating very clever race conditions.  Code reviews are good for helping with these.  Again, I don't think breaking the VI is the right way to go.  A good warning that can be dismissed would be nice and that's with it working well.  If it erroneously found race conditions I'd be likely to just turn it off.  Much like the auto-insert feedback node 'feature'.

Share this post


Link to post
Share on other sites

I'm also in the "definitely don't break vis unless you really have to" camp, with a caveat of "unless the user asks you to". There is a problem with being overwhelmed by warnings, but I wonder whether some UI that let the developer selectively make warnings 'break' VIs might work - so I could choose to let race conditions break things, or choose other warnings to cause breakage. That sort of 'soft' breakage might be helpful.

Share this post


Link to post
Share on other sites

Thanks for the feedback, everyone. This is one of those "years away on the horizon" projects for me, but I wanted to get a feel for how to start thinking about it. 

 

Let me ask the question another way -- there is a body of research out there on software constructs that cannot create race conditions. Certain operations are simply ruled out even if they are frequently ok because they open the door to problems but it is too hard (or unknown) to prove whether a given usage of an operation is safe or not. 

 

Assume that LabVIEW retained the ability to have a raw global or a free-for-all DVR to do the "I just want to do this and have it work the easy way" solution, we could also have a separate feature -- let's call it a "safe global" for the moment -- which didn't allow all the flexibility but in return allowed you to know that you didn't have issues. Would there be interest in LabVIEW developing that as a parallel solution? Or would you rather LV focus on improving the warning feedback for questionable cases in the easier-to-use-but-error-allowing solutions? The two areas are somewhat orthogonal. 

  • Like 1

Share this post


Link to post
Share on other sites

I'm no doubt a minority given the current list of replies, but I'd say warnings or feedback isn't the way to go. I'd prefer effort go to building safe constructs that leverage the existing strengths of the language.

I mean a DVR is almost there, what if you could have implicit named globals that are selectable by name at the boundary of an IPE structure? Instant "safe" global, the user doesn't need to know anything about the synchronization going on behind the scenes. I realize of course this is no safer than a normal DVR but you'd be solving a lot of synchronization issues surrounding globals right out of the gate using pieces that are already there...

Even better if these named variables need not be global, but could be local or arbitrarily scoped data.

Basically I don't believe it's the IDE's job to teach the programmer, which is what a warning system is if it's not completely ignored. The IDE's job is to give me rock solid tools I can use easily.

  • Like 2

Share this post


Link to post
Share on other sites

I'm with Shaun here: warnings in their current state are ineffective due to their overwhelming nature. I can't remember the last time I paid attention to the warning list.

 

I regularly do to find hidden objects in a diagram of inherited code :D but normally ignore the useless unconnected terminals warning. This last one was maybe sort of useful in old days but nowadays with event structure and what else I often end up with terminals just laying around in the according event structure without ever needing to be connected.

 

For the rest I'm with everyone else here: Don't treat an analysis result like race conditions as a breaking error. It's a serious warning (unlike unconnected terminals) but certainly shouldn't break code execution by its own.

Share this post


Link to post
Share on other sites

For the rest I'm with everyone else here: Don't treat an analysis result like race conditions as a breaking error. It's a serious warning (unlike unconnected terminals) but certainly shouldn't break code execution by its own.

 

I agree. If it compiles, don't break it. Like in C where your code compiles, even if you use uninitialized variables for example. A warning would be the right kind of thing to issue.

And please allow the terminals of the event structure to be hidden to avoid false warnings.

Share this post


Link to post
Share on other sites

I agree with this.  If it compiles -- even it it's "wrong" in what it does -- then it should not "break" in the IDE.  Some form of warning that CAN be ignored by those who want to do so, is really the best solution, even if there are lots of LV programmers who routinely ignore errors that are generated by the IDE.

 

In other words, if you really want to setup what some would consider to be a "race condition" then the language should allow for that.  No matter how thoroughly the original architecture of the underlying language, there will be "holes" and possibilities that developers will propose based on their own idiosyncratic, "advanced" explorations around what is perceived by them as limitations in the language.  After all it is an iterative process in the end no matter what else you may want to believe.

Share this post


Link to post
Share on other sites

Basically I don't believe it's the IDE's job to teach the programmer

 This. 1000 times, this.

 

Don't compilers like GCC have a ton of command line switch options? Diverging a bit, I would like to have a bunch of options for what is displayed as warnings etc.

 

However, AQ, I'm weary of totally ruling out your suggestion. As things stand now I think breaking code based on a race condition is wrong in my opinion. But if the technology allows it, why not leverage that? I just feel currently it doesn't. I'm sure with lots of new ideas things seem "wrong" because we can't really imagine them. But once they are presented to us and we become used to them, we don't know how we lived without them.

Edited by GregFreeman

Share this post


Link to post
Share on other sites

Could the compiler fix some of the easier race conditions? It could see that I'm doing something really simple with a global and block other callers for the 10 microseconds I'll need it.

Oh, look, you have a property node floating off in the distance, let me do that before I start the event structure.

Edited by infinitenothing

Share this post


Link to post
Share on other sites

I would welcome the ability to guarantee race-condition free code but it must not replace the ability to do otherwise.

 

Breaking a VI because of race conditions is allowable only if the programmer has explicitly told the IDE that this code MUST be race-condition free.  Otherwise the effect on us poor mortal programmers will be a major burden.

 

Given the idea of being able to declare specific behaviour for certain VIs (as I would propose it) what else could we force? No data copies? No UI Thread? What else would be enforcable down the line in several years?

Share this post


Link to post
Share on other sites

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.