Pcall And Coroutines

lua-users home
wiki

pcall and coroutines didn't always get along. [coxpcall] for Lua 5.1 reimplements pcall/xpcall in terms of coroutines to allows things like yielding across a pcall. There were also patches like ResumableVmPatch. Lua 5.2 (LuaFiveTwo) implements an approach like ResumableVmPatch [1].

Below implements pcall/error like functions in terms of coroutines. The downside is the overhead of the coroutine construction per pcall. coxpcall is fairly similar but doesn't reimplement error as a coroutine.yield.

local function tuple(...)

  return {n=select('#', ...), ...}

end



function pcall(f, ...)

  local co = coroutine.create(f)

  local res = tuple(coroutine.resume(co, ...))

  if res[1] and coroutine.status(co) == "suspended" then

    res[1] = false

  end

  return unpack(res, 1, res.n)

end



local handlers = setmetatable({}, {__mode='kv'})



function xpcall(f, err, ...)

  local co = coroutine.create(f)

  handlers[co] = err

  local res = tuple(coroutine.resume(co, ...))

  if res[1] and coroutine.status(co) == "suspended" then

    res[1] = false

  end

  if not res[1] and err then

    res[2] = err(co, res[2])

      -- note: assumes err can accept coroutine as first argument.

  end

  return unpack(res, 1, res.n)

end





function error(e, level_) --FIX:level not handled

  coroutine.yield(e)

end





-- test



local function g(x)

  if x == "fail" then

    error "fail"

  elseif x == "fail2" then

    local y = nil + nil  -- error raised by Lua

  end

end



local function f(x)

  print(1)

  g(x)

  print(2)

  return 3,4

end



print(pcall(f, "ok"))

print(pcall(f, "fail"))

print(pcall(f, "fail2"))

print(xpcall(f, debug.traceback, "fail"))

print(xpcall(f, debug.traceback, "fail2"))



--DavidManura


RecentChanges · preferences
edit · history
Last edited July 3, 2010 11:27 pm GMT (diff)