Common LUA Globals

Service Global Variables

The following true-global and thread-private global variables are pre-loaded for a Lua Service state. All of these variables are visible in the Lua _ENV environment which contains “free scope” (non-local) variable names.

Further below is discussion of the difference between true-global and thread-private global variables.

First, here are the true-globals (as per the following discussion. Their value is shared across all threads using that Lua state. Modifying them within one thread will affect the value within other threads.

Variable Type Description
SCRIPT_KEY String The key used by the loader to identify which LUA script content to execute.
SCRIPT_MTIME Number The modification time (epoch) of the script which was loaded to return this LUA.
APPLICATION_IDX Integer The unique index within n2svcd of the n2svcd application which is executing this LUA.
APPLICATION_NAME String The name of the n2svcd application which is executing this LUA.
APPLICATION_PID Integer The process ID of the n2svcd application which is executing this LUA.
SERVICE_MODULE String The class of the LuaService module which initiated this LUA.
LOADER_MODULE String The class of the LuaLoader module which loaded this LUA.
REQUIRE_LIST List of Table The list of internally-satisfiable require modules given to the LuaLoader to be used in conjunction with this script name. Most Loaders will not provide any require modules, in which case this global variable will be nil.
[] Table Anonymous table.
.name String The module name which will be internally satisfied by a require.
.lua_chunk String The LUA content (compiled or non-compiled) which satisfies the require.
.version Integer An associated version number provided by the loader logic.

Additionally the following thread-private globals are pre-defined for each thread.

Variable Type Description
INSTANCE_IDX Hex String The instance_idx of the LuaInstance currently executing this LUA.
TRACE_LEVEL 0 - 3 An integer value indicating if trace-level debugging is currently enabled.
0 = none, 1 = DEBUG, 2 = DUMP, 3 = SPAM.
This can be used to avoid needless computation overhead when tracing is not activated.
CHAIN_COUNT Integer The number of times that this processing has chained to a follow-on script.
The initial value for this counter is 0.

In addition, each definition may extend these by adding service-specific true-global and/or thread-private Lua values in the environment of every Lua thread/state initiated by that service.

See the Lua Services configuration within the LogicApp.

Notes on Global Variables

As described above, there is a subtle but important distinction between “true-global” and “thread-private global” when running Lua scripts inside LogicApp.

In many cases this distinction will not be visible to the service script writer. However, there are specific situations where this distinction can become very important, specifically in relation to the use of top-level variables inside a chunk loaded using require.

The remainder of this page describes when and how this distinction becomes important.

Shared State & Script Key

The distinction between true-global and thread-private global becomes relevant when the same “script key” is loaded more than once within a single LogicApp process. In this case the LogicApp will create a single Lua state (with a single Lua memory space) and re-use that state multiple times by creating individual execution threads within that single state/memory space.

This approach provides very significant efficiency gains, since it avoids the CPU and memory overhead of re-compiling and loading the Lua state into memory repeatedly.

In practice, this approach is relevant to nearly all services, as nearly all Lua-based services are based around a small number of common script keys, including:

The exceptions will be:

Internal Implementation

The Lua framework uses a shared-state mechanism to reduce memory usage in applications where multiple services load the same script_key.

In Lua terms, each instance of the running script is a Lua “thread”, which is a separate stack within a common, shared Lua state. Each of these threads shares a single Lua memory space.

However, in general these threads will not be aware of each other’s existence because most variables will not be shared.

Specifically:

In detail: Whenever each thread starts/resumes execution, the standard _G global variables table is adjusted so that it is backed by a “thread-private global variables table”.

Note: See the caveat relating to top-level local variables inside modules loaded using require.

Relationship with _ENV

A “free” variable is a variable which is not defined as a local variable, or is not defined as a function-owned upvalue by any other mechanism e.g. the C Lua API.

Any free (non-local) variable named is strictly-speaking a syntactic equivalent for _ENV.<varname> where _ENV is the function’s “environment” table. For scripts running within LogicApp, the _ENV has its default value which is _ENV = _G.

To reiterate. The LogicApp does not modify _ENV when executing threads. The LogicApp modifies _G directly which affects free (non-scoped) variables because Lua by default sets its environment _ENV to be = _G.

True Globals

The real _G table contains the truly-shared global values, this contains:

Thread-Private Globals

The thread-private globals are stored in a separate table which at thread-runtime is attached to the _G table. When executing the thread, the following changes are made to _G prior to resuming the script execution.

This means that for getting and setting free-scope (non-local) variables in Lua scripts run within LogicApp:

  1. When used as an r-value (getting) if the variable exists in _G then the true-global value is got.
  2. When used as an r-value (getting) if the variable does not exist in _G then the thread-private global value is got.
  3. When used as an l-value (setting) if the variable exists in _G then the true-global value is set.
  4. When used as an l-value (setting) if the variable does not exist in _G then the thread-private global value is set.

Effectively, unless your free-variable name already exists as a key in the true-global table, then the thread-private global table is used for reading and writing variables.

The following thread-private global variables are defined prior to starting each thread.

Direct Access to True Globals

To modify an existing true-global value, it is sufficient just to assign the new value.

To create a new true-global value, use the Lua rawset method which will bypass the metatable mechanism.

-- Set BSTATE = 'stateA' as a true-global value visable across all threads.
rawset (_G, 'BSTATE', 'stateA')

Note that because the LogicApp will periodically create a new Lua state. A global variable change will be seen by all other threads sharing your Lua state. But there will be more than one Lua state even for a single script key.

The Lua rawget function also exists although will not typically be useful.

Direct Access to Thread-Private Globals

To access the underlying thread-private globals table, use the n2svcd.thread_globals method.

local tgvars = n2svcd.thread_globals ()

Internally this method accesses the __index metatable value for _G.

Iterating _G with Pairs

The pairs iterator on the _G table has been modified so that it will:

Variables Inside “require”

There is some non-intuitive behavior in regards to top-level variables inside Lua chunks which are loaded with a call to require. This affects top-level variables only, not variables which are inside functions defined within that require chunk.

This is because the chunk loaded by require is loaded only once, and so the “closure” is created only once. Any local variable is part of that singleton closure and hence is common to all threads which share that Lua state.

This is because the chunk loaded by require is loaded only once and hence the assignment statement setting that variable is executed only once. The first thread to load that require will execute the assignment, and the variable will be placed in that thread’s thread-private global table.

Any subsequent thread will receive a copy of the require object but will not re-execute the code contained in the required chunk. Hence it will not have any entry in its thread-private global table - i.e. the free variable does not exist.