Skip to main content

Thread

Threads are designed to execute loops or delays without blocking the game. A thread exists until its code has finished executing or it is manually terminated using Kill.

info

All threads run sequentially (synchronously) on the main thread. Therefore, there is no need to worry about writing thread-safe code.

warning

If you use an infinite loop inside a thread, you must call Thread.Pause() (even Thread.Pause(0) is sufficient). Failing to do so will freeze the entire game or server, as the thread will never yield control back to the main thread. In Lua and JavaScript the runaway protection stops such a thread after 5 seconds and shuts the resource down; in Squirrel nothing steps in.

JavaScript: threads are async functions​

In Lua and Squirrel a thread is a coroutine, and Thread.Pause suspends it. JavaScript has no coroutines, so a thread is an async function and Thread.Pause returns a promise: the thread suspends where you await it.

Thread.Create(async () => {
while (true) {
await Thread.Pause(1000);
Chat.AddMessage("One second later.");
}
});

Everything else is the same: the thread starts on the next tick, wakes up on the first tick after the pause, and Thread.Kill ends it. A few consequences of the promise model:

  • Forgetting await means no pause at all. Thread.Pause(1000); on its own returns immediately, the loop spins, and the runaway protection stops the resource after 5 seconds.
  • A thread function that is not async (or that never awaits) runs to its end in one go and is finished right after.
  • A killed thread is simply never resumed. Code after the await it is waiting on does not run, and neither does a finally block or a catch around it.
  • Thread.Pause and Thread.WaitUntil can be awaited in any async function, not only in a thread. Awaiting them in an event handler works and is a convenient way to delay something; such a wait belongs to no thread, so Thread.Kill cannot cut it short.
  • An async event handler is not awaited by the game: whatever the handler returns, and any Events.Cancel, has to happen before its first await.

Quick Reference​

NameTypeDescription
CreateSharedCreates a new thread with a function that is executed asynchronously.
PauseSharedPauses the current thread for the specified duration.
WaitUntilSharedPauses the current thread until a condition is met.
KillSharedStops the specified thread.
IsAliveSharedReturns whether the specified thread is currently alive and running.

Timing​

Threads are driven by the script tick. A thread never runs between two ticks, and everything below follows from that:

  • The server ticks about every 10 ms, plus however long the tick itself takes.
  • The client ticks once per rendered frame: roughly 16 ms at 60 FPS, 33 ms at 30 FPS, and much longer while the game stutters or streams.

Thread.Pause(ms) therefore means at least ms, never exactly ms. The thread wakes up on the first tick after the time has elapsed, so a pause is always rounded up to the next tick.

danger

Do not use Thread.Pause to measure time. Adding up the values you passed to it does not give you elapsed milliseconds, because the error accumulates with every iteration and is different on every machine. Thread.Pause(1) in a loop is not a millisecond counter: on the server it advances in steps of roughly 10 ms, on the client in frames.

To measure time, read a clock instead. System.GetTime is the clock the threads themselves run on and is available on both sides:

Thread.Create(function()
local start = System.GetTime()

while System.GetTime() - start < 5000 do
Thread.Pause(0)
end

Chat.AddMessage("Five seconds are really over.")
end)
tip

Thread.Pause(0) means "give up the rest of this tick and continue on the next one". It is the right call inside a loop that has to run every frame. A smaller value buys you nothing, because the next tick is the earliest the thread can continue either way.


Shared Functions​

Create​

Creates a thread with a function that is executed on the next script tick.

int threadId = Thread.Create(function threadFunc, any ...)

info

The function does not start running inside Thread.Create, not even its first line. It runs for the first time on the next tick, so anything the calling code does afterwards still happens first. This also holds for a thread created from inside another thread.

note

Threads have no parent. A thread created from inside another thread runs on its own, and killing the one that created it does not affect it.

Example:

Events.Subscribe("sessionInit", function()
Thread.Create(function()
-- Get player model hash key.
local playerModel = Game.GetHashKey("M_Y_MULTIPLAYER")

-- Request model.
Game.RequestModel(playerModel)

-- Wait until model loaded.
while not Game.HasModelLoaded(playerModel) do
Game.RequestModel(playerModel)
Thread.Pause(0)
end

-- Change player model.
Game.ChangePlayerModel(Game.GetPlayerId(), playerModel)

-- Release model.
Game.MarkModelAsNoLongerNeeded(playerModel)
end)
end)

Pause​

Pauses the current thread for at least the specified duration. See Timing.

Thread.Pause(int milliseconds)

In JavaScript the function returns a promise: await Thread.Pause(int milliseconds).

warning

In Lua and Squirrel this function can only be used within a running thread. Calling it anywhere else raises a script error. In JavaScript it can be awaited in any async function, see above.

info

The comment in the example below is a simplification: the message is sent no sooner than every 5 seconds, and the small overshoot of each round adds up over time. If a task has to happen at an exact wall-clock moment, compare against a clock instead of relying on the pause.

Example:

Events.Subscribe("scriptInit", function()
Thread.Create(function()
while true do
Thread.Pause(5000)

-- This message is sent every 5 seconds.
Chat.AddMessage("5 seconds are over.")
end
end)
end)

WaitUntil​

Pauses the current thread until the condition returns true. The condition is called once per script tick, starting with the next tick, so this is the same as a loop that calls Thread.Pause(0) until the condition holds, only shorter.

Thread.WaitUntil(function condition)

In JavaScript the function returns a promise: await Thread.WaitUntil(function condition).

warning

Like Pause, in Lua and Squirrel this function can only be used within a running thread.

info

If the condition raises an error, the error is raised in the waiting thread, at the WaitUntil call, where you can catch it like any other error.

Example:

Thread.Create(function()
local model = Game.GetHashKey("SULTANRS")
Game.RequestModel(model)

Thread.WaitUntil(function()
return Game.HasModelLoaded(model)
end)

Chat.AddMessage("Model loaded.")
end)

Kill​

Stops the specified thread. It never runs again: whatever it was waiting for, the code after that point is not executed. IsAlive returns false right away.

Thread.Kill(int threadId)

info

A thread may kill itself. The code that called Thread.Kill keeps running until the thread's next Thread.Pause or Thread.WaitUntil, which is where it stops for good.


IsAlive​

Returns whether the specified thread is currently alive and running.

bool alive = Thread.IsAlive(int threadId)