You may have never heard of the UI thread or the root loop, but as a revision to the old adage goes, what you don’t know can hurt you.
LabVIEW is a naturally multithreaded application and it dynamically allocates these threads. There are five execution systems in LabVIEW, and each execution system allocates four threads for execution, per processor core by default. For a quad-core processor LabVIEW will allocate 80 execution threads; a dual-core processor has 40 execution threads, and so on. In addition to these execution threads, there exists the UI thread.
So, what is the UI thread?
The majority of nodes in LabVIEW will have no problem executing in the 20+ execution threads mentioned above, but certain nodes in LabVIEW can only execute within the UI thread. There is a void of documentation regarding this subject, and therefore there is no real comprehensive list of functions and VIs. The trick to understanding the UI thread is knowing the basic processes that will require the thread, which are listed below:
- Screen drawing
- Keyboard/mouse input
- VI Server
- Thread-unsafe CINs and DLLs
The UI thread uses cooperative multitasking to allow simultaneous execution of nodes, and it exists on all implementations of LabVIEW: Windows, Linux, Mac, etc.
Well, that doesn’t seem so bad… what gives?
The Root Loop
It requires the UI thread to be idle before it can execute, and blocks the UI thread while it is executing.
There exists an even smaller subset of nodes in LabVIEW that require the root loop to execute. These nodes are responsible for OS-level messaging. While it is important to understand what nodes run in the UI thread, it is critical to understand what nodes run in the root loop, a few of which are listed below:
- VI server methods that state “Must wait until user interface is idle: Yes” in the Context Help. Print VI, Run VI, and Save Instrument are a few methods that require root loop. One could speculate that these functions interact with the OS file system.
- VIs that invoke an OS dialog, such as One/Two Button Message, File Prompts, etc. These dialogs are native OS dialogs. Native LabVIEW dialogs, such as Three-Button Message, do not require root loop.
- Open VI Reference. Opening a Typed VI Reference requires root loop. Opening an Untyped VI Reference will only require Root Loop if a path is passed; if a string is passed the VI will be loaded from memory and not require Root Loop.
- Context Menus. These include things such as the color palette selector, calendar date popup, waveform graph configuration, VISA resource selector, combo box selector, etc.
How can I determine that the UI thread or root loop is to blame?
UI thread and root loop issues can be hard to track down in large applications. If a time-critical process is running poorly, and performing UI actions (clicking, dragging windows, long-click on the title bar, opening a context menu or dialog) seem to exacerbate the situation, then there is a good chance that a portion of the time-critical process is running in the UI thread and is also victim to nodes requiring root loop.
So how should I take this into consideration when writing my application?
In addition to the above information, there are special cases regarding the UI thread and root loop that might not be as obvious. The following list is a good starting point for what you can implement to avoid running into issues with your application.
- Open Typed VI References ahead of time to load the VIs into memory.
- Substitute Run VI with Call by Reference.
- Avoid using OS dialogs such as: One/Two Button Message, File Prompts, etc.
- Avoid making calls that place the VI in the UI thread if not needed, and abstract those calls to separate VIs.
- Keep in mind that Call by Reference when calling a Remote VI will require the UI thread on both the Client and Remote side.
While this isn’t a fully comprehensive list of all the nodes and instances that will put a VI into the UI thread or invoke the root loop, hopefully it serves as a solid base of knowledge on the topic. The root loop is elusive, and often hard to debug or pin as the culprit for an issue within an application. It isn’t a bug in LabVIEW, but a consequence of OS-level messaging that can’t be avoided. With this knowledge you should feel confident in finding a solution when the UI thread and root loop don’t want to play nice.