Scheduler: Difference between revisions

From Bohemia Interactive Community
Jump to navigation Jump to search
m (→‎Scheduled Environment: internal link corrected)
m (Text replacement - "[[Script (Handle)|" to "[[Script Handle|")
(21 intermediate revisions by 5 users not shown)
Line 1: Line 1:
The scheduler is the part of the game engine that decides which thread runs at a certain point in time. It has the ability to pause a running thread, move it to the back of the running queue and start a new thread.
{{TOC|side}}
The [[Scheduler]] is the part of the game engine that decides which script runs at a certain point in time. It has the ability to pause a running script, move it to the back of the running queue and start a new script.


== Threads ==


A new thread can be started in a script by using [[spawn]], [[execVM]] and [[exec]] commands. A script or function started this way will run in a scheduled environment, where as a function which is started via [[call]] will retain the scheduling from the environment it was called from e.g a [[call]] from scheduled code will remain scheduled, a call from unscheduled code will remain unscheduled.
== Scripts ==
 
A new script can be started by using [[spawn]], [[execVM]], [[exec]] and [[execFSM]] commands. The script started this way will be added into the script scheduler and will be executed in turns, giving priority to the scripts waiting longest since their last suspension. Completed scripts are removed from the scheduler while scripts not completed in allocated time are suspended.
 


== Scheduled Environment ==
== Scheduled Environment ==


Running code in a scheduled environment starts a new thread. The executing instance will not wait for the result of scheduled code and will continue on with its execution, so it is not possible to return any values from code executed in this manner although a [[Script (Handle)|Script handle]] for started thread is provided.
Running code in a scheduled environment starts a new script. The executing instance will not wait for the result of scheduled code and will continue on with its execution, so it is not possible to return any values from code executed in this manner although a [[Script Handle|Script handle]] for started script is provided.


As described below there is no way to predict when scheduled code will have finished, although you can use the command [[scriptDone]] with the provided [[Script (Handle)|Script handle]] to query if the thread execution has finished and there is also the command [[terminate]] to abort the thread.
As described below there is no way to predict when scheduled code will have finished, although you can use the command [[scriptDone]] with the provided [[Script Handle|Script handle]] to query if the script execution has finished and there is also the command [[terminate]] to abort the script.


The advantage of scheduled code is that it runs asynchonously in the background and that you are able to run code with a huge performance impact (nearly) without slowing down the game.
Because the scheduled code can run only for a fixed duration, heavy scripts spread their load over time, and thus are having lesser impact on the game performance.


All scheduled scripts can run for a maximum of 3ms in a frame. Each Script that runs add's up to the total runtime and as soon as the total runtime of 3ms is reached the current script is paused and the game continues calculating everything else it needs to calculate that frame (For example sound and graphics rendering). On the next frame the scheduler again starts to run scripts for 3ms, starting with the script which hasn't been executed for the longest time.
'''All scheduled scripts can run for a maximum of 3ms in a frame'''. (Except if inside loadingscreen where it is 50ms per frame) <br>
Each Script that runs add's up to the total runtime and as soon as the total runtime of 3ms is reached the current script is paused and the game continues calculating everything else it needs to calculate that frame (For example sound and graphics rendering). On the next frame the scheduler again starts to run scripts for 3ms, starting with the script which has not been executed for the longest time.


This means any script that ran as the 3ms runtime was reached will be paused in the middle of it's execution and depending on how many scripts are spawned it might take several frames till it will run again.
This means any script that ran as the 3ms runtime was reached will be paused in the middle of it is execution and depending on how many scripts are spawned it might take several frames till it will run again.
A while true loop with sleep started in scheduled environment therefore has little chance to follow with exact interval, because [[sleep]] only marks the script as done in the current frame and the next time the script is executed the engine will check if the sleep is over.
A while true loop with sleep started in scheduled environment therefore has little chance to follow with exact interval, because [[sleep]] only marks the script as done in the current frame and the next time the script is executed the engine will check if the sleep is over.
This means at 20 FPS the time between one frame and the next is roughly 50ms. That would make a sleep 0.01 wait for atleast 0.05 seconds.This effect get's bigger when the fps get even lower and if the scheduler is so overfilled that your script only get's checked every few frames instead of every frame.
This means at 20 FPS the time between one frame and the next is roughly 50ms. That would make a sleep 0.01 wait for atleast 0.05 seconds.This effect get's bigger when the fps get even lower and if the scheduler is so overfilled that your script only get's checked every few frames instead of every frame.
Line 21: Line 25:
If you spawn a scheduled script it will only start to run in the next frame when the scheduler starts fresh again.
If you spawn a scheduled script it will only start to run in the next frame when the scheduler starts fresh again.


