Script interruption is a technology introduced in VBS2 2.x. Its purpose is to ensure scripts cannot decrease the overall performance to the point where it would adversely affect the simulation performed by engine. This introduces certain complexity one has to be aware of when creating scripts.
Scheduled vs. non-scheduled
The scripts most affected by this technology are ones that are time-sensitive – in a scheduled environment you cannot rely on something being executed exactly when you want it to. If you are working on a time-sensitive script it is essential you try to stick to non-scheduled environment as much as possible.
Scheduled environment is subject to script interruption (scheduling). That means your script can be interrupted at any time, either to give the opportunity for another script to run or to allow the engine to perform other necessary tasks. The former case has also the very important implication that an event called “race condition” may occur.
A race condition could occur when two or more scripts are working with one global resource (such as a global variable) at the same time. An example in SQF could be:
// Script 1 _damage = damage player; _damage = _damage * 0.5; player setDamage _damage; // Script 2 player setDamage (damage player + 0.1);
Now, let’s imagine we start both scripts at the same time and player’s damage is 0.1. Since they’re both running in scheduled environment we cannot be certain in what order the commands will be executed. Based on this randomness the following outcomes are possible:
- damage is 0.15 – Script 1 runs first, then Script 2
- damage is 0.15 – Script 1 starts, is interrupted after the first line, Script 2 runs, Script 1 is resumed
- damage is 0.2 – Script 2 runs first, then Script 1
The best practice is to simply avoid such case. If this is not possible at all, you can use the noInterrupt command to act as a mutex as demonstrated in the example below. See below for further explanation of the command.
noInterrupt true; // mutex lock _damage = damage player; _damage = _damage * 0.5; player setDamage _damage; noInterrupt false; // mutex release
Unlike scheduled, non-scheduled environment is not subject to the script interruption at all – your script will run until it has finished, no matter how long it takes. This has two disadvantages – you cannot use suspending commands (sleep, uiSleep and waitUntil) and the engine will stop performing other simulations while the script is being executed. That means complex scripts could very negatively impact performance. On the other hand, non-scheduled environment is ideal for anything time-critical.
Determining which environment your script is running in
There are several rules that determine whether you are working with scheduled or non-scheduled environment
Anything that creates a new “thread” (script) is automatically scheduled, i.e.
Code contained within the following is always non-scheduled:
- unit or vehicle Initialization Statement
- event handler, both configured in config.cpp and added via script (addEventHandler, displayAddEventHandler, ctrlAddEventHandler, etc.)
Furthermore, using the call command will allow you to remain in the non-scheduled environment. Since call can be combined with compile and preprocessFile, this is what you should be using in favor of execVM whenever possible.
player execVM "doSomething.sqf"; // Bad player call compile preprocessFile "doSomething.sqf"; // Good
Too many scripts running
The more scripts/“threads” running at one time, the more time will be taken by the script switching logic and the less time the chosen script has to run before being interrupted again. As a result, this number should be kept as low as possible, using call compile preprocessFile "script.sqf" in place of execVM "script.sqf" as demonstrated above unless it is absolutely necessary to do otherwise. You should limit the number of scripts with infinite loops checking for some conditions and see if a similar logic cannot be performed using an event handler.
The noInterrupt command allows you to temporarily disable script interruption. This command should be used with caution and only when necessary as it will prevent other scripts from running and possibly negatively affect the overall performance of VBS.
The -scriptInterruption startup parameter allows you to change the behavior of script interruption. It is not recommended to disable it permanently but it can be a useful tool when debugging problems with your scripts to narrow down the problem. You can read more about it in the manual.
This section details the actual workings of the script interrupter within the engine. You do not need to understand it and it is here mainly for those who are curious.
All scripts placed in a queue ordered from the least recently executed to the most recently executed script. Every time the limit for processing scripts within the current frame is reached, the execution of the current script is interrupted. Upon entering the new frame a new script to execute is selected based on whether its condition allows it (the script might be sleeping or waiting for a condition). Thanks to the ordering, the least recently run script with a satisfied condition will run. If no script satisfies its condition the whole process is repeated the next frame.