c - 安全释放 XS 代码中的资源(在作用域退出时运行析构函数)

标签 c perl destructor xs perl-xs

我正在编写一个 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 APIsave_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 版本中引入的?是否有关于它们的使用的进一步指导?什么时候触发析构函数?大概是在与 FREETMPSLEAVE 宏相关的地方?

最佳答案

经过进一步研究,事实证明 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 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)SAVEMORTALISEV(SV *sv) 可能比过早的 更正确code>sv_2mortal() 在某些情况下。

这些宏基本上一直可用,至少在 Perl 5.6 或更早版本中是这样。

关于c - 安全释放 XS 代码中的资源(在作用域退出时运行析构函数),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45784972/

相关文章:

c - 如何获取结构 "Elf64_Rela"中的符号名称

c - 链表的交集

perl - 如何获取 Perl/Tk 文本小部件中的文本?

perl - 学习编程范式的能力会受到文化或母语语法的影响吗?

c++ - 临时对象是如何创建的,实际发生的操作是什么?

c++ - 如何在不调用析构函数的情况下重新分配 vector ?

c++ - 按下按钮时增加枚举值

c - 如何判断输入值是否为数字?

Perl 和复杂的 SOAP 请求

c++ - 是否可以颠倒破坏顺序?