== Where code starts scheduled ==
=== Where code starts scheduled ===
 
* init.sqf
* initServer.sqf
* initPlayerLocal.sqf
* initPlayerServer.sqf
* [[Arma 3: Functions Library#Pre and Post Init | functions with postInit attribute]] (although suspension is allowed, any long term suspension will halt the mission loading until suspension has finished)
* code executed with [[spawn]]
* code executed with [[execVM]]
* code executed with [[exec]]
* code executed with [[call]] from a scheduled environment
 
=== Scheduler diagnostics commands ===
 
The following commands are available:
* [[diag_activeSQSScripts]] - for all [[exec]]ed scripts in the scheduler 
* [[diag_activeSQFScripts]] - for all [[execVM]]ed and [[spawn]]ed scripts in the scheduler
* [[diag_activeMissionFSMs]] - for all [[execFSM]]ed scripts in the scheduler
* [[diag_activeScripts]] - for all scripts in the scheduler


*init.sqf
*initServer.sqf
*initPlayerLocal.sqf
*initPlayerServer.sqf
*[[Functions_Library_(Arma_3)#Pre_and_Post_Init | functions with postInit attribute]] (although suspension is allowed any long term suspension will halt the mission loading until suspension has finished)
*code execution with  [[spawn]]
*code execution with [[execVM]]
*code execution with [[exec]]
*code execution with [[call]] from a scheduled environment


== Unscheduled Environment ==
== Unscheduled Environment ==
Line 38: Line 51:
The unscheduled environment runs (as described above) in the executing instance. The executing instance waits until the called function is finished. This ensures the execution order and is the fastest way for scripters to execute their code. The disadvantage is that a called function can halt or slow down the game if the function has a high performance consumption. Therefore those functions should be spawned and thereby run in a scheduled environment.
The unscheduled environment runs (as described above) in the executing instance. The executing instance waits until the called function is finished. This ensures the execution order and is the fastest way for scripters to execute their code. The disadvantage is that a called function can halt or slow down the game if the function has a high performance consumption. Therefore those functions should be spawned and thereby run in a scheduled environment.


== Where code starts unscheduled ==
=== Where code starts unscheduled ===


*triggers
* [[Arma 3 Debug Console|Debug Console]]
*[[Functions_Library_(Arma_3)#Pre_and_Post_Init | functions with preInit attribute]]
* [[Eden Editor: Trigger|Triggers]]
*FSM-conditions
* [[Waypoints]] (condition ''and'' activation)
*Event handlers (on units and in GUI)
* All pre-init code executions including [[Arma 3: Functions Library#Pre and Post Init|functions with preInit attribute]]
*object Init Event Handlers  
* [[FSM]] conditions
*object initialization fields
* [[:Category:Event Handlers|Event Handlers]] on units and in GUI
*all pre-init code executions
* ''EachFrame'' code ([[Arma 3: Mission Event Handlers#EachFrame|Event Handler]] / [[BIS_fnc_addStackedEventHandler|Scripted EH]] / [[onEachFrame]])
*expressions of Eden Editor entity attributes
* Object initialization fields
*code execution with [[call]] from a unscheduled environment
* Expressions of Eden Editor entity/mission attributes
*code inside [[isNil]]
* Code execution with [[call]] from an unscheduled environment
*sqf code which is called from sqs-code
* Code executed with [[remoteExecCall]]
* Code inside [[isNil]]
* [[SQF Syntax|SQF]] code called from [[SQS Syntax|SQS]] code
* [[Conversations#Conversation Event Handler|Conversation Event Handler]]
* Code inside [[collect3DENHistory]]


== Loops ==
=== while Loops ===
 
A [[while]]-[[do]] loop is limited to (hard-coded) '''10,000''' iterations in a non-scheduled environment.


A while do loop will be limited to 10,000 iteration in non-scheduled environment. In scheduled environment such limit does not apply.


== Suspension ==
== Suspension ==


Suspension is the process to wait a period of time or to wait for something to happen. Methods for suspension are [[sleep]], [[uiSleep]], [[waitUntil]] and all other kind of loops.
Suspension is the process to wait a period of time or to wait for something to happen. Commands for suspension are [[sleep]], [[uiSleep]], [[waitUntil]].
Suspension with sleep and uiSleep is generally forbidden in an unscheduled environment. Loops will run for 10,000 iterations before breaking the loop and continuing with the following code.
 
If you want to use suspension in your script then ensure that you are running your code in scheduled environment.
Suspension is '''forbidden''' in an unscheduled environment and trying to use such command will fail with an error; you must ensure that you are running your code in a scheduled environment '''and''' can suspend with the '''[[canSuspend]]''' command.
You can test your current environment for the ability of suspension with [[canSuspend]].
 
 
[[Category:Scripting Topics]]

Revision as of 19:09, 28 August 2021

The Scheduler is the part of the game engine that decides which script runs at a certain point in time. It has the ability to pause a running script, move it to the back of the running queue and start a new script.


Scripts

A new script can be started by using spawn, execVM, exec and execFSM commands. The script started this way will be added into the script scheduler and will be executed in turns, giving priority to the scripts waiting longest since their last suspension. Completed scripts are removed from the scheduler while scripts not completed in allocated time are suspended.


Scheduled Environment

Running code in a scheduled environment starts a new script. The executing instance will not wait for the result of scheduled code and will continue on with its execution, so it is not possible to return any values from code executed in this manner although a Script handle for started script is provided.

As described below there is no way to predict when scheduled code will have finished, although you can use the command scriptDone with the provided Script handle to query if the script execution has finished and there is also the command terminate to abort the script.

Because the scheduled code can run only for a fixed duration, heavy scripts spread their load over time, and thus are having lesser impact on the game performance.

All scheduled scripts can run for a maximum of 3ms in a frame. (Except if inside loadingscreen where it is 50ms per frame)
Each Script that runs add's up to the total runtime and as soon as the total runtime of 3ms is reached the current script is paused and the game continues calculating everything else it needs to calculate that frame (For example sound and graphics rendering). On the next frame the scheduler again starts to run scripts for 3ms, starting with the script which has not been executed for the longest time.

This means any script that ran as the 3ms runtime was reached will be paused in the middle of it is execution and depending on how many scripts are spawned it might take several frames till it will run again. A while true loop with sleep started in scheduled environment therefore has little chance to follow with exact interval, because sleep only marks the script as done in the current frame and the next time the script is executed the engine will check if the sleep is over. This means at 20 FPS the time between one frame and the next is roughly 50ms. That would make a sleep 0.01 wait for atleast 0.05 seconds.This effect get's bigger when the fps get even lower and if the scheduler is so overfilled that your script only get's checked every few frames instead of every frame.

If you spawn a scheduled script it will only start to run in the next frame when the scheduler starts fresh again.

Where code starts scheduled

  • init.sqf
  • initServer.sqf
  • initPlayerLocal.sqf
  • initPlayerServer.sqf
  • functions with postInit attribute (although suspension is allowed, any long term suspension will halt the mission loading until suspension has finished)
  • code executed with spawn
  • code executed with execVM
  • code executed with exec
  • code executed with call from a scheduled environment

Scheduler diagnostics commands

The following commands are available:


Unscheduled Environment

Often also called non-scheduled environment but means the same and you will find both terms used in this wiki. The unscheduled environment runs (as described above) in the executing instance. The executing instance waits until the called function is finished. This ensures the execution order and is the fastest way for scripters to execute their code. The disadvantage is that a called function can halt or slow down the game if the function has a high performance consumption. Therefore those functions should be spawned and thereby run in a scheduled environment.

Where code starts unscheduled

while Loops

A while-do loop is limited to (hard-coded) 10,000 iterations in a non-scheduled environment.


Suspension

Suspension is the process to wait a period of time or to wait for something to happen. Commands for suspension are sleep, uiSleep, waitUntil.

Suspension is forbidden in an unscheduled environment and trying to use such command will fail with an error; you must ensure that you are running your code in a scheduled environment and can suspend with the canSuspend command.