Exhausted allowed execution time error with true loop: Fix

Hi, i assume you have ran into the “exhausted allowed execution time” when making a true loop with an if statement in it?

Like this example LUA code block that will throw this error:

while true do
if boolvalue == true then
-- code
end -- if statement's end
end -- true loop's end

This will throw an error since theres no wait between the start and end of the true loop, so after it repeats 3 times, it throws the error “Exhausted allowed execution time”. to easily get around this, just put a task.wait() inbetween the if statement’s end and the true loop’s end, like this:

while true do
if boolvalue == true then
-- code
end -- if statement's end
task.wait()
end -- true loop's end

this should fix your problem, if it does not, theres probably something else wrong with your code. Hope this helps!

3 Likes

why putting task.wait() when you can use RunService?

1 Like

Because it’s shorter and has the same effect :+1:

5 Likes

task.wait() is exactly the same as RunService.Heartbeat.

1 Like

to be honest, runservice is probably better for this type of loops that should run every frame- this is because its guaranteed that it will run every frame, and on top of that, you have more control on when to stop the entire thing.

1 Like

If you are runservice there is no reason to use a loop in the first place. A run service is a loop is a senseless idea.

Do this instead

RUNSERVICEEVENT:Connect(function()
-- // Your code
end)
1 Like

RunService’s events are loops though- the only different thing is that they resume at different rates, and spawn new threads (either that, or they reuse threads) for execution.

1 Like

They are not. One uses event based coding and the other one uses yielding.

No thats not true, they all hold a purpose.

  • .RenderStepped always runs before rendering
  • .Stepped runs before physics
  • .HeartBeat runs after physics

All of them can run at different rates

1 Like

I am talking at the scheduler level though, as both of the yielding-threads and those events run at a specific order, however, the only difference is that the yielding alternative has a control issue, and isn’t guaranteed to run every frame at all.

See this:

Thats what I just said though?

1 Like

Oh I thought you meant something different

2 Likes

You could also change it to this, so you have less waits

local allowedExecutionTime = 1/60

local lastWait = os.clock()
while true do
	--code

	local now = os.clock()
	if now - lastWait >= allowedExecutionTime then
		task.wait()
		lastWait = now
	end
end

but this should only be used for really long loops. Not infinite loops that should run only once per frame.

1 Like

Just a slight correction, task.wait() resumes before RunService.Heartbeat is fired. The quote provided is only true if the :Wait() method is called. The Task Scheduler highlights the difference between resumptions and running a function connected to RunService.Heartbeat; but here’s a test to prove how resumptions fire before Heartbeat:

Test Code
local RunService = game:GetService("RunService")
local endTime = os.clock() + 10
local heartbeatFunctions = {}

--Create 10 different functions with 10 different connections
for i = 1, 10 do
	heartbeatFunctions[i] = {
		Function = function(deltaTime)
			if os.clock() >= endTime then
				print(i, "Heartbeat ", os.clock())
				heartbeatFunctions[i].Connection:Disconnect() --Spam guard
			end
		end
	}
	heartbeatFunctions[i].Connection = RunService.Heartbeat:Connect(heartbeatFunctions[i].Function)
end


--Create 10 different loops with 10 different resumptions
--Note how these threads are created after Heartbeat is connected
for i = 1, 10 do
	task.spawn(function()
		while true do
			if os.clock() >= endTime then
				print(i, "Resume ", os.clock())
				break --Spam guard
			end
			task.wait()
		end
	end)
end

Found it interesting that Heartbeat connections appear to be Last In First Out(LIFO) instead of FIFO

Brought this up because sometimes it’s important to know when the code will execute in regards to other code.

The task scheduler docs are out-of-date with the release of task.wait(). wait() throttles, whereas task.wait() will resume when its time is up regardless of how many resumptions there are within the same frame. The resumption time was also upgraded to 1/60, which can also be seen by running the Test Code further up.

1 Like

I am aware the fact that the new schedular is running at 60hz instead of 30hz, however, do you have any sources that the new APIs aren’t prone to the issue I previously mentioned? Not to be annoying, but I don’t believe people that say docs (or any source of official teaching) are out of date without any proof provided.

regardless of that issue existence, the yielding solution has a control issue when compared to the event alternative- plus, the run service events hold specific purposes which should be enough for cases where you would probably need an infinite loop.

I believe I covered some of this in the release post for the task library however it looks like the task scheduling documentation is out of date. I’ll log that and hopefully get it updated soon.

To confirm, members of the task library are not throttled.

4 Likes
2 Likes