Repository navigation
pcall doesn't work in conjection with coroutines. #45
Description
Activity
GopherLua is a Lua5.1 implementation.
So codes that does not work in the original C-Lua 5.1 implementation does not work in the GopherLua too.It seems that your code does not work in the original C-Lua.
#include <stdio.h> #include "lua.h" int main(int argc, char **argv) { lua_State *L; lua_State *co; int i; int status; L = (lua_State*)luaL_newstate(); luaopen_base(L); co = lua_newthread(L); if (luaL_loadfile(co, "test.lua")) { printf("%s\n", lua_tostring(co, lua_gettop(co))); lua_close(co); return; } for(i = 0; i < 20; i++) { lua_pushstring(co, "tick"); lua_pushinteger(co, i); status = lua_resume(co, 2); if(status == LUA_ERRRUN && lua_isstring(co, -1)) { printf("Error: %s", lua_tostring(co, -1)); lua_pop(co, 1); break; } } lua_close(L); return 0; }
This code generates an error: "Got Error: attempt to yield across metamethod/C-call boundary".
That is a seperate issue in C-Lua that can be worked around with the following Lua code, however it's because the coroutine yield is trying to pass through the pcall stack, which can't be serialised in the C Lua stack as a sane value without a patch to the VM. I would note that this work-around doesn't work with Gopher-Lua.
xpcall = function( _fn, _fnErrorHandler ) local typeT = type( _fn ) assert( typeT == "function", "bad argument #1 to xpcall (function expected, got "..typeT..")" ) local co = coroutine.create( _fn ) local tResults = { coroutine.resume( co ) } while coroutine.status( co ) ~= "dead" do tResults = { coroutine.resume( co, coroutine.yield() ) } end if tResults[1] == true then return true, unpack( tResults, 2 ) else return false, _fnErrorHandler( tResults[2] ) end end pcall = function( _fn, ... ) local typeT = type( _fn ) assert( typeT == "function", "bad argument #1 to pcall (function expected, got "..typeT..")" ) local tArgs = { ... } return xpcall( function() return _fn( unpack( tArgs ) ) end, function( _error ) return _error end ) end
Hmmm, your work-around works for me( it seems the same result with C-Lua5.1.4).
test.go
package main import ( "fmt" "github.com/yuin/gopher-lua" ) func main() { state := lua.NewState() main, err := state.LoadFile("test.lua") if err != nil { panic(err) } thread := state.NewThread() s := lua.ResumeOK for i := 0; i < 20; i++ { if s != lua.ResumeError { s, err, _ = state.Resume(thread, main, lua.LString("tick"), lua.LNumber(i)) if err != nil { fmt.Println("Error: ", err) break } } } }
test.lua
require("workaround") local ok, err = pcall( function() local evt, n = coroutine.yield() print("Tick: " .. n) if n > 10 then error("N is > 10") end end) if not ok then print("Got Error: " .. tostring(err)) error("Dying.") end
workaround.lua
xpcall = function( _fn, _fnErrorHandler ) local typeT = type( _fn ) assert( typeT == "function", "bad argument #1 to xpcall (function expected, got "..typeT..")" ) local co = coroutine.create( _fn ) local tResults = { coroutine.resume( co ) } while coroutine.status( co ) ~= "dead" do tResults = { coroutine.resume( co, coroutine.yield() ) } end if tResults[1] == true then return true, unpack( tResults, 2 ) else return false, _fnErrorHandler( tResults[2] ) end end pcall = function( _fn, ... ) local typeT = type( _fn ) assert( typeT == "function", "bad argument #1 to pcall (function expected, got "..typeT..")" ) local tArgs = { ... } return xpcall( function() return _fn( unpack( tArgs ) ) end, function( _error ) return _error end ) end
test.c
#include <stdio.h> #include "lua.h" int main(int argc, char **argv) { lua_State *L; lua_State *co; int i; int status; L = (lua_State*)luaL_newstate(); luaL_openlibs(L); co = lua_newthread(L); if (luaL_loadfile(co, "test.lua")) { printf("%s\n", lua_tostring(co, lua_gettop(co))); lua_close(co); return; } for(i = 0; i < 20; i++) { lua_pushstring(co, "tick"); lua_pushinteger(co, i); status = lua_resume(co, 2); if(status == LUA_ERRRUN && lua_isstring(co, -1)) { printf("Error: %s", lua_tostring(co, -1)); lua_pop(co, 1); break; } } lua_close(L); return 0; }
GopherLua(master) prints
Tick: 1 Error: can not resume a dead thread
C-Lua(5.1.4) prints
Tick: 1 Error: attempt to call a nil value
Hrm, it would seem I'm misunderstanding something in the stack then. I'll do some more digging tomorrow and comment if I can find anything.
Due to lack of your response I am closing this issue for now.
Please feel free to reopen this issue if you have any questions.
It seems that coroutines and pcalls don't quite work right when nested. I'm not sure why it is, but pcalls and coroutine don't play nice together.
This is a complete example program, that reproduces the problem. It should be spitting out several Tick: lines, instead it's spitting nothing out at all.
If you comment out the pcall stuff, it functions as expected.