Is Your Code Headed for a Costly Disaster?

Publish Date: Dec 16, 2013 | 2 Ratings | 5.00 out of 5 | Print | Submit your review

This article is part of a series on the Top 5 LabVIEW Rookie Mistakes. Subscribe to NI News to keep up with the series.


Figure 1. Formula 1 Car Crash / formula1wolf / CC BY-NC-ND 2.0

Properly architected code can run like a top-performing race car on a Formula 1 track. Contrarily, poorly architected code can crash and burn and cost a company millions of dollars without it ever knowing. One of the main ways that this hidden disaster manifests itself is through race conditions.

Race conditions in the programming world don’t center on the weather like in Formula 1 but instead with the mishandling of data. Race conditions can be found in all programming languages and are difficult to diagnose but with better training and software engineering practices, a developer can leave them in the dust.

What Are Race Conditions?

Race conditions occur when data values are accidentally overwritten due to the timing of events or scheduling of operations. Race conditions are a common problem for programs that execute multiple tasks in parallel and share data or resources between them.

Imagine a bank account that is a shared resource. A person can operate on that resource by withdrawing or depositing money. If two people can access the bank account at the exact same time and one tries to deposit money while the other tries to withdraw money, which operation is performed?


Figure 2. Row of ATMs / denniswong/ CC BY 2.0

Banking programs have a buffer for operations so they queue up and are performed as they come in. This is available through proper software architecture and is not the default for a singular memory space like a variable. If two operations are sent to a variable at the exact same time, one would be performed and the other overwritten as there is no extra memory assigned to remember the other operation.

If two areas of code access a shared resource (for example, local, global, or shared variables), then commands have to “race” each other to be the one operated one. Race conditions can lead to software-controlled hardware acting unexpectedly or even losing test data as it is overwritten.

There are ways to diagnose and, better yet, prevent race conditions from even happening.

How Can Race Conditions Be Prevented?

Unlike other programming languages where passing data through variables is essential, NI LabVIEW system design software provides the dataflow method of moving data from one part of a program to another. The parallelism inherent to LabVIEW makes overusing variables problematic because shared memory is often accessed by different code locations at the same time and lead to race conditions.

One of the better ways to test existing VIs for race conditions is to use the LabVIEW VI Analyzer Toolkit. Of the more than 90 tests that are included with the toolkit, one improves VI performance by checking for overuse of local and global variables. Using the VI Analyzer report, a developer could efficiently jump to different parts of code and control access logic, like semaphores, or switch to a data-secure method of communication to negate the race condition.



Figure 3. This semaphore shipping example from LabVIEW 2013 shows how race conditions can be prevented.

Semaphores are found in other programming languages and operate similarly in LabVIEW. Semaphores are objects used to help control access to portions of code, like a shared resource. Imagine a library book as the shared resource. The semaphore acts like the librarian and allows only one person to check out a book at a time. Another patron can’t read a checked-out book until it is checked back in. Semaphores work similarly by adding “locking” and “unlocking” code around sections of critical code that operate on a shared resource. If another section of code tries to access a “locked” resource, then it will wait there and not execute further until the other section of code is completed and “unlocked.” This functionality is also known as a mutual exclusion, or mutex.

A developer must be cautious using semaphores though as it could result in a continuously blocked program. When code encounters a locked semaphore, it blocks the rest of that line of code execution until it is unlocked. If the operation that unlocks the semaphore takes a long time, this could lead to timing behaviors that the developer didn’t expect. Similarly, the unlocking operation could continuously operate without ceasing and the other execution would be infinitely blocked while waiting for the other section to complete. Using other data communication methods instead of a variable can result in a healthier architecture than using control access logic sometimes.

A developer can safely pass data from one part of a LabVIEW program to another using a variety of methods, including wires, queues, events, notifiers, and functional global variables. These mechanisms are each designed for a specific use case, but all have the advantage of eliminating race conditions when properly used. The VI Analyzer tool does not test for other shared resources like files, references to hardware resources, or shared variables which can also introduce race conditions so it is better to use a more data-secure architecture like a Queued Message Handler (QMH) and be mindful when using shared resources programmatically.

The QMH is a scalable architecture used by many LabVIEW applications. The QMH facilitates multiple sections of code running in parallel and sending data between them without race conditions. This is done by using queues to transfer data between loops instead of variables. The nature of queues and data flow make it so operations are maintained even when they are sent at the same time.

 


Figure 4. Queued Message Handler Template From LabVIEW 2013

The QMH is similar to a state machine in that each section of code can represent a task, such as acquiring data, and future tasks can easily be added with a new loop. The QMH is a version of the Producer/Consumer design pattern, where the user interface produces messages and the tasks consume them.

The best possible method to avoiding race conditions is to start with a proper architecture, like the QMH or others, that passes data between loops with a data-secure method and safely accesses critical shared resources. By doing this, developers can help ensure that their applications spend more time on the track and less time in pit row.

For more detailed information on race conditions and how to avoid them, access the LabVIEW self-paced online training (ni.com/self-paced-training) for Core 1 on Race Conditions and Core 3 on the Queued Message Handler.

Free access to self-paced online training comes with the NI Standard Service Program (SSP) and new LabVIEW purchases.

This article is part of a series on the Top 5 LabVIEW Rookie Mistakes. Subscribe to NI News to keep up with the series.

 

Back to Top

Bookmark & Share


Ratings

Rate this document

Answered Your Question?
Yes No

Submit