Copyrighrt (c) 2014-2020 UPVI, LLC

LabVIEW Error Handling, DLLs, and Threads

Last Updated: Saturday, 02 January 2021 Published: Saturday, 12 December 2020 Written by Jason D. Sommerville

Version 1.2.1 of Live HDF5 was a major pain to create, but it addresses a problem that has existed in Live HDF5 since the beginning. The problem is that in the LabVIEW threading model multithreaded DLL calls can happen from any of LabVIEW threads in the execution system under which the calling VI is running. Unfortunately, many external libraries, including a few minor ones like, say, the Windows API, and, of course, HDF5 use thread-local state data to keep track of errors, among other things. The net result of this is that if a Call Library Function Node (CLFN) is configured to run in any thread, and the function generates an error, and a second function call is required to retrieve the error information, there is no guarantee that the error retrieval call will be called by the same thread as the first. In the event that the error retrieval does not happen in the same thread, improper error information is returned. The example below, shown calling the (deprecated) Windows API function OpenFile demonstrates this. (Why would you ever want to implement OpenFile like this in LabVIEW? I have no idea. That's not the point.)

Option 1 (wrong): Thread-local errors in external code can't be handled properly

The solution proposed by NI, and most LabVIEW developers is to change the calls to UI thread. I suspect that this is why the option exists, and it does indeed solve the problem. (I do wonder if in the particular case of calling into the Windows API if LabVIEW might not have the potential to schedule API calls in between OpenFile and GetLastError, thereby messing this up differently. However, for the more common instance of calling a non-Windows, non-UI library, this is not a concern.)

Option 2 (Okay): The NI recommended way to handle thread-local errors in external functions

What I have never been able to fathom is why this is a good idea. Why would I want to bog down the UI thread with what could be a potentially time consuming activity in a DLL function. For instance, what if I'm interfacing to a library that accesses the disk to write potentially large files?

So, I how do we get the non-blocking thread efficiency of Option 1 with the proper error handling of Option 2? The only way to do that is to wrap the function call in another DLL, as shown in Option 3. By calling both OpenFile and GetLastError in an external call, we guarantee that GetLastError executes in the same thread and in the proper sequence. Of course, we had to go through the hassle of writing and compiling a custom DLL to make it happen.

Code for MyWrapper.dll

Option 3 (Best, but hard to implement): Write an external function that wraps the function and error handling calls.

Of course, this is exactly what is required for the Live HDF5 library. Unfortunately, that meant writing wrapper functions for something like 400 HDF5 calls. In case you're wonder, yes, some automatic code generation was used.

As an additional suggestion, in some cases it is more beneficial to pass the entire error cluster into the wrapper DLL. This is how Live HDF5 handles the errors, as shown in the example below. In my case, this is because the error handler for HDF5, H5Ewalk, returns significantly more information than a single code and I determined that it was easier to handle the population of the LabVIEW error cluster consistently if it was handled in the C-code. To do this, the error cluster parameter in the CLFN should be configured for Adapt To Type, Handles by Value. Make sure to use the proper C-type struct definition, and when in doubt, use the CLFN configuration

Live HDF5's typical error handling scheme. HDF5 functions are accessed through wrappers which populate LabVIEW error clusters.

 

Configuration of the error cluster parameter in the CLFN call.

Code generated by "Create C File" from CLFN (editted for clarity)

The fact that this needs to be done is a royale pain. It would be nice if LabVIEW had a way for the CLFN nodes to be linked together to ensure that consecutive calls (or even non-consecutive calls) were all called from the same thread.

Undoubtable this is the point where some pure-programming proponent out there will now pipe up and say, "Yes, but if your library was designed correctly, this isn't a problem." My response is, "I live in the real world, not the pure world. In the real world, I am not willing or usually able to reimplement someone else's library to make it pure code. Take your Haskell back to the CS department."