简体   繁体   English

使用 call_sv() 将数组引用传递给 perl 回调,我应该使用 newRV() 还是 newRV_noinc()?

[英]Passing an array reference to a perl callback using call_sv(), should I use newRV() or newRV_noinc()?

I have this XS code ( XsTest.xs ):我有这个 XS 代码( XsTest.xs ):

#define PERL_NO_GET_CONTEXT
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"

MODULE = My::XsTest  PACKAGE = My::XsTest
PROTOTYPES: DISABLE

void
foo( callback )
       SV *callback
    PREINIT:
        AV *array;
        SSize_t array_len;
        SV **sv_ptr;
        SV *sv;
        double value;
    CODE:
        if ( !SvROK(callback) ) {
            croak("Not a reference!");
        }
        if ( SvTYPE(SvRV(callback)) != SVt_PVCV ) {
            croak("Not a code reference!");
        }
        /* This array will go out of scope (and be freed) at the end of this XSUB 
         * due to the sv_2mortal()
         */
        array = (AV *)sv_2mortal((SV *)newAV()); /* Line #28 */
        /* NOTE: calling dSP is not necessary for an XSUB, since it has 
         * already been arranged for by xsubpp by calling dXSARGS 
         */
        printf( "Line #28: SvREFCNT(array) = %d\n", SvREFCNT(array));
        ENTER;
        SAVETMPS;
        PUSHMARK(SP);
        EXTEND(SP, 1);
        /* Should I use newRV_inc() or newRV_noinc() here? Or does it not 
         * matter?
         * NOTE: XPUSHs mortalizes the RV (so we do not need to call sv_2mortal()
         */
        XPUSHs((SV *)newRV_inc((SV *) array)); /* Line #41 */
        printf( "Line #41: SvREFCNT(array) = %d\n", SvREFCNT(array));
        PUTBACK;
        call_sv(callback, G_VOID);
        printf( "Line #45: SvREFCNT(array) = %d\n", SvREFCNT(array)); /* Line #45: */
        array_len = av_top_index(array) + 1;
        printf( "Array length: %ld\n", array_len );
        if ( array_len != 1 ) {
            croak( "Unexpected array size: %ld", array_len );
        }
        sv_ptr = av_fetch( array, 0, 0 );
        sv = *sv_ptr;  
        if (SvTYPE(sv) >= SVt_PVAV) {
            croak("Not a scalar value!");
        } 
        value = SvNV(sv);
        printf( "Returned value: %g\n", value);
        FREETMPS;  /* Line # 58 */
        LEAVE;
        printf( "Line #60: SvREFCNT(array) = %d\n", SvREFCNT(array));

I trying to figure out whether to use newRV_inc() or newRV_noinc() at line #41.我试图弄清楚是在第 41 行使用newRV_inc()还是newRV_noinc()

The Perl callback is defined in the test script p.pl : Perl 回调在测试脚本p.pl中定义:

use strict;
use warnings;
use ExtUtils::testlib;
use My::XsTest;

sub callback {
    my ( $ar ) = @_;
    $ar->[0] = 3.12;
}
My::XsTest::foo( \&callback );

The output from running p.pl is:运行 p.pl 的p.pl是:

Line #28: SvREFCNT(array) = 1
Line #41: SvREFCNT(array) = 2
Line #45: SvREFCNT(array) = 2
Array length: 1
Returned value: 3.12
Line #60: SvREFCNT(array) = 2

