Reproduction Steps
The functions task.defer
and task.delay
have strange behavior when the coroutine provided to them is not suspended, or if the coroutine is being scheduled for resumption while already being scheduled to be resumed later.
Expected Behavior
If the coroutine provided to task.defer
or task.delay
is dead, nothing should happen.
If the coroutine provided to task.defer
or task.delay
is running or normal, it should be resumed correctly when it would normally be resumed.
If a coroutine is being scheduled for resumption while already being scheduled to be resumed later, these should not interfere and both resumptions should happen correctly.
Actual Behavior
If the coroutine provided to task.defer
or task.delay
is dead, the provided arguments will still be put into the thread (which can resurrect the thread!):
local co = task.spawn(function()end)
print(coroutine.status(co)) -- prints "dead"
task.defer(co,print) -- puts print onto the coroutine
-- which makes it look like a freshly created coroutine
print(coroutine.status(co)) -- prints "suspended"
task.spawn(co,1,2,3) -- prints "1 2 3"
print(coroutine.status(co)) -- prints "dead"
If the coroutine provided to task.defer
or task.delay
is running or normal, the coroutine will be resumed but without any of the extra arguments provided:
task.defer(coroutine.running(),true)
print(coroutine.yield()) -- prints nothing
If a coroutine is being scheduled for resumption while already being scheduled, some strange stuff happens:
local co = coroutine.create(function(...)
warn(...)
while true do
warn(coroutine.yield())
end
end)
task.defer(co,print,1,2,3)
task.defer(co,print)
-- printed normally:
-- 1 2 3 function: 0xXXXXXXXXXXXXXXXX
-- error printed:
-- cannot resume dead coroutine
Workaround
These issues can be worked around by creating a new coroutine that will solely resume the original coroutine.
For example:
task.delay(1,task.spawn,coroutine.running(),true) -- will create a new coroutine that just resumes the main coroutine
print(coroutine.yield())
task.defer(task.spawn,task.spawn(function()end)) -- no error, just ignored
task.defer(task.spawn,task.spawn(function()end),true) -- errors, might be another bug
local co = coroutine.create(function(...)
print(...)
while true do
print(coroutine.yield())
end
end)
task.defer(task.spawn,co,1)
task.defer(task.spawn,co,2)
Issue Area: Engine
Issue Type: Other
Impact: Moderate
Frequency: Rarely