我正在编写一个 XS 模块。我分配一些资源(例如malloc()
or SvREFCNT_inc()
)然后做一些涉及 Perl API 的操作,然后释放资源。这在普通 C 中很好,因为 C 没有异常,但使用 Perl API 的代码可能会croak()
,从而防止正常清理和泄漏资源。因此,除了相当简单的情况外,似乎不可能编写正确的 XS 代码。
当我croak()
自己时,我可以清理到目前为止分配的任何资源,但我可能会croak()
直接调用函数,这会避开我编写的任何清理代码。
伪代码来说明我的担忧:
static void some_other_function(pTHX_ Data* d) {
...
if (perhaps) croak("Could not frobnicate the data");
}
MODULE = Example PACKAGE = Example
void
xs(UV n)
CODE:
{
/* Allocate resources needed for this function */
Data* object_graph;
Newx(object_graph, 1, Data);
Data_init(object_graph, n);
/* Call functions which use the Perl API */
some_other_function(aTHX_ object_graph);
/* Clean up before returning.
* Not run if above code croak()s!
* Can this be put into the XS equivalent of a "try...finally" block?
*/
Data_destroy(object_graph);
Safefree(object_graph);
}
那么如何安全地清理 XS 代码中的资源呢?如何注册一些在抛出异常或从 XS 代码返回到 Perl 代码时运行的析构函数?
到目前为止我的想法和发现:
我可以创建一个在析构函数中运行必要清理的类,然后创建一个包含此类实例的凡人 SV。在未来的某个时候,Perl 将释放那个 SV 并运行我的析构函数。然而,这似乎相当倒退,必须有更好的方法。
XSAWYERX 的XS Fun小册子似乎详细讨论了 DESTROY 方法,但没有讨论源自XS 代码的异常的处理。
LEONT 的
Scope::OnExit
模块具有XS 代码使用SAVEDESTRUCTOR()
和SAVEDESTRUCTOR_X()
宏。这些似乎没有记录。Perl API将其列为公共但未记录的函数
save_destructor()
。save_destructor_x()
Perl 的
scope.h
头文件(由 包含perl.h
)声明SAVEDESTRUCTOR(f,p)
和SAVEDESTRUCTOR_X(f,p)
宏,没有任何进一步的解释。从上下文和Scope::OnExit
代码来看,f
是一个函数指针和p
一个将传递给f
.pTHX_
_X 版本适用于使用宏参数声明的函数。
我在正确的轨道上吗?我应该酌情使用这些宏吗?它们是在哪个 Perl 版本中引入的?关于它们的使用是否有任何进一步的指导?究竟什么时候触发析构函数?大概在与FREETMPS
orLEAVE
宏相关的地方?