Pre-processor keywords

Tasks can be configured by specifying keywords (prefixed with "--#") as part of their script. These keywords are extracted by a pre-processor from the header of the script when it is loaded. Some keywords must be followed by values enclosed in double quotes. This is Lua for LabVIEW specific functionality: Lua considers anything following a "--" a comment and ignores it. Making the configuration and implementation part of the same script file keeps everything task-related in a single place. Occasionally, it is desirable to run tasks with the same implementation but different configurations. In such a case, put the implementation in a separate script and import it from scripts containing only pre-processor keywords that specify the various configurations.

The script header includes all lines up to the first line that is neither empty nor starts with "--". This implies that the header runs up to the first line of code or up to the first multi-line comment. Keywords that occur later in the script are ignored. This is useful because it is possible for string literals in a script to contain a further task script. Had keyword extraction not been limited to the header, any keywords of such an embedded script would confuse the configuration of the parent task.

All keywords are optional. When omitted, default values are used. When keywords are specified in an import script, all but the import, version, and description keywords are ignored. This allows an import script to do double duty as a main task script. Any keyword arguments should be treated as case dependent. This also holds for the paths of imported scripts so that compatibility with case dependent file systems is retained and importing also works under, say, Linux. The following keywords can be used to configure a task:

--#priority "<priority>"

Sets the execution priority of the task. This keyword is ignored when swapping a task. The possible choices are identical to the LabVIEW VI priority settings with the proviso that "subroutine priority" is not applicable:

Which of these can actually be used depends on the configured task alternatives. The default priority is also configurable. Configuration is required because LabVIEW 7.0 does not allow setting the priority or execution system in a built application. To provide a way around this, Lua for LabVIEW allows a selection of tasks, each with a particular combination of priority and preferred execution system to be configured. When no alternatives are configured, it is only possible to start tasks at "normal priority" in the "standard" execution system.

Be sure to read the LabVIEW documentation on multi threading, priorities, and execution systems before using these techniques with tasks. Beware that scheduling does not always behave as one might reasonably expect. To gain confidence, perform latency and performance measurements. Beware that frequent context switches between execution systems can introduce overhead that does not occur when scheduling remains within an execution system. When the system load is light and no protracted operations occur once your application is initialised, there is no need to resort to priorities and execution systems.

--#prefsys "<execution system>"

Sets the preferred execution system of the task. This keyword is ignored when swapping a task. The possible choices are identical to the LabVIEW VI execution system settings with the proviso that "same as caller" is not applicable:

Which of these can actually be used depends on the configured task alternatives. The default execution system is also configurable. See the discussion of the priority keyword for details.

--#identifier "<identifier>"

Allows a specific identifier to be assigned to a task. Lua for LabVIEW ensures that tasks have a unique identifier by preventing the starting of tasks with the same identifier as an already existing task. This keyword is ignored when swapping a task. When you omit this keyword, a unique number is auto-assigned as identifier instead. To prevent clashes with such numbers, it is not allowed to specify an identifier that consists only of decimal digits.

Since only one task with a particular identifier can be running at any one time, a specific identifier can be used to prevent multiple tasks that use or manage some exclusive resource from being started. Alternatively, an identifier makes it possible to signal or message a task providing a particular service. Preferably pick an identifier that matches the resource being protected or service being provided. When swapping a task with a specific identifier, the script of the replacement task should specify the same identifier.

--#class "<class name>"

Optionally a task can be assigned a class, which is some arbitrary string that can be used to group multiple tasks that are similar in function or utility. The class is shown in the "Task Manager". Future versions of the "Task Manager" may use this keyword to organize tasks in a tree view.

Task scripts of the same class are likely to also be grouped together in a directory. More likely than not this directory will have the same name as the class name you would like to be set. To set the class name to the name of the directory containing the task script, use "<dir>" as the argument.

--#handler "<identifier of handler task>"

When tasks are run, they detach themselves from the launching context after leaving initializing mode. Any errors that occur after this happens have no place to go. Normally, a task goes into error mode so that such errors can be reviewed and confirmed via the "Task Manager". This keyword allows you to override this behaviour and instead handle the errors programmatically. To do so, specify the identifier of a handler task as a parameter to this keyword. That task should be set up to receive the error messages via the task messaging mechanism. Once the error message has been sent to the handler, the failed task will remove itself from the task list instead of staying stuck in error mode. The keyword is ignored for synchronous tasks (tasks started via the Do Task API VI or do_task Lua for LabVIEW function) since these pass any error back to the launching context.

A handler task will typically take some corrective or emergency action, and in addition will likely want to log the error somewhere. When the action performed by the handler is noteworthy, consider prepending a notice to the error message before logging it. That way users will be notified of the corrective or emergency action taken. The task script below shows how to implement a simple handler that merely prints the error messages it receives.

--#identifier "myhandler"
while true do
    while msg.available() do
        print(msg.next())
    end
    sleep()
