joyrex2001 has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I am working on a perl5 adapter for the gRPC api, using perlxs (https://github.com/joyrex2001/grpc-perl). In this implementation I am passing xs-objects as input to other xs-objects. The problem I have it that when I use these objects, the reference count is not increased, and the objects are de-scoped to early.

As an example, consider the following setup:
Grpc::XS::Call T_PTROBJ Grpc::XS::Channel T_PTROBJ Grpc::XS::Timeval T_PTROBJ
In the call constructor, I pass both a Channel and Timeval instance, see:
Grpc::XS::Call new(const char *class, Grpc::XS::Channel channel, \ const char* method, Grpc::XS::Timeval deadline, ... ) PREINIT: CallCTX* ctx = (CallCTX *)malloc( sizeof(CallCTX) ); ctx->wrapped = NULL; CODE: ## some code removed ## ctx->wrapped = grpc_channel_create_call( channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS, completion_queue, method, host_override, deadline->wrapped, NULL); RETVAL = ctx; OUTPUT: RETVAL
When the constructor is called, the timeval object is already out-of-scope, and gets dereferenced by perl. However, this is actually not correct, since it's only out of scope in perl, not in my xs scope.

I think the best way forward would be to increase the refcount of the object so the object is not destoyed yet, any tips how to do that? Or maybe there is another approach?

Gr., Vincent

Replies are listed 'Best First'.
Re: PerlXS typemap and reference counting
by BrowserUk (Patriarch) on Apr 23, 2016 at 22:50 UTC

    Are you saying that when you call new() from Perl code (something like):

    use Grpc::XS::Call; use Grpc::XS::Channel; use Grpc::XS::Timeval; my $timeval = Grpc::XS::Timeval->new( ... ); my $channel = Grpc::XS::Channel->new( ... ); my $call = Grpc::XS::Call->new( $channel, $timeval ); ...

    by the time the values arrive in your XS code, they are out of scope?

    If so, then the likelihood is that they have already been GC'd before you call the constructor (easily checked) and the problem lies in your Timeval & Channel constructors.

    You can manually increment the reference count using SvREFCNT_inc or its optimised variants; but be sure that you don't render them immortal and create a memory leak.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
    In the absence of evidence, opinion is indistinguishable from prejudice.
      > by the time the values arrive in your XS code, they are out of scope?

      No, at that point they are in scope. But the problem is, they are referenced within the object (and used in another XS method). Perl thinks they are out of scope after the constructor is called, but this isn't the case here. This means I need to increase the reference count and this is the part I don't know how to do in XS. How do I increase the reference count in the constructor, where I get the two objects (and decrease upon destroy).
      When writing this all down, I think I am better off refactoring parts of the XS code to perl and have the perl interpreter manage the reference counting...
        I need to increase the reference count and this is the part I don't know how to do in XS

        As BrowserUK indicated, you can use SvREFCNT_inc/SvREFCNT_dec (see perldoc perlapi) to increment/decrement the reference count in XS, or you can write perl wrappers for those XS functions if you want to do the manipulating from perl space:
        use strict; use warnings; use Devel::Peek; use Inline C => Config => CLEAN_AFTER_BUILD => 0, BUILD_NOISY => 1; use Inline C => <<'EOC'; void incref(SV * sv) { SvREFCNT_inc(sv); } void decref (SV * sv) { SvREFCNT_dec(sv); } EOC my $str = 'hello world'; Dump $str; print "\n"; incref $str; Dump $str; print "\n"; decref $str; Dump $str;
        The REFCNT of those 3 dumps is 1, 2, and 1 respectively.

        I think I am better off refactoring parts of the XS code to perl and have the perl interpreter manage the reference counting

        That wouldn't be my first choice, but it's certainly an option - especially if it's proving difficult to handle the reference counting correctly.

        Cheers,
        Rob
        No, at that point they are in scope. But the problem is, they are referenced within the object (and used in another XS method). Perl thinks they are out of scope after the constructor is called, but this isn't the case here.

        Okay. I get what you're saying now -- it would have been a whole lot clearer if you'd provided a full example; or were more precise in your description...

        They don't go out of scope in the constructor, not even immediately on return from it. They go out of scope when you reach the end of the block (subroutine or package) in which they were defined (my). Ie. they act exactly like normal perl lexical variables.

        You don't want them to act like normal perl variables, because you're storing them inside your XS structure, which perl doesn't know anything about, and so it duly GC's them once their Perl scope comes to an end.

        This means I need to increase the reference count and this is the part I don't know how to do in XS. How do I increase the reference count in the constructor, where I get the two objects (and decrease upon destroy).

        If my latest guess is correct -- you don't make it easy -- this is less about how to programmically increment and decrement the reference counts, but rather when to do so.

        And actually, you seem to have a pretty clear handle on that: You increment it when you first store it into your XS structure; and decrement it a) if you overwrite it with a different value; or b) when you are destroying the structure. (Ie. Add a DESTROY) method for the Call object.

        Maybe you already tried this and it didn't work? Gave errors?

        The problem is that you are using the T_PTROBJ typemap; and by the time your XS code gets control, the reference has already been unwrapped -- by the XS generated typemap code -- and what you get is not the SV_ref, but the value you put inside it. Which is useful, most times, but not if you need to manipulate the ref counts.

        The solution is to move from T_PTROBJ to T_OPAQUE, which means your XS code will be given the svref itself; which will allow you to manipulate the refcount; but also require you to unwrap the reference pointer yourself.

        If you posted a working -- or close to working -- version of your code; we could probably show you how to finish it.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
        In the absence of evidence, opinion is indistinguishable from prejudice.
        Don't use malloc, use Newx , see perlclib