Hey Blue
First of all, thank you for reporting this, there appears to be a breakdown in our ergonomics/documentation.
The limitation here is that Lua specifies a few places where it isn’t safe to use coroutine.yield
, this includes metamethods on tables and the error callback provided to xpcall. I at this point the Lua asynchronous model isn’t doing us any favors since we don’t have a clear indication what calls are going to call yield
. As Nayely described, calling print
on our platform ends up calling yield
which unfortunately wasn’t clear, as illustrated by your test examples.
can we safely call coxpcall?
The answer here is unfortunately “it depends”, since coxpcall
doesn’t provide any co-routine execution in its error handler (like it does for the first callback argument), it is only safe if you can avoid any calls to yield
. We are going to explore how we can make this experience better for developers internally for a future release but in the mean time it might be easier to define your own implementation of xpcall
that executes the handler in side of the cosock context.
local myxpcall(f, err, ...)
local results = table.pack(pcall(f, ...))
-- calling f was successful
if results[1] == true then
return table.unpack(results, 2)
end
-- calling f was unsuccessful and the handler is invalid
if type(err) ~= "function" then
return table.unpack(results, 2)
end
-- calling f was unsuccessful and the handler is valid, call the handler
return err(table.unpack(results, 2))
end
Since the execution here fully moves out of the lua error handling back into our coroutine, we should be safe in calling the provided error handling function.
To be clear, the xpcall semantics are not the only place that this can crop up. For example, if we try and call print
from a metamethod, we end up with a somewhat more helpful error message
local T = {}
T.__index = T
T.__gc = function(t)
print("cleaning up T:", t.id)
end
function T.new()
return setmetatable({id = math.random()}, T)
end
function make_and_drop()
local t = T.new()
end
for i=1, 10 do
make_and_drop()
end
print(collectgarbage("collect"))
> lua ./meta-method-problem.lua
lua: error in __gc metamethod (attempt to yield from outside a coroutine)
I hope that has answered your questions