安全地释放 XS 代码中的资源(范围退出时 运行 个析构函数)
Safely freeing resources in XS code (running destructors on scope exit)
我正在编写一个 XS 模块。我分配一些资源(例如 malloc()
或 SvREFCNT_inc()
),然后执行一些涉及 Perl API 的操作,然后释放资源。这在普通 C 中很好,因为 C 没有异常,但是使用 Perl API 的代码可能 croak()
,从而阻止正常清理和泄漏资源。因此,除了相当简单的情况外,似乎不可能编写正确的 XS 代码。
当我 croak()
自己时,我可以清理到目前为止分配的所有资源,但我可能会直接调用 croak()
的函数,这会避开我编写的任何清理代码。
Pseudo-code 说明我的担忧:
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 代码 return 返回到 Perl 代码时,我如何注册一些 运行 的析构函数?
到目前为止我的想法和发现:
我可以创建一个 class,运行 需要在析构函数中进行清理,然后创建一个包含此 class 实例的凡人 SV。在未来的某个时候,Perl 将释放那个 SV 和 运行 我的析构函数。但是,这似乎有点落后,必须有更好的方法。
XSAWYERX 的 XS Fun 小册子似乎详细讨论了 DESTROY 方法,但没有讨论 在 XS 代码中产生的异常的处理。
LEONT 的 Scope::OnExit
module features XS code 使用 SAVEDESTRUCTOR()
和 SAVEDESTRUCTOR_X()
宏。这些似乎没有记录。
Perl API 列表 save_destructor()
和 save_destructor_x()
的功能与 public 相同,但未记录。
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" 块,并且足够安全和稳定。
摘自 Localizing changes in perlguts,其中讨论了 { local $foo; ... }
块的等效内容:
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 an ENTER
/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 argument p
.
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), and p
.
该部分还列出了一些专门的析构函数,例如 SAVEFREESV(SV *sv)
和 SAVEMORTALIZESV(SV *sv)
,在某些情况下它们可能比过早的 sv_2mortal()
更正确。
这些宏基本上一直可用,至少 Perl 5.6 或更早版本。
我正在编写一个 XS 模块。我分配一些资源(例如 malloc()
或 SvREFCNT_inc()
),然后执行一些涉及 Perl API 的操作,然后释放资源。这在普通 C 中很好,因为 C 没有异常,但是使用 Perl API 的代码可能 croak()
,从而阻止正常清理和泄漏资源。因此,除了相当简单的情况外,似乎不可能编写正确的 XS 代码。
当我 croak()
自己时,我可以清理到目前为止分配的所有资源,但我可能会直接调用 croak()
的函数,这会避开我编写的任何清理代码。
Pseudo-code 说明我的担忧:
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 代码 return 返回到 Perl 代码时,我如何注册一些 运行 的析构函数?
到目前为止我的想法和发现:
我可以创建一个 class,运行 需要在析构函数中进行清理,然后创建一个包含此 class 实例的凡人 SV。在未来的某个时候,Perl 将释放那个 SV 和 运行 我的析构函数。但是,这似乎有点落后,必须有更好的方法。
XSAWYERX 的 XS Fun 小册子似乎详细讨论了 DESTROY 方法,但没有讨论 在 XS 代码中产生的异常的处理。
LEONT 的
Scope::OnExit
module features XS code 使用SAVEDESTRUCTOR()
和SAVEDESTRUCTOR_X()
宏。这些似乎没有记录。Perl API 列表
save_destructor()
和save_destructor_x()
的功能与 public 相同,但未记录。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" 块,并且足够安全和稳定。
摘自 Localizing changes in perlguts,其中讨论了 { local $foo; ... }
块的等效内容:
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)
和 SAVEMORTALIZESV(SV *sv)
,在某些情况下它们可能比过早的 sv_2mortal()
更正确。
这些宏基本上一直可用,至少 Perl 5.6 或更早版本。