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

3

6

When I use the following script:

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)}

I'm getting an error message: lua entry thread aborted: runtime error: attempt to yield across C-call boundary.

I'm using the newest luasocket installed from luarocks with Lua 5.1 using nginx compiled with LuaJIT 2.1. What is causing this error message and how do I fix it?

Functional answered 7/5, 2015 at 21:15 Comment(2)
Do you have a full example we can plug into content_by_lua_file to see where it fails? Does it fail on the smtp.send or require line? I suspect it's the former, but would like to confirm.Sarsenet
That is pretty much the full example. It fails on the send function.Functional
P
4

smtp.send uses LuaSocket's socket.protect function for handling internal errors. This function is implemented in C and doesn't allow yielding in the current releases (the version in git HEAD now allows yielding on Lua 5.2+, see discussion 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 that should allow yielding on all Lua versions (at the cost of an extra temporary coroutine). You can try replacing the C function with that Lua function like so:

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

-- continue with your own code:
local smtp = require("socket.smtp")
-- ...
Prosthodontist answered 14/5, 2015 at 6:53 Comment(2)
someone tries to yield from within it is the crux of the issue. If the same script works from standalone LuaJIT using luasocket package, it has to be related to ngx_lua. The issue in my case was that ngx_lua socket library was used (which does implicit yield) and the fix was to use the stock luasocket library.Sarsenet
Eliminating the yield would also solve the problem. But how do you do that? The socket.smtp module already tries to load the default LuaSocket functions via require("socket") -- it doesn't even know about the Nginx versions. How do you prevent Nginx from replacing LuaSocket's internal functions (apparently)? Even if you manage, the original LuaSocket functions are blocking (which is probably why Nginx implements its own coroutine-based scheduler in the first place) ...Prosthodontist
R
4

This is caused by the combined use of LuaJIT and socket.smtp, which spins up a coroutine. From 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.

It looks like the best workaround may be to use this implementation of require.lua: https://github.com/pygy/require.lua. It's written in pure Lua, instead of C, to get around this problem with LuaJIT.

Roderickroderigo answered 13/5, 2015 at 14:37 Comment(2)
Hmm, I downloaded require-lua 0.1.7-2 and installed via luarocks, then put require = require"require".require at the top of my script. I'm still getting the same error: attempt to yield across C-call boundary from the smtp.send function.Functional
arby, it looks like @siffiejoe's answer here is the correct one. If socket.protect is written in C, then that's the real underlying problem!Roderickroderigo
P
4

smtp.send uses LuaSocket's socket.protect function for handling internal errors. This function is implemented in C and doesn't allow yielding in the current releases (the version in git HEAD now allows yielding on Lua 5.2+, see discussion 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 that should allow yielding on all Lua versions (at the cost of an extra temporary coroutine). You can try replacing the C function with that Lua function like so:

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

-- continue with your own code:
local smtp = require("socket.smtp")
-- ...
Prosthodontist answered 14/5, 2015 at 6:53 Comment(2)
someone tries to yield from within it is the crux of the issue. If the same script works from standalone LuaJIT using luasocket package, it has to be related to ngx_lua. The issue in my case was that ngx_lua socket library was used (which does implicit yield) and the fix was to use the stock luasocket library.Sarsenet
Eliminating the yield would also solve the problem. But how do you do that? The socket.smtp module already tries to load the default LuaSocket functions via require("socket") -- it doesn't even know about the Nginx versions. How do you prevent Nginx from replacing LuaSocket's internal functions (apparently)? Even if you manage, the original LuaSocket functions are blocking (which is probably why Nginx implements its own coroutine-based scheduler in the first place) ...Prosthodontist
S
2

I've seen this message in a somewhat similar situation; in my case it was related to the design of ngx_lua, which implements its own coroutine scheduler. This means that sock:receive doesn't block, but instead does implicit yield to the scheduler and, as the result, if the call to sock:receive is made with a C function being on the stack, you are likely to get the error you see.

In my case I was making sock:receive call from a debug hook and was getting this error until I switched to using socket methods from my own version of luasocket that doesn't yield. I'd check if socket.smtp is using the "normal" version of luasocket.

Sarsenet answered 8/5, 2015 at 15:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.