the problem

lua 版本: Lua 5.4.3 Copyright (C) 1994-2021 Lua.org, PUC-Rio

相关 issue HTTP calls do not work with lua 5.4.3 #331

报错:

socket/http.lua:54: bad argument #1 to 'receive' (string expected, got light userdata)

the solution

有人已经提交一个 PR 了: https://github.com/diegonehab/luasocket/pull/334

根据这个 PR 的 review comment 来看, 貌似是这个提交漏改了一处: https://github.com/diegonehab/luasocket/commit/f329aae724573b87bd2a814f4858d29ca894600b

luarocks 其实是变相地支持从 git 仓库安装 rocks 的.

不过我测试 luarocks install https://example.com/xxxx.spec 失败了, 报的是网络问题, 但是实际上我用 curl 请求是成功的, 不太清楚原因. 因此只好把 spec 文件下载回来再 install.

https://github.com/diegonehab/luasocket/blob/master/rockspec/luasocket-scm-2.rockspec 修改一下:

ersion = "scm-3"
source = {
  url = "git://github.com/pkulchenko/luasocket.git"
  , branch="lua-543-aux-buffer-fix"
}

我把修改后的结果直接放 gist 了, 安装:

cd /tmp/ && curl -LO https://gist.github.com/ttys3/31dbf88ee7d708294d8ae5b0a4954424/raw/c74afc3edc1a48c0f7e1c2c9992750301bbb74ff/luasocket-scm-3.rockspec
luarocks install ./luasocket-scm-3.rockspec

这样安装成功后, 发现还是报同样的错. 然后发现是通过 pacman 安装了 lua-socket 包, 其优先级不知道为什么居然比我的 luarocks 的要高.

简单处理, 把系统的先卸载了吧, 看上去没啥其它东西依赖它:

❯ ls /usr/lib/lua/5.4/socket 
 core.so   serial.so   unix.so

 
❯ paru -R lua-socket
checking dependencies...
error: failed to prepare transaction (could not satisfy dependencies)
:: removing lua-socket breaks dependency 'lua-socket' required by lua-copas
:: removing lua-socket breaks dependency 'lua-socket' required by lua-sec
  ☸ homenas (tkn-demo) in ~/.luarocks via  v5.4.3 
🔴 1 ❯ paru -R lua-socket lua-sec lua-copas
checking dependencies...
:: luarocks optionally requires lua-sec: HTTPS support

Package (3)  Old Version  Net Change

lua-copas    2.0.2-3       -0.10 MiB
lua-sec      2:1.0.2-1     -0.19 MiB
lua-socket   20200329-1    -0.23 MiB

Total Removed Size:  0.52 MiB

暂时来说, 问题解决. 等 https://github.com/diegonehab/luasocket 发新版本修复.


what is Light Userdata ?

https://www.lua.org/pil/28.5.html

https://www.shouce.ren/api/lua/5/_161.htm

目前为止我们使用的userdata称为full userdata。Lua还提供了另一种userdata: light userdata。

一个light userdatum是一个表示C指针的值(也就是一个void *类型的值)。由于它是一个值,我们不能创建他们(同样的,我们也不能创建一个数字)。可以使用函数lua_pushlightuserdata将一个light userdatum入栈:

void lua_pushlightuserdata (lua_State *L, void *p);

尽管都是userdata,light userdata和full userdata有很大不同。Light userdata不是一个缓冲区,仅仅是一个指针,没有metatables。像数字一样,light userdata不需要垃圾收集器来管理她。

有些人把light userdata作为一个低代价的替代实现,来代替full userdata,但是这不是light userdata的典型应用。首先,使用light userdata你必须自己管理内存,因为他们和垃圾收集器无关。第二,尽管从名字上看有轻重之分,但full userdata实现的代价也并不大,比较而言,他只是在分配给定大小的内存时候,有一点点额外的代价。

Light userdata真正的用处在于可以表示不同类型的对象。当full userdata是一个对象的时候,它等于对象自身;另一方面,light userdata表示的是一个指向对象的指针,同样的,它等于指针指向的任何类型的userdata。所以,我们在Lua中使用light userdata表示C对象。

看一个典型的例子,假定我们要实现:Lua和窗口系统的绑定。这种情况下,我们使用full userdata表示窗口(每一个userdatum可以包含整个窗口结构或者一个有系统创建的指向单个窗口的指针)。当在窗口有一个事件发生(比如按下鼠标),系统会根据窗口的地址调用专门的回调函数。为了将这个回调函数传递给Lua,我们必须找到表示指定窗口的userdata。为了找到这个userdata,我们可以使用一个表:索引为表示窗口地址的light userdata,值为在Lua中表示窗口的full userdata。一旦我们有了窗口的地址,我们将窗口地址作为light userdata放到栈内,并且将userdata作为表的索引存到表内。(注意这个表应该有一个weak值,否则,这些full userdata永远不会被回收掉。)