As far as I can see, if I use newRV_inc() :据我所知,如果我使用newRV_inc()

  • The reference count of array is set to 1 at line #28 when calling newAV() ,调用newAV()时, array的引用计数在第 28 行设置为 1,
  • then it is decreased to zero also at line #28 when calling sv_2mortal() on the same array,然后在同一数组上调用sv_2mortal()时,它也会在第 28 行降至零,
  • at line #41 I create a reference using newRV_inc() and the reference count of array is increased back again from 0 to 1 (due to the _inc in newRV_inc() ),在第 41 行,我使用newRV_inc()创建了一个引用,并且array的引用计数再次从 0 增加到 1(由于_inc newRV_inc()中的 _inc ),
  • at line #58, the FREETMPS macro is called, but this does not affect (?) the refcount of array since it was created outside the SAVETMPS boundary we set up for the callback temporaries.在第 58 行,调用了FREETMPS宏,但这不会影响(?) array的引用计数,因为它是在我们为回调临时设置的SAVETMPS边界之外创建的。 On the other hand, the reference we pushed at line #41 is freed here (since it was made mortal), which causes it to relinquish its ownership of array and hence the reference count of array will be decreased to zero again.另一方面,我们在第 41 行推送的引用在这里被释放(因为它是 mortal 的),这导致它放弃了它对array array引用计数将再次减少到零。
  • at line #60, the XSUB exits and array will be freed (on a following call by the perl runop loop to FREETMPS ) since it has reference count of zero.在第 #60 行,XSUB 退出并且array将被释放(在 perl 运行循环到FREETMPS的后续调用中),因为它的引用计数为零。 All the scalars in the array will also be freed at that point (?).数组中的所有标量也将在该点被释放 (?)。

The problem with the above reasoning is that it does not agree with the output from SvREFCNT() as shown above.上述推理的问题在于它与如上图所示的SvREFCNT()中的 output 不一致。 According to the output, the reference count of array is 2 (and not 1) at exit.根据 output,退出时array的引用计数为 2(而不是 1)。

What is going on here?这里发生了什么?

Additional files to reproduce:要重现的其他文件:

lib/My/XsTest.pm : lib/My/XsTest.pm

package My::XsTest;
use strict;
use warnings;
use Exporter qw(import);
our %EXPORT_TAGS = ( 'all' => [ qw(    ) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw(    );
our $VERSION = 0.01;
require XSLoader;

XSLoader::load();
1;

Makefile.PL : Makefile.PL

use 5.028001;
use strict;
use warnings;
use utf8;
use ExtUtils::MakeMaker 7.12; # for XSMULTI option

WriteMakefile(
  NAME          => 'My::XsTest',
  VERSION_FROM  => 'lib/My/XsTest.pm',
  PREREQ_PM     => { 'ExtUtils::MakeMaker' => '7.12' },
  ABSTRACT_FROM => 'lib/My/XsTest.pm',
  AUTHOR        => 'Håkon Hægland <hakon.hagland@gmail.com>',
  OPTIMIZE      => '',  # e.g., -O3 (for optimize), -g (for debugging)
  XSMULTI       => 0,
  LICENSE       => 'perl',
  LIBS          => [''], # e.g., '-lm'
  DEFINE        => '', # e.g., '-DHAVE_SOMETHING'
  INC           => '-I.', # e.g., '-I. -I/usr/include/other'
)

Compilation汇编

To compile the module, run:要编译模块,请运行:

perl Makefile.PL
make

You should use newRV_inc().您应该使用 newRV_inc()。

Your actual problem is that you are creating a new RV which leaks.您的实际问题是您正在创建一个泄漏的新 RV。 The fact that the RV is never freed means that the reference count on array is never decremented. RV 永远不会被释放的事实意味着数组上的引用计数永远不会减少。 You need to mortalise the return value of newRV_inc().您需要使 newRV_inc() 的返回值死亡。

One other comment: the reference count of array is not reduced to zero when you mortalise it;另一条评论:当你将它死亡时,数组的引用计数不会减少到零; it remains as 1. I'm not sure where you got that idea from.它仍然是 1。我不确定你从哪里得到这个想法。 What actually happens is that when you call newAV(), you are given an AV with a reference count of one, which is 1 too high.实际发生的情况是,当您调用 newAV() 时,您会得到一个引用计数为 1 的 AV,即 1 太高了。 Left as-is, it will leak.保持原样,它会泄漏。 sv_2mortal() doesn't change array's ref count, but it does take ownership of one reference, which "corrects" the overall reference count and array will no longer leak. sv_2mortal() 不会改变数组的引用计数,但它确实拥有一个引用的所有权,这“更正”了整体引用计数并且数组将不再泄漏。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM