cperldestructorxsperl-xs

Safely freeing resources in XS code (running destructors on scope exit)


I am writing an XS module. I allocate some resource (e.g. malloc() or SvREFCNT_inc()) then do some operations involving the Perl API, then free the resource. This is fine in normal C because C has no exceptions, but code using the Perl API may croak(), thus preventing normal cleanup and leaking the resources. It therefore seems impossible to write correct XS code except for fairly simple cases.

When I croak() myself I can clean up any resources allocated so far, but I may be calling functions that croak() directly which would sidestep any cleanup code I write.

Pseudo-code to illustrate my concern:

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);
  }

So how do I safely clean up resources in XS code? How can I register some destructor that is run when exceptions are thrown, or when I return from XS code back to Perl code?

My ideas and findings so far:

Am I on the right track with this? Should I use these macros as appropriate? In which Perl version were they introduced? Is there any further guidance available on their use? When precisely are the destructors triggered? Presumably at a point related to the FREETMPS or LEAVE macros?


Solution

  • Upon further research, it turns out that SAVEDESTRUCTOR is in fact documented – in perlguts rather than perlapi. The exact semantics are documented there.

    I therefore assume that SAVEDESTRUCTOR is supposed to be used as a "finally" block for cleanup, and is sufficiently safe and stable.

    Excerpt from Localizing changes in perlguts, which discusses the equivalent to { local $foo; ... } blocks:

    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.

    The section also lists a couple of specialized destructors, like SAVEFREESV(SV *sv) and SAVEMORTALIZESV(SV *sv) that may be more correct than a premature sv_2mortal() in some cases.

    These macros have basically been available since effectively forever, at least Perl 5.6 or older.