我正在编写一个 XS 模块。我分配一些资源(例如 malloc()
或 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 code使用SAVEDESTRUCTOR()
和SAVEDESTRUCTOR_X()
宏。这些似乎没有记录。Perl API将
save_destructor()
和save_destructor_x()
函数列为公开但未记录的函数。Perl 的
scope.h
header (包含在perl.h
中)声明了SAVEDESTRUCTOR(f,p)
和SAVEDESTRUCTOR_X(f,p)
宏,没有任何进一步的解释。从上下文和Scope::OnExit
代码来看,f
是函数指针,p
是将传递给的空指针f
。 _X 版本适用于使用pTHX_
宏参数声明的函数。
我的做法是否正确?我应该适本地使用这些宏吗?它们是在哪个 Perl 版本中引入的?是否有关于它们的使用的进一步指导?什么时候触发析构函数?大概是在与 FREETMPS
或 LEAVE
宏相关的地方?
最佳答案
经过进一步研究,事实证明 SAVEDESTRUCTOR
实际上已被记录在案 – 在 perlguts 而不是 perlapi 中。那里记录了确切的语义。
因此我假设 SAVEDESTRUCTOR
应该用作清理的“finally” block ,并且足够安全和稳定。
摘自 Localizing changes in perlguts ,它讨论了等同于 { local $foo; ...
block :
There is a way to achieve a similar task from C via Perl API: create a pseudo-block, and arrange for some changes to be automatically undone at the end of it, either explicit, or via a non-local exit (via die()). A block-like construct is created by a pair of
ENTER
/LEAVE
macros (see Returning a Scalar in perlcall). Such a construct may be created specially for some important localized task, or an existing one (like boundaries of enclosing Perl subroutine/block, or an existing pair for freeing TMPs) may be used. (In the second case the overhead of additional localization must be almost negligible.) Note that any XSUB is automatically enclosed in anENTER
/LEAVE
pair.Inside such a pseudo-block the following service is available:
[…]
SAVEDESTRUCTOR(DESTRUCTORFUNC_NOCONTEXT_t f, void *p)
At the end of pseudo-block the function
f
is called with the only argumentp
.
SAVEDESTRUCTOR_X(DESTRUCTORFUNC_t f, void *p)
At the end of pseudo-block the function
f
is called with the implicit context argument (if any), andp
.
该部分还列出了一些专门的析构函数,例如 SAVEFREESV(SV *sv)
和 SAVEMORTALISEV(SV *sv)
可能比过早的 更正确code>sv_2mortal()
在某些情况下。
这些宏基本上一直可用,至少在 Perl 5.6 或更早版本中是这样。
关于c - 安全释放 XS 代码中的资源(在作用域退出时运行析构函数),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45784972/