As I program in LabVIEW I am surprised by the number of
times small bits of code cause large problems.
For example, I was working with a large system that communicated with
many instruments. I made some changes,
tried running the code, and the program locked up! My gut reaction was to blame
the LabVIEW development environment or a driver, but in the end I discovered
the code I changed caused the hang.
I did finally find what was causing the hang, but it took a
long time. The hang was caused by two
resources that were used by two different pieces of code (see the Wikipedia’s
Dining Philosopher article for a good explanation of this). Both pieces of code had one of the resources,
but neither would relinquish the resource because they were waiting for the
This can be shown in LabVIEW with the following code:
So Don't do That
above example is simple enough to be trivially avoided. However, it can get more subtle. Consider a non-Reentrant SubVI; a second
caller to the subVI will block until the subVI finishes on the first
caller. This allows the same hang to
occur with a subVI and a queue as shown below.
Note that there is a second queue shown which is not causing the hang,
but is needed for the subVI to be called twice:
Still Want to Use References?
The code I had written was fixed easily. The code had started before Data Value
References (DVRs) existed in LabVIEW, so it used a Queue with a single element
as a referenced resource. I made a
change to the data dependency so that the Dequeue would happen later and not
block the other code from executing. The experience did make me a little more
cautious, and I’m on a quest to find out how to avoid these mistakes in the
future. Here’s what I’ve got so far:
Use a message-based architecture to avoid
sharing resources: I know many are
successful with message-based architectures like the actor framework. I cringe at doing this myself. It works, but it is trading one set of
problems for another.
Use the “In Place Element Structure” instead of
Queues for references. The In Place
Element Structure groups the data value read in the same structure as the data
value write. This means that the read
won’t happen until all the inputs to the structure are ready. The Dequeue function in the example above
happens before all the inputs are ready for the Enqueue function, and that
allows the hang to occur.
Consider setting VI executions to
reentrant. When creating a new VI, the
default is non-reentrant. If a VI is
non-reentrant, it is a resource that must be shared and it may block.
especially wary of code that must wait for multiple resources to be available. For such code, see the “Dining Philosophers”
link for ways to address the lockup issue.
The Good News
References are an intuitive way to program, and they are
used frequently in LabVIEW. Consider all
the references in LabVIEW:
You will use many references as you program in
LabVIEW. You may even start to use
references in your APIs as I do. If you
have advice or a story about references locking up code, I’d love to hear
Synchronization VIs: DVRs, Queues, Notifiers,
VI Server: VI References, Control References,
Driver APIs: DAQ Tasks, VISA Resources, FPGA
Computer Resources: File References, TCP Refnum,
Many, many others…