In the central coastline deserts of Chile there exists an elusive wingless wasp that looks like a panda bear, is referred to as an ant, and has a sting strong enough to put a cow down. Meet the Panda Ant: Similarly, deep within LabVIEW there exists an elusive message loop that has the potential to sting you. The Panda Ant is neither good (its sting can paralyze a cow) nor bad (it looks like a panda bear), but learning about it now will prevent you from getting stung by it in the future. The same can be said for the root loop, it's not a bug in LabVIEW and you have most likely never noticed that it exists, but learning about it now could prevent you from getting stung by it in the future. Getting StartedIf you Google, "LabVIEW 'Root Loop'" you will get hits for posts on LAVA, the NI forums, the NI Community pages, the NI Idea Exchange, the LabVIEW Wiki, and if you dig deep enough you will find great information on Info LabVIEW. Sometimes the links will prove fruitful in filling in portions of the picture, other times you find dead ends. The information is out there, but is not in one centralized location. Throughout this post I will do my best to include helpful links as references in the post where appropriate. My hope is that this blog post will synthesize this information and hopefully take some of the mystery out of the root loop. Root Loop 101In this reply on the NI Idea Exchange Aristos Queue summarizes the Root Loop as follows, There exist nodes in LabVIEW that will block the UI Execution System because they require the root loop to execute. When people run into problems with the root loop it is because they expect multiple nodes, each of which depend on the root loop, to execute concurrently. Below is a list of these nodes (complete to the best of my knowledge). "Most nodes in LabVIEW can be performed in any execution thread. Dialog FunctionsAny function call that use an OS dialog window requires the root loop. This includes the One Button Dialog Function, Two Button Dialog Function, and any function that launches an open file dialog when a file path input terminal is left unwired (like the Open/Create/Replace File Function). Notice that the Three Button Dialog Function is not on this list, it does not require the root loop as it is native to LabVIEW. Open VI Reference (In certain situations)Open VI Reference will require the root loop when it is used to open a reference to an untyped VI by path, or when it is used to open a reference to any typed VI by path or name. Notice that when this function is used to open a reference to an untyped VI by name it does not require the root loop. Here is a quote from the LabVIEW Help file regarding the "vi path" input terminal of the Open VI Reference function: If you wire a path, LabVIEW waits until the user interface is idle to load the VI from disk. If you wire a name string, LabVIEW does not need to wait until the user interface is idle, as it does not load a VI from disk. LabVIEW will only search in memory for a VI with a specified name. VI Server MethodsCertain VI Server Methods for the VI class require the root loop to execute. You can tell which ones by looking at their context help for "Must wait until user interface is idle: Yes". The two I know of are RunVI and SaveVI. Context MenusThese shortcut menus can require the root loop to execute. This means that any time an operator opens a context menu for a front panel object while the application is running the UI execution system can be blocked. One example of this is the calendar popup on the time stamp control. Take a look at the video below to see an example. Here is another example showing how context menus will block other nodes that require the root loop to execute, this example focuses on the Open VI Reference function. [EDIT: See this update for deeper dive into the above demo] For an example of how to safely setup the Asynchronous Call by Reference node take a look at the Launch Actor Core.vi that ships with the Actor Framework: (C:\..\vi.lib\ActorFramework\Actor\Launch Actor Core.vi): The LabVIEW Execution SystemBefore we go further we should take a quick detour into LabVIEW's different execution systems. Most of what I mention here is from the LabVIEW Help article entitled "Multitasking in LabVIEW", which I recommend reading. LabVIEW is multithreaded, a fact which we can make use of with modern processors with multiple cores. An application running on a machine with multiple cores can take advantage of the operating system's multitasking capabilities to run multiple applications at the same time. LabVIEW's multithreading capabilities extend the OS's multitasking so that multiple tasks within a LabVIEW application can run in parallel with other applications that are running on the system. LabVIEW has six multiple execution systems (see list below or image above). Each execution system maintains a queue of active tasks. If all of the tasks in the queue have the same priority, then after a task has executed for a certain amount of time it moves to the end of the queue and the next task runs. The active task will execute by calling the generated code of the VI. At some point, the generated code checks back in with the execution system to see if it needs the thread back so that it can assign another task to run. If not, the code for the VI continues to run. Once a task completes it is removed from the queue. Here is a breakdown of LabVIEW's execution systems:
#Threads = #Cores * 4 Priority Levels * 5 Non-UI Execution Systems + 1 UI Execution System For a machine with a single core processor this would equate to 21 threads. There is a great utility located at vi.lib\Utility\sysinfo.llb\threadconfig.vi that allows you to view and/or configure the number of threads launched, here is a screenshot of the default setup for my 8-core machine: This VI works by modifying the LabVIEW INI file to include entries that define the number of threads per priority per execution system. You can manually add the entries to the INI file of your EXE. These INI settings are defined here, and this is what they look like: Main TakeawayThe main thing to notice here is that the UI execution system has one thread. Think back to the quote from Aristos Queue above. Certain functions can only execute in the UI execution system's thread when there is no cooperative multitasking taking place, meaning that when these functions execute they block the thread. This can create issues in your software that are difficult to debug. Who would ever correlate a user clicking on a context menu with the Open VI Reference function being blocked? What is the Root Loop?Good question. Here is what I have dug up (I would love feedback on this from NI). The root loop executes within the UI execution system thread and is responsible for driving the windows message loop (see rolfk's posts here, here and here). Quoting the Wikipedia entry for the windows message loop: Microsoft Windows programs are event-based. They act upon messages that the operating system posts to the main thread of the application. These messages are received from the message queue by the application by repeatedly calling the GetMessage (or PeekMessage) function in a section of code called the "event loop." Again quoting from Wikipedia, this time from the entry on the event loop: In computer science, the event loop, message dispatcher,message loop, message pump, or run loop is a programming construct that waits for and dispatches events or messages in a program. It works by making a request to some internal or external "event provider" (which generally blocks the request until an event has arrived), and then it calls the relevant event handler ("dispatches the event"). The event-loop may be used in conjunction with a reactor, if the event provider follows the file interface, which can be selected or 'polled' (the Unix system call, not actual polling). The event loop almost always operates asynchronously with the message originator. The root loop needs to be part of the UI execution system thread because the UI execution system thread is the first thread loaded when Windows creates the LabVIEW process; the 'root loop' is the root loop of the LabVIEW process and communicates with the Windows message loop. SummaryYou may not have known that the Panda Ant existed, or that it could seriously hurt you if it stung you. Most likely the same can be said for the root loop in LabVIEW. The fact that the root loop blocks cooperative multitasking of the UI Thread is not a bug in LabVIEW, but it is something useful to know about. It is also worth knowing that when you read the context help file for a node in LabVIEW and it states, "Must wait until user interface is idle: Yes" that it may require the root loop to execute. Odds are good that you have never run up against the root loop, and if you have it may been minor enough for you not to notice. However, now if you do see it in the wild you will know what it is and what it's doing. Jon McBee is a Principal Software Engineer at Cambridge NanoTech and is a Certified LabVIEW Architect, Certified LabVIEW Embedded Developer, Certified TestStand Developer, an NI Certified Professional Instructor, and a LabVIEW Champion
10 Comments
Peter Duncan
2/11/2015 06:47:23 am
Awesome, thanks for this!
Reply
Roger
2/11/2015 08:48:32 am
Very usefull. Thanks for this great info!
Reply
Omar
2/11/2015 09:16:40 am
Very awesome insights!
Reply
John
2/12/2015 04:31:17 am
Good summary. This has bitten me in the past and continues to be an issue when using VI Server in user-facing applications. Wish I had this information a long time ago when this particualr 'ant' bit me.
Reply
LVRules
2/26/2015 08:57:14 am
Nice blog and article. The one thing i would note about your Async Launch example of the actor core is this method has a known bug in LV2013 so its not recommended for that version. I have moved away from leaking this reference like the actor core b/c it was actually causing more issues than not for what i was doing.
Reply
3/10/2015 03:43:32 pm
Keep up the great articles, Jon! One tip I've learned about the thread pool assigned to each execution system -- even though the `threadconfig.vi` UI maxes out at 20 threads, once the INI keys are created, you can manually edit these values higher if necessary with a text editor. Why would that ever be necessary? One more example that LabVIEW does not cooperatively schedule is Call Library Function Node callsites blocking concurrently within an execution system. We ran across bizarre, intermittent failure modes recently as one execution system started bumping against the 20 thread limit. The temporary fix was bumping the threshold higher in `LabVIEW.ini`. The longer term fix involved moving loops in LabVIEW into the C library as separate threads, using `PostLVUserEvent()` to then communicate back with LabVIEW asynchronously. I would recommend against editing thread pool allocations under normal circumstances, but it helped us in a pinch pending the refactor of the library!
Reply
PBD_ctrl
3/13/2015 03:11:22 pm
Awesome article; what do you think the impact of root loop is on loading classes from file? Do you think this plays a role in LabVIEW IDE slow down when class count reaches over 500?
Reply
Jon McBee
3/13/2015 03:24:06 pm
Thank you. I don't think root loop can be blamed for the IDE slowdown with large numbers of classes in a project, check out this white paper: http://digital.ni.com/public.nsf/allkb/F0FC362A73C794BA86257C6700692B0B
Reply
Purav Soni
9/29/2023 01:27:37 am
I don't know how to say thank you to you this article has help me soo much to understand the thread in LabVIEW.
Reply
Leave a Reply. |
Tags
All
Archives
October 2019
LabVIEW Blogs |