在 Lua 中管理 C 对象
今天同事在设计引擎的脚本接口时遇到一个问题:需要把 C 对象指针放到 Lua 中,允许 Lua 保存这个指针,并传递给其它模块。
这是给 Lua 写 C 扩展时常见的问题,撇开如何如何将对象的方法导入 Lua 这个更复杂的问题不谈,我主要想说说 C 对象的生命期管理的问题。
一开始的设计是把对象的销毁方法也导入 Lua ,由脚本程序员手工管理。这是很明显的 C 程序员的思路:谁构造谁释放。但在这里是不合适的,不符合带 gc 机制语言的习惯。
我们当然希望脚本更为健壮,不需要考虑对象释放的问题。所以晚上我想了一下,修改了一下这部分的实现。
从效率方面着手,这个问题分两种情况:
第一种情况很简单,C 对象可以被传入 Lua 状态机后,逻辑上可以确保它的指针一定一直有效,程序直到 Lua 状态机本身关闭后,才会删除对象。这种情况我们只需要把 C 对象指针以 lightuserdata 的形式压入堆栈即可。
第二种情况就是,C 对象由脚本创建或获得。在没有地方对其引用之后,对象则应该被删除以释放其占用的资源。这种情况,我们应该使用 fulluserdata ,为其注册 gc 元方法。
不过问题复杂在,引用 C 对象的可以是脚本也可以在 C 代码中。脚本中对 userdata 的引用 lua 状态机会自行解决,但 lua 的 gc 过程并不能直接知道 C 中是否对对象还有引用,这就是我们需要做的工作了。
python 的 C 接口提供了相关的函数,可以在 C 界面上对 PyObject 加减引用。但是 lua 的 gc 是基于根扫描的,状态机中并没有引用计数。很自然的,lua 就没有类似的 C 接口了。
我的解决方法是,在 lua 注册表中创建一个弱表(value 是弱的,而 key 是强的),把 C 对象指针和对应的 fulluserdata 以及它在 C 中的引用数量记入这个表里。然后提供一对 API 对引用计数增减。当引用计数为 0 时,清除关于计数的表项。最终可利用 gc 回收掉已无引用的 C 对象。
详细的程序可以参考我的 wiki 上贴的代码。
这里补充几点说明:
所有对象的 gc 元方法是共享的,而不是每次创建 fulluserdata 创建一个新的元表。这是一个简单的优化,可以节省不少的内存。方便起见,这个元表也放在那个弱表内。注意:在 Lua 中,每次压入一个 CFucntion 都会重新分配内存创建一个新对象。所以应该尽可能的共用。
每次从 C 对象指针生成 fulluserdata 时,都会去检查以前是否生成过。这样才能使引用计数统一计算。
代码随手写的,并没有经过严格的测试,如果谁想拿去用可自便,但发现 bug 请通知我修改过来。 :D
Comments
Posted by: Cloud | (19) June 19, 2009 12:19 AM
Posted by: sunjun | (18) June 17, 2009 02:12 PM
Posted by: lostpencil | (17) October 23, 2007 11:08 AM
Posted by: 托托 | (16) October 22, 2007 07:43 PM
Posted by: Cloud | (15) October 21, 2007 03:36 PM
Posted by: Atry | (14) October 21, 2007 01:29 AM
Posted by: 12 | (13) October 20, 2007 11:40 PM
Posted by: Atry | (12) October 20, 2007 09:17 PM
Posted by: Atry | (11) October 20, 2007 09:15 PM
Posted by: Cloud | (10) October 20, 2007 06:03 PM
Posted by: 炮炮火枪手 | (9) October 20, 2007 05:47 PM
Posted by: Leon | (8) October 20, 2007 04:09 PM
Posted by: redfox | (7) October 20, 2007 03:03 PM
Posted by: Anonymous | (6) October 19, 2007 04:26 PM
Posted by: Cloud | (5) October 19, 2007 01:42 PM
Posted by: Leon | (4) October 19, 2007 11:28 AM
Posted by: 都虎 | (3) October 18, 2007 04:35 PM
Posted by: 轻如风过 | (2) October 18, 2007 12:53 PM
Posted by: FoX | (1) October 18, 2007 10:47 AM