Reproduction Steps
task.defer
example:
local thread
thread = coroutine.create(function()
for _ = 1, 30 do
task.defer(thread)
coroutine.yield()
end
print("done!")
end)
task.spawn(thread)
Equivalent task.spawn
example:
local thread
thread = coroutine.create(function()
for _ = 1, 30 do
task.spawn(function()
task.wait()
task.spawn(thread)
end)
coroutine.yield()
end
print("done!")
end)
task.spawn(thread)
Expected Behavior
task.defer
should behave the same as task.spawn(function(c, ...) task.wait() task.spawn(c, ...) end)
Actual Behavior
task.defer
errors with Maximum re-entrancy depth (10) exceeded
while task.spawn
completes successfully and prints "done!"
Workaround
Using task.spawn
instead of task.defer
Issue Area: Engine
Issue Type: Other
Impact: Moderate
Frequency: Very Rarely
Date First Experienced: 2022-03-02 23:03:00 (-06:00)
Date Last Experienced: 2022-03-02 23:03:00 (-06:00)
Side-note: why am I doing something so weird?
First of all, this is not the actual code I’m using, this is a minimal reproduction of the issue.
I have an object set up to handle some precise and critical ordering of tasks. The tasks are only ran when triggered, which involves a task.defer
call to schedule task processing. It’s conceivable that a task will spawn off a new coroutine that ends up triggering task processing again. This does happen – I ran into it while writing tests for this object.
A common alternative would be something like:
while true do
if self._shouldProcessTasks then
self:_processTasks()
end
task.wait()
end
function Object:_triggerProcessTasks()
self._shouldProcessTasks = true
end
Not weird at all, right!
But I was avoiding that busy loop by using the task library:
function Object:_triggerProcessTasks()
if not self._processingTasks then
task.defer(self._processTasks)
end
end
I have since switched to task.spawn
which doesn’t have this issue, oddly enough:
function Object:_triggerProcessTasks()
if not self._processingTasks then
task.spawn(function()
task.wait()
self:_processTasks()
end)
end
end