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: 

Global variable cluster race conditions?

Solved!
Go to solution

Hello,

 

I'm looking for some advice on global variable clusters.

 

I am working on a program that has a fair bit of input and output data from the controller modules as well as the user interface. As of now I have grouped some sections of data into clusters, and written those clusters to global variables. From a programming standpoint this has made things simpler for me because wherever I need a piece of data I just wire the global to a bundle or unbundle and pull out or write what I need. 

 

I have been mindful of race conditions this whole time, so I made sure that wherever I write to an element of any of my global variable clusters it is the only time I am writing to that element. Now, there are multiple places where I write to the same global variable cluster, but I am writing to different elements in that cluster.

 

For instance, I have a global variable cluster for the output signals sent to my cRIO that my DAQmx Write VI pulls from:

rseabeck_2-1622658279173.png

 

Here I am writing to one of the elements:

rseabeck_0-1622657805581.png

 

And here I am writing to a few other elements:

rseabeck_1-1622657958211.png

 

There are no instances where I am writing to the same element twice in the "Cell 1 cRIO output signals global" cluster, so it seems to me that this approach is safe. However, this is my first Labview project, and I know there are some very wise users around here who may be able to tell me if I am doing this completely wrong. I'd like to avoid having to split everything up into individual globals, but if that's what it takes then I guess it is what it is.

 

Let me know if you need any clarification! And thank you for your help!

0 Kudos
Message 1 of 12
(1,913 Views)

No matter what language you are programming in, constantly reading from and writing to globals is a dangerous game to play.  That being said, almost all my globals are clusters, but I use them only to hold things like configuration parameters that only get written to once (from a file, for example) and read when necessary.

 

There must be a better way, but I am not well-versed with cRIO, so maybe there's not.

Bill
CLD
(Mid-Level minion.)
My support system ensures that I don't look totally incompetent.
Proud to say that I've progressed beyond knowing just enough to be dangerous. I now know enough to know that I have no clue about anything at all.
Humble author of the CLAD Nugget.
0 Kudos
Message 2 of 12
(1,886 Views)

@rseabeck wrote:

 

I have been mindful of race conditions this whole time, so I made sure that wherever I write to an element of any of my global variable clusters it is the only time I am writing to that element. Now, there are multiple places where I write to the same global variable cluster, but I am writing to different elements in that cluster.

 


That is a classic scenario that can and probably will lead to race conditions. Just keep in mind, even if you change only one element of a cluster, you are writing back ALL elements of the cluster. If you do these writes in two places in parallel, you cannot be sure, that both your changes will finally be part of your global cluster.

 

Regards, Jens

Kudos are welcome...
Message 3 of 12
(1,879 Views)
Solution
Accepted by topic author rseabeck

As stated, using globals you are flirting with diaster. They are safe if you ar eusing WORM (Write Once Read Many) globals but in your usage you are not guaranteed that you do not have a race condition and that you are corrupting your data. Globals can also be used safely if you guarantee that there is ONLY one place that writes to the global. When I say this I am not referring to multiple writers that appear to do it without conflict I mean literally only one writer.

 

I write very large applications and I cannot recall the last time I used a global variable. There generally better methods. Messaging between tasks using queues or notifiers for example. Definign tasks or using OOP to encapuslate your data are other options.



Mark Yedinak
Certified LabVIEW Architect
LabVIEW Champion

"Does anyone know where the love of God goes when the waves turn the minutes to hours?"
Wreck of the Edmund Fitzgerald - Gordon Lightfoot
Message 4 of 12
(1,875 Views)

I had a cluster like that in one of my first LabVIEW projects.  That is where I truly learned what a race condition was.

 

 

A better method is a functional global variable.  But now just one where you read from it, change something and set it back.  You need to make sure the read and write are both enclosed in the subVI.  You'd have a read method for just when you want to read.  An initialize where you set the value of the master cluster initially.  And then a case for each element of the cluster where you update a specific element of the cluster so that in one action you read, update the element, write the cluster back to the shift register.

 

The problem with this is that it can get messy with bigger clusters, or more complicated clusters where you might have various types of data and would thus need multiple different inputs, or a variant input where you'd have to then convert the variant to the correct datatype.

 

I haven't done this, but you might want to look at or the newer map functions where you each piece of data becomes an element in the map.  Or use a Data Value Reference so that you can lock access to the master cluster and update the appropriate elements of the cluster as needed.

0 Kudos
Message 5 of 12
(1,871 Views)

I just wanted to amplify a specific detail related to what JensG69 already said:

 


@JensG69 wrote:

Just keep in mind, even if you change only one element of a cluster, you are writing back ALL elements of the cluster. If you do these writes in two places in parallel, you cannot be sure, that both your changes will finally be part of your global cluster.

In the screencap you posted, here is how that race condition happens.  Both screencaps show you reading from the global immediately as the pictured code starts executing.  Then code inside the pictured structures executes for a bit and when it's done, some cluster elements are updated. Then you write the entire cluster back to the global.

 

If both bits of pictured code start running at the same time, it's *very* likely that you'll retain updates from only one of them -- it will be the slower one that finished last and overwrote whatever had been updated previously by the faster one.

 

Really, you need to find a different way, even at the expense of extra time and learning curve.  I think a Functional Global (as suggested by  RavensFan) is likely to be the most beginner-friendly approach overall.

 

 

-Kevin P

CAUTION! New LabVIEW adopters -- it's too late for me, but you *can* save yourself. The new subscription policy for LabVIEW puts NI's hand in your wallet for the rest of your working life. Are you sure you're *that* dedicated to LabVIEW? (Summary of my reasons in this post, part of a voluminous thread of mostly complaints starting here).
Message 6 of 12
(1,823 Views)

Shameless plug: A Look At Race Conditions


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
Message 7 of 12
(1,807 Views)

Thank you Mark. This was very clear and concise.

 

I started dusting off my Core 2 notebook and I think I am going to tackle this problem with channel wires. I know I could use queues too, but I think that will get cluttered since I have queues set up for my state machines.

 

I appreciate the help all of you have given!

0 Kudos
Message 8 of 12
(1,747 Views)

@rseabeck wrote:

There are no instances where I am writing to the same element twice in the "Cell 1 cRIO output signals global" cluster, so it seems to me that this approach is safe. However, this is my first Labview project, and I know there are some very wise users around here who may be able to tell me if I am doing this completely wrong. I'd like to avoid having to split everything up into individual globals, but if that's what it takes then I guess it is what it is.


That sounds like good care, but if you have several loops you'll still get race conditions. Using the mentioned Functional Global/Action Engine should take care of that. Most programs end up having just 1 loop, or atleast 1 Consumer loop doing most all of the actual work, if the cluster resides in that as a shift register all writes happens in 1 place and nowhere else. 🙂

G# - Award winning reference based OOP for LV, for free! - Qestit VIPM GitHub

Qestit Systems
Certified-LabVIEW-Developer
0 Kudos
Message 9 of 12
(1,737 Views)

Putting them in a cluster pretty much guarantees race conditions.  Just move the elements out of the cluster.

"If you weren't supposed to push it, it wouldn't be a button."
0 Kudos
Message 10 of 12
(1,704 Views)