Luasocket + nginx error - lua entry thread aborted: runtime error: attempt to yield across C-call boundary

Luasocket + nginx error - lua entry thread aborted: runtime error: attempt to yield across C-call boundary

当我使用以下脚本时:

local smtp = require("socket.smtp")
local from = "from@host"
local rcpt = "rcpt@host"
local msg = {
  headers = {
    to = rcpt,
    subject = "Hi"
  },
  body = "Hello"
}
smtp.send{from = from,rcpt = rcpt,source = smtp.message(msg)}

我收到一条错误消息:lua entry thread aborted: runtime error: attempt to yield across C-call boundary

我正在使用从 luarocks 安装的最新 luasocket 和 Lua 5.1,使用用 LuaJIT 2.1 编译的 nginx。是什么导致了此错误消息,我该如何解决?

我在类似的情况下看到过这条消息;在我的例子中,它与 ngx_lua 的设计有关,它实现了自己的协程调度程序。这意味着 sock:receive 不会阻塞,而是隐式 yield 到调度程序,因此,如果对 sock:receive 的调用是使用堆栈上的 C 函数进行的,您很可能会收到您看到的错误。

在我的例子中,我从调试挂钩进行 sock:receive 调用并收到此错误,直到我切换到使用我自己版本的 luasocket 中不产生的套接字方法。我会检查 socket.smtp 是否使用 "normal" 版本的 luasocket。

这是由于 LuaJIT 和 socket.smtp 的组合使用引起的,它启动了协程。来自 https://github.com/openresty/lua-nginx-module/issues/376:

@AterCattus This is a known limitation in LuaJIT (and the standard Lua 5.1 interpreter) that the require() builtin is currently implemented as a C builtin across which you cannot initiate a yield.

看起来最好的解决方法可能是使用 require.lua 的这个实现:https://github.com/pygy/require.lua。它是用纯 Lua 而不是 C 编写的,以通过 LuaJIT 解决这个问题。

smtp.send 使用 LuaSocket 的 socket.protect 函数来处理内部错误。此函数在 C 中实现,并且在当前版本中不允许屈服(git HEAD 中的版本现在允许在 Lua 5.2+ 上屈服,请参阅应该允许屈服的讨论 here). Apparently someone tries to yield from within it. In etc/dispatch.lua in the LuaSocket package (better use the git HEAD version) there is a replacement function for socket.protect所有 Lua 版本(以额外的临时协程为代价)。您可以尝试用 Lua 函数替换 C 函数,如下所示:

local socket = require("socket")
local base = _G
-- paste modified socket.protect function here

-- continue with your own code:
local smtp = require("socket.smtp")
-- ...