end

By implementing multiple handlers you can provide a selection of actions to be taken when a task fails. When the specified handler task does not exist, the pre-processor will throw an error. When the specified handler no longer exists once the task throws an error, the task will revert to the default behaviour of going into error mode. Note that specifying a handler for a task will cause it to ignore the global "task error logger" configuration parameter.

--#hide

Hide the task's scripts from the Task Manager GUI. This makes it impossible to use the "retrieve" button in the "script(s)" tab. This can be useful when the task script or one of its import scripts contains sensitive information such as a password. Note that this only provides protection when the task and the Task Manager run in different runtimes on different accounts or machines, as is the case for a client-server deployment.

--#defer

Defer initialisation. By default, a task leaves initializing or swapping mode when its main script is executed (which occurs after execution of any import scripts). When this keyword is used, the task does not automatically switch modes but instead waits until you call the task.active function. This allows the task script to perform deferred initialisation actions: operations that must complete successfully before the task is considered to have been successfully run. Alternatively, it allows a task script to perform initialisation actions that depend on whether it is being swapped or started normally.

The Run Task API VI and run_task Lua for LabVIEW function return without error only after the task being run switches from initializing to running mode. An error is returned when loading fails, any of the scripts fails to pre-process or compile, or an error occurs when running one of the import scripts: the error is propagated from the task's context to the launching context. When the defer keyword is specified, an error is also returned when the main task script (or any function called from there) throws an error at any time prior to calling task.active. When an error is returned to the launching context, the task will be immediately removed from the task list. Only errors that occur after the task has switched to running mode cause the task to go into error mode and remain listed pending confirmation of the error.

One use is preventing the Lua or LabVIEW code that runs the task from proceeding before requisite initialisation actions are complete or subsequently required resources are allocated. Another use is to perform error-prone initialisation as deferred initialisation so that errors that are likely to occur can be managed programmatically instead of manually through operator confirmation.

--#disallow_manual

Prevents manual signals. When this keyword is specified, the task cannot be sent signals via the "Task Manager" user interface with the exception of the confirm signal that can be sent when the task has thrown an error. This is useful when a task is being started and stopped programmatically and relied upon to be running without interruption, for example tasks that provide some background service or tasks that perform system initialisation and cleanup. It prevents the operator from accidentally stopping or otherwise interrupting the task. This keyword does not disallow programmatic signalling as done via the provided Lua for LabVIEW signalling functions or the LabVIEW task API.

--#import "<script path>"

The main task script can import additional scripts using this keyword. Imported scripts can in turn also use this keyword to import further scripts. Imported scripts are executed before the scripts that import them. This ensures that the functionality provided by the imported scripts is initialised. Lua-implemented function libraries or Lua-table based configuration settings are typical examples of possible content of import scripts. The import script is specified via a relative or absolute path. When a relative path is specified, the directory holding the already loaded task script and import scripts (including the script for which the importing is done) as well as the various script search directories will be pre-pended in an attempt to locate the script. Try to use unique script names to prevent this multi-directory search from loading the wrong script. As is the case for path strings passed from Lua to LabVIEW, any occurrences of both "\" and "/" are converted to the platform specific path separator unless otherwise configured via lv.substitute_separators. As opposed to quoted strings in normal Lua code, a backslash is not interpreted as an escape character and thus need not be escaped.

The result of processing all import keywords is a tree/hierarchy of loaded scripts with the main task script at its root. The depth-first execution order implies that the main task script is executed last. Preferably, the import scripts should only contain definitions (of functions or variables) and occasionally some initialisation code. This ensures that the main task script is a proper reflection of the processing that will be done when the task runs. When a particular script is imported twice (e.g. because its functionality is required by both the main task script and one of the import scripts specified in the main task script) it will only be loaded once and executed once. The imported scripts and main scripts are compiled in the launching context and subsequently executed in the task's context. This ensures that any compile errors occur before the task is instantiated and its execution commences.

To discourage implementing protracted operations as part of the initial execution of import scripts and ensure that any compile errors are caught on starting a task, tasks by default do not leave initializing mode before execution of the main script commences unless overridden via the defer keyword. The main script is the last script in the execution order. Import scripts that perform some protracted action therefore block the context the task is being run from. This blocking can be prevented by calling task.active from within the import script so as to allow the launching context to commence. Preferably do not use this method as it makes the task behave in a manner that is not be readily visible in the main task script. It is better form to provide such protracted operations as a Lua function that must be called explicitly from the main script.

--#version "<version>"

A free format version string. This keyword is extracted for import scripts as well. This can be used to document the script. The version strings of the script(s) making up a task can be accessed programmatically.

--#description "<description>"

Allows a brief description of the task's function or utility to be specified for display purposes. This keyword is extracted for import scripts as well. The description of the main task script is shown in the "Task Manager". The descriptions of the script(s) making up a task can be accessed programmatically.


Go to table of contents