gaijin
gaijin

Reputation: 185

Perl XS Memory Handling of Strings

I have an XSUB like this:

char *
string4()
CODE:
    char *str = strdup("Hello World4");
    int len = strlen(str) + 1;
    New(0, RETVAL, len, char);
    Copy(str, RETVAL, len, char);
    free(str);
OUTPUT:
    RETVAL

But this shows up as a memory leak, on the New(), in valgrind and if I run it in a loop the resident memory will continue to grow.

I get the same thing if I use this one too:

char *
string2()
CODE:
    char *str = strdup("Hello World2");
    RETVAL = str;
OUTPUT:
    RETVAL

I'm able to prevent the leak and the increasing memory size by doing:

char *
string3()
PPCODE:
    char *str = strdup("Hello World3");
    XPUSHs(sv_2mortal(newSVpv(str, 0)));
    free(str);

but the problem with this solution is that when I compile with -Werror I get the following warnings/errors.

test.c: In function ‘XS_test_string3’:
/usr/lib/x86_64-linux-gnu/perl/5.20/CORE/XSUB.h:175:28: error: unused variable ‘targ’ [-Werror=unused-variable]
 #define dXSTARG SV * const targ = ((PL_op->op_private & OPpENTERSUB_HASTARG) \
                            ^
test.c:270:2: note: in expansion of macro ‘dXSTARG’
  dXSTARG;
  ^
test.c:269:9: error: unused variable ‘RETVAL’ [-Werror=unused-variable]
  char * RETVAL;

the c file gets built with an unused RETVAL:

XS_EUPXS(XS_test_string3); /* prototype to pass -Wmissing-prototypes */
XS_EUPXS(XS_test_string3)
{
    dVAR; dXSARGS;
    if (items != 0)
       croak_xs_usage(cv,  "");
    PERL_UNUSED_VAR(ax); /* -Wall */
    SP -= items;
    {
    char *  RETVAL;
    dXSTARG;
#line 61 "test.xs"
    char *str = strdup("Hello World3");
    XPUSHs(sv_2mortal(newSVpv(str, 0)));
    free(str);
#line 276 "test.c"
    PUTBACK;
    return;
    }
}

So is there a better way to handle the returning of allocated strings in XS? Is there a way to return the string using RETVAL and free the memory? I appreciate any help.

Upvotes: 0

Views: 473

Answers (1)

ikegami
ikegami

Reputation: 385897

Among other problems[1], your first snippet allocates memory using New, but never deallocates it.

Among other problems, your second snippet allocates memory using strdup, but never deallocates it.

The underlying problem with your third snippet is that you claim the XS function returns a value and it doesn't. That value would have been assigned to RETVAL, which is automatically created for that very purpose. The variable won't be created if you correctly specify that you don't return anything.

void
string3()
PREINIT:
    char *str;
PPCODE:
    str = strdup("Hello World3");
    XPUSHs(sv_2mortal(newSVpv(str, 0)));
    free(str);

or just

void
string3()
PPCODE:
    XPUSHs(sv_2mortal(newSVpv("Hello World3", 0)));

Note that I moved your declarations out of PPCODE. In C, declarations can't appear after non-declarations, and the code in PPCODE can appear after non-declarations (depending on the options used to build Perl). Declarations belong in PREINIT. You could also use curlies around the code in PPCODE.


  1. One of them is the use of New. You shoudln't be using New. New was deprecated in favour of Newx ages ago. New hasn't even been in the documentation for as long as I can remember.

Upvotes: 1

Related Questions