This article details the buffering behaviors LabVIEW uses for network-published shared variables. Refer to Using the LabVIEW Shared Variable for a technical overview of the important features of the LabVIEW Shared Variable.
The paper explores the implementation and behavior of the data buffers that you can enable for network-published Shared Variables, including the behavior of shared variables with multiple readers and writers on a Windows machine, Real-Time target, or VI. This paper also includes examples to illustrate the differences between the different APIs that can access Shared Variables, including the static Shared Variable Node, the DataSocket API, and the programmatic Shared Variable API.
Note: This document focuses on the behavior of Shared Variables in LabVIEW 8.6 and later.
NI-Publish Subscribe Protocol (NI-PSP): NI-PSP is NI's proprietary publish-subscribe protocol (PSP). NI-PSP is composed of a server called the Shared Variable Engine that hosts values, timestamps, and other Shared Variable information. NI-PSP is designed for the use case where many accessors must access or update a latest data value. It is not designed for high data throughput or low-latency. NI-PSP is supported by a number of host servers, including Windows and LabVIEW Real-Time.
Shared Variable Engine (SVE): The SVE is a software framework that enables a networked-published Shared Variable to send values over a network. On Windows, the SVE is a Windows service (tagsrv.exe) that launches at system startup. On a real-time (RT) target, the Shared Variable Engine is an installable component that loads when the RT target boots.
Process (lvlib): A process is a LabVIEW project library that has been deployed to a Shared Variable Engine. All Shared Variables must exist within a project library; therefore, all deployed Shared Variables exist within an online process.
Network-Published Shared Variable with Buffering Enabled: Shared Variables are the objects within the Shared Variable Engine that are composed of a name (URL), data type, value, and other properties. In this document, all instances of the term “Shared Variable” refer to network-published Shared Variables with buffering enabled unless otherwise specified.
Accessor: The readers and writers of Shared Variables, which can be located in VIs, the Distributed System Manager, or elsewhere. Refer to the LabVIEW APIs for Accessing Shared Variables section of this paper for a list of accessors, such as the Static Shared Variable Node and the Programmatic Shared Variable API. A single Shared Variable can be accessed by several computers and RT targets simultaneously. Shared Variable accessors exist outside of the Shared Variable Engine and they make requests to change the value or properties of a Shared Variable or receive published changes from a Shared Variable Engine.
Note: Accessors of a Shared Variable do not actually pass information to one another directly, but instead pass and receive data via a server, the Shared Variable Engine.
Shared Variable accessors come in two forms.
Publisher: A publisher, or writer, can change the current value of a Shared Variable. When a publisher is written to, it has the following interaction with the SVE that is hosting the publisher’s Shared Variable:
1. The publisher requests the SVE to make a value change to the Shared Variable.
2. The SVE checks the publisher’s domain access privileges and allows the publisher to change the value of the Shared Variable.
3. The publisher publishes a new value to the Shared Variable.
4. The Shared Variable Engine notifies all subscribing accessors of the change.
Writing to a publisher does not guarantee an update because the NI-PSP is lossy. Buffering can prevent some data loss, but it does not make the protocol truly lossless.
Subscriber: A subscriber, or reader, can output the latest value of a Shared Variable. A subscriber does not make requests to the SVE to obtain the latest value of a Shared Variable. Instead, the SVE notifies the subscriber when a value, timestamp, status or other aspect of a Shared Variable has changed. A subscriber will always return the most recent value for a variable, if it has no other values in its buffer.
Some accessors can be publishers and subscribers at the same time. The DataSocket and programmatic Shared Variable APIs can have multiple read and write nodes connected with a single reference.
Server side buffer: You configure this buffer in the Network page of the Shared Variable Properties dialog box. Although this buffer resides on the server, the SVE does not create it until the first subscription request from a client is received; therefore, the SVE will not buffer updates when there are no accessors listening. New subscriptions only receive the latest buffered value when connecting even if another subscription has already initialized the buffer. Items are added to this buffer when we publish Shared Variable data from an accessor. The SVE empties the buffer as quickly as possible and is designed to accommodate bursts of writes to prevent data loss. The SVE destroys the server side buffer when the last accessor is unsubscribed.
The server side buffer will always be the same size as the accessor side buffer when using Shared Variable Static Nodes. The same is not true for APIs such as DataSocket and programmatic Shared Variable because we can define the size of the accessor side buffer dynamically.
NI-PSP Client (NPC): The NPC is responsible for maintaining and operating all the Accessor Side buffers and Views for subscribers on a single machine. The NPC also communicates with remote SVEs for publishers on a single machine. On Windows the NPC is installed with the SVE. The same is true in LabVIEW Real-Time (RT) if you install the Network Variable Engine component on an RT target. You also have the option of installing the NPC stand-alone from the SVE with the Variable Client Support for LabVIEW Real-Time component. With this configuration an RT target cannot host Shared Variables, but it can still publish and subscribe to Shared Variables while using less memory and CPU resources on the RT target. The NPC exists for each application instance accessing Shared Variables. Therefore, on Windows it’s possible to have several NPCs operating simultaneously. On RT there is only one application instance; therefore, there is only one instance of the NPC.
Accessor side buffer: This is the buffer responsible for maintaining the queue of values for all accessors of a Shared Variable per application. It is this buffer that insulates your Shared Variable accessors from fluctuations in loop speed or network traffic. The NPC removes values from this buffer when the buffer overflows. Undeploying a Shared Variable does not destroy the accessor side buffer; however, the Shared Variable accessor will return bad quality errors.
Applications that can access a Shared Variable include, LabVIEW, Distributed System Manager, stand-alone Real-Time applications (rtexe), and stand-alone Windows applications.
The accessor side buffer size is a property of the accessor connection and is established when a subscriber comes online. This property is set at edit time for Static Shared Variable nodes in the Shared Variable properties dialog window. For the DataSocket APIs this property is set at run-time via the DataSocket BufferMaxPackets property node. For the programmatic Shared Variable this property is set by the Open and Verify Variable Connection and Open Variable Connection in Background VIs; therefore to change the buffer size at runtime the calling VI must first close and then reopen the connection.
For example, in the diagram bellow, a Shared Variable is hosted on a Windows PC -- running Distributed System manager and a LabVIEW VI -- and two cRIOs, each running stand-alone Real-Time applications. In this case each machine and application is accessing the same Shared Variable. Each of these machines runs an application with one or more subscribing accessors. Here, the Windows machine will have two accessor side buffers (one for LabVIEW and one for Distributed System Manager), and each cRIO will have a single accessor side buffer.
Figure 1: The hierarchy of the Shared Variable Engine, Accessor side buffers, and Shared Variable Accessors accessing a single Shared Variable. Each color represents a single machine with applications accessing the same Shared Variable.
View: A View is sometimes referred to as a Shared Variable read buffer in the LabVIEW help. We will use the term “View” to avoid confusion with the actual buffers in the system. A View is not a programming construct, but rather a concept used to describe Shared Variable behavior. The same physical buffer (accessor side buffer) is used by all accessors where each accessor maintains its own index into the buffer. The behavior of this “index into the buffer” is described here as a View.
A View can be thought of as the first-in, first-out (FIFO) buffer associated with a unique accessor. Each accessor will have its own View, and the items in this View have the same value and relative order as the items in the accessor side buffer. Unlike the accessor side buffer, the NPC removes values from an accessor’s View when the accessor is read as well as when it is overflowed. An accessor’s View cannot be larger than its associated accessor side buffer. A View is destroyed when you undeploy the SV or the accessor leaves memory. If a View contains a single value, this value will be output by the subscribing accessor each time the accessor is read.
Static Shared Variable Node: This is the node that is most commonly thought of when describing a Shared Variable. You can only configure the static node through the LabVIEW project. The static node supports blocking reads. Each instance of a Shared Variable node on a block diagram is an individual accessor regardless of whether these nodes are accessing a single Shared Variable or multiple Shared Variables.
Figure 2: Shared Variable Static Nodes
Programmatic Shared Variable API: This API, introduced in LabVIEW 2009, allows you to read and write Shared Variable and other PSP items and IOVs using a run time API. This method allows you to dynamically determine which Shared Variables to read and write at run time. The Shared Variable Dynamic API does not support accessor side buffering in LabVIEW 2009, but it does in LabVIEW 2010. All programmatic Shared Variable VIs sharing a single reference wire constitute a single accessor.
Figure 3: Programmatic Shared Variable API
DataSocket API: DataSocket is a network API that can access a variety of different network protocols, including NI-PSP. All DataSocket VIs sharing a single reference wire constitute a single accessor.
Figure 4: DataSocket API
DSC Tag API: The Tag API utilizes LabVIEW Object Oriented Programming (LVOOP) and was designed to efficiently access and manage a large number of Shared Variables. The Tag API can access a limited number of data types compared to the programmatic Shared Variable and DataSocket APIs. The Tag API can also read Citadel trace data for a Shared Variable, as well as read and acknowledge Shared Variable alarms. All Tag API VIs sharing a single reference wire constitute a single accessor.
Figure 5: DSC Tag API
DSC Event Structure API: The Event Structure API allows for dynamic user events to be generated when a registered Shared Variable changes values. This API is convenient when automating a user interface. By default, the Event Structure API does not access values within an accessor side buffer. To do this, you must wire a value to the updates to buffer (no buffer) input on the Request Value Change Notifications VI. The Event Structure API is only available when you have the LabVIEW DSC module. When you use the Event structure to access Shared Variable data, accessors are created when user events are registered with the shared variable value change notification refnum from the Request Value Changes VI.
Figure 6: DSC Event Structure API
Accessor side buffers exist on a per-application basis, and they are created at different times depending on the API you use to access the Shared Variable. The static Shared Variable node will create an accessor side buffer immediately when a VI or stand-alone application containing the static node runs. The accessor side buffer for a Shared Variable accessed through the programmatic Shared Variable, DataSocket, and Tag APIs will not be created until you explicitly or implicitly open a connection to the Shared Variable.
Each Shared Variable accessed by any particular application will have its own accessor side buffer. For example, if a user runs a VI subscribing to two Shared Variables, Variable_1 and Variable_2, each of these Shared Variables will have their own accessor side buffer. These accessor side buffers will have no interaction with one another.
When a Shared Variable is deployed, it does not have a value associated with it. In Distributed System Manager, the value of a Shared Variable in this state we would be listed as “No Known Value.” LabVIEW gives a warning and returns the default value for the variable’s datatype. If an accessor comes online and an accessor side buffer is created, the accessor side buffer will contain no values (e.g. it has a value of NULL).
Figure 7: The Behavior of the accessor side as it comes online.
If a Shared Variable already has a value associated with it and a new accessor comes online, the accessor side buffer will contain the Shared Variable’s current value.
Figure 8: The behavior of an accessor side buffer on multiple machines when there are already values in the buffer.
When you undeploy a Shared Variable, the NPC deploys all accessor side buffers. If that Shared Variable is redeployed, any accessor side buffers that come online will have a value of NULL. Variables are not automatically undeployed when a VI stops running. You can undeploy Shared Variables manually using the LabVIEW project and Distributed System Manager, or programmatically using the VIs in the DSC Engine Control » Libraries and Processes palette.
Views share some properties with accessor side buffers, but there a few critical differences. If an accessor side buffer has just come online and its value is NULL, the accessor’s View will have the default value for that Shared Variable data type. If the NPC adds items to an accessor's View, the default value will become the first element in the accessor’s View. If an accessor comes online when there are already values within the accessor side buffer, the accessor’s View will have the last value written to the Shared Variable.
Figure 9: Values within an accessor side buffer and single accessor's view accessing one Shared Variable.
Views are destroyed when the accessor leaves memory. Accessors can leave memory either by the calling VI exiting memory or by calling a close function such as DataSocket Close or Close Variable Connection. Static Shared Variable node accessors leave memory when the calling VI leaves memory.
Like accessor side buffers, values can be removed from a View by overflowing the buffer. You can overflow an accessor side buffer without overflowing a View. In the same vein, you can overflow an accessor side buffer and one accessor’s view without overflowing the view of another accessor who is subscribed to the same Shared Variable. This can happen when each accessor comes online at different times.
Unlike accessor side buffers, you can also remove values from an accessor’s View by reading from that accessor, but there must always be one value that remains within an accessor’s view. In this case, the same value can be read multiple times from an accessor. This last value will be immediately removed once an accessor has written a new value to the Shared Variable. The only way you can completely remove all values from an accessor’s View is by undeploying and redeploying the Shared Variable to which the accessor is subscribed.
All accessor Views obey the following set of rules:
Shared Variables with buffering enabled can return several warnings that are specific to the Server and Accessor Side buffers. In most situations where a warning is returned from a Shared Variable reader, the warning is describing the current condition of that accessor’s View rather than the Accessor side buffer shared by multiple accessors accessing a single variable. These warnings will only occur when using the Static Shared Variable node or the Programmatic Shared Variable API. The DataSocket, Tag, and Event Structure APIs will not return these warnings.
Warnings with Shared Variable Static Nodes and Shared Variable Programmatic API (2010 or later)
180121602: The read buffer for the variable read is empty. The last known value was returned.
This warning occurs when the View of an accessor is empty. As stated in the warning description, the last value written to the Shared Variable will be output each time you access the accessor. This warning will be produced until another error or warning is encountered or an accessor writes to the Shared Variable.
-1950678979: The shared variable server-side write buffer overflowed.
This warning occurs when a publisher writes too many new values to the SVE in a short period of time and overflows the buffer. This is not a common warning because the Shared Variable Engine can process updates quickly.
-1950678980: The shared variable server-side write buffer is full.
This warning occurs when a publisher writes too many values to the Shared Variable Engine and fills the server-side buffer. This is not a common warning because the Shared Variable Engine can process updates quickly.
-1950678981: The shared variable client-side read buffer overflowed.
This warning occurs at a Shared Variable read node when the accessor side buffer has been overflowed. This warning displays until another error or warning is encountered or an accessor writes to the Shared Variable.
-1950678982: The shared variable client-side read buffer is full.
This warning occurs at a Shared Variable read node when the accessor side buffer has been filled. This warning displays until another error or warning is encountered or an accessor writes to the Shared Variable.
Errors with Shared Variable Static Nodes and Shared Variable Programmatic API (2010 or later)
The shared variable API and static shared variable nodes also can return several errors more generally related to shared variables or the shared variable engine.
-1950678945: The host name specified in the variable identifier URL does not correspond to the local host, but the specified variable engine URL only supports an operation on the local host. You cannot perform the specified operation on a remotely hosted variable or stream endpoint. Use the PSP Variable engine URL (ni.var.psp) to read or write a network-published I/O variable or I/O alias remotely.
This error occurs when a shared variable operation only permitted on a variable hosted locally is attempting to act on a
remotely hosted variable.
-1950679023: The process specified in the binding URL does not exist. Ensure that all shared variables are deployed and the variable identifier URL is correct.
When specifying an identifier URL as "ni.var.psp:\\hostname\process\variable" the shared variable engine could not find the
process specified.
-1950679035: Unable to locate the shared variable in the Shared Variable Engine (SVE). Deployment of this shared variable may have failed, the SVE has not started or the SVE is too busy to respond to this request.
This error can occur for a variety of reasons. The most common causes are the SVE service being disabled or not running
and the shared variable not being present in the SVE. Identify the cause and start the SVE engine service or deploy the
variable if necessary.
-1950679037: The shared variable specified in the binding URL does not exist in the process. Ensure that all shared variables are deployed and the variable identifier URL is correct.
When specifying an identifier URL as "ni.var.psp:\\hostname\process\variable" the shared variable engine could not find the
variable specified.
-1967361997: The data type of the shared variable does not support the property value you specified.
This error occurs when a shared variable property has been given a value that isn't acceptable for that data type.
-2147024891: Access denied. This error can occur if you attempt to write a single writer shared variable that is already connected to a subscriber with write access. Ensure that you connect only one target with write access to this shared Variable at a time.
Shared variables with the single writer property set to true will produce this error when more than one writer attempts to
access the variable simultaneously.
Each of the following three examples accesses the same Shared Variable (NTBuf). This Int32 Shared Variable has a buffer of 10 elements. Each example undeploys and redeploys the variable between runs of the VI to ensure all accessor side buffers have a value of NULL and the Views have a value of 0. The values shown for each view and buffer in each step are after the For loop in each step has finished executing. These examples may seem complicated, but they do not represent typical usage or the feature. Instead, they were designed to demonstrate Shared Variable buffering behavior.
Example 1
In this example, there are 4 static nodes (two writers, two readers) for a total of 4 accessors. Because there are only two readers, you only have two accessor Views, but all accessors share the same accessor side buffer.
1. NTBuf is deployed (not shown on the block diagram)
2. Write to NTBuf 5 times
3. Read from Accessor A 10 times
4. Write to NFBuf 5 times
5. Read From Accessor B 10 times
Example 2
In this example, you have a single DataSocket Open VI and all readers and writers share the same reference wire. Therefore, there is one accessor with one view. The DataSocket API requires you to explicitly define NTBuf’s buffer size at runtime using the BufferMaxPackets property node.
1. NTBuf is deployed (not shown on the block diagram)
2. DataSocket Open Accessor A
3. Write to NTBuf 5 times
4. Read from Accessor A 10 times
5. Write to NTBuf 5 times
6. Read from Accessor A 10 times
Example 3
In this example, you have two DataSocket Open VIs and different groups of readers, and writers are connected with the two reference wires. Therefore, you have two accessors, each with their own View, which share a single accessor side buffer.
1. NTBuf is deployed (not shown on the block diagram)
2. DataSocket Open Accessor A
3. Write 5 times to NTBuf @ Accessor A
4. DataSocket Open Accessor B
5. Write 5 times to NTBuf @ Accessor B
6. Read from Accessor A 10 times
7. Write 5 times to NTBuf @ Accessor B
8. Read From Accessor A 10 times
9. Read from Accessor B 10 times
10. Write 5 times to NTBuf @ Accessor B
11. Read From Accessor B 10 times
Shared Variables are designed for distributed applications where networked data must be shared among multiple machines. Due to the architecture of NI-PSP and its use of a Shared Variable Engine as an intermediary between all publishers and subscribers, Shared Variables cannot guarantee high throughput or low latency. If low latency or high throughput is a requirement of your application, consider using TCP, UDP, or Network Streams as an alternative to Shared Variables. In all applications, especially those accessing a large number of Shared Variables, update rates should be as low as possible.
Due to the number of different APIs available in LabVIEW for accessing Shared Variables, it is important to create the communication layer of your application using only one API. This will allow you to architect your application with the behavior of that specific API in mind. This will produce cleaner code and improve your ability to debug your code.
Static Shared Variable Node: Use this API when quickly prototyping code that requires network communication. This API allows you to quickly and easily get communication up and running with minimal effort. The Static Shared Variable Node should only be used in finished or deployed applications when the number of Shared Variables used is very small and scalable, or when reusable code is not a critical feature of the application. Because Static Shared Variable Nodes can execute without a dataflow dependency, there is an increased possibility of race conditions. It is good programming practice to always use error wires to force order of execution. This practice encourages you to handle Shared Variable errors and warnings and makes for more readable code. Running error wires will also make your code run faster because the LabVIEW compiler optimizations takes advantage of serialized code.
Programmatic Shared Variable API: NI recommends this API for most applications that use Shared Variable communication. This API requires you to enforce dataflow and allow for the Shared Variable accessed by the API to be dynamically determined at run time. This API can also be used in LabVIEW VIs that do not exist in the same project as the Shared Variables it is accessing.
DataSocket API: For all new and future development, NI recommends using the Programmatic Shared Variable API instead of the DataSocket API. For PSP URLs, there is feature parity between these two APIs. The programmatic Shared Variable API also has better performance because it does not run in the LabVIEW UI thread.
DSC Tag API: NI recommends this API for developers who are designing applications that must access a very large number of variables in highly scalable and reusable code. This API includes VIs that integrate with other DSC features such as Shared Variable alarms and events and historical Citadel trace data. The Tag API also includes additional tools for dynamically determining and changing the Shared Variables accessed by the API. While this is also possible with the programmatic Shared Variable API, the high level tools included in the Tag API would have to be created from scratch. This API does not make use of Shared Variable buffering.
DSC Event Structure API: NI recommends this API for applications that require event driving programming based upon Shared Variable value and status changes, as well as Shared Variable alarms and events.
Network Buffering is a powerful feature that decreases the likelihood of Shared Variable data loss in applications with inconsistent read and writes rates. Enabling this feature requires more CPU and memory resources. Network Buffering can also lead to unexpected race conditions between Shared Variable accessors, as shown in the examples in this document. You must also handle additional errors and warnings that only occur when Shared Variable buffering is enabled. For these reasons, Network Buffering should only be enabled in instances when it is deemed absolutely essential for an application and the developer can ensure race conditions are avoided.
Overuse and misuse of Shared Variables in a LabVIEW Real-Time application can result in poor machine performance and, in some cases, crashes. Typical misuses include using too many Shared Variables or attempting to push too much data through a single Shared Variable. In applications deployed to small Real-Time targets such as Compact RIO or Compact FieldPoint, no more than 25 variables should be used. In applications deployed to large Real-Time targets such as PXI or Industrial Controllers, no more than 150 Shared Variable should be used. In all cases, NI recommends that you keep Shared Variable update rates as low as possible and host the Shared Variable remotely on a Windows PC whenever possible.