簡體   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()?

我有這個 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));

我試圖弄清楚是在第 41 行使用newRV_inc()還是newRV_noinc()

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

運行 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

據我所知,如果我使用newRV_inc()

  • 調用newAV()時, array的引用計數在第 28 行設置為 1,
  • 然后在同一數組上調用sv_2mortal()時,它也會在第 28 行降至零,
  • 在第 41 行,我使用newRV_inc()創建了一個引用,並且array的引用計數再次從 0 增加到 1(由於_inc newRV_inc()中的 _inc ),
  • 在第 58 行,調用了FREETMPS宏,但這不會影響(?) array的引用計數,因為它是在我們為回調臨時設置的SAVETMPS邊界之外創建的。 另一方面,我們在第 41 行推送的引用在這里被釋放(因為它是 mortal 的),這導致它放棄了它對array array引用計數將再次減少到零。
  • 在第 #60 行,XSUB 退出並且array將被釋放(在 perl 運行循環到FREETMPS的后續調用中),因為它的引用計數為零。 數組中的所有標量也將在該點被釋放 (?)。

上述推理的問題在於它與如上圖所示的SvREFCNT()中的 output 不一致。 根據 output,退出時array的引用計數為 2(而不是 1)。

這里發生了什么?

要重現的其他文件:

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

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'
)

匯編

要編譯模塊,請運行:

perl Makefile.PL
make

您應該使用 newRV_inc()。

您的實際問題是您正在創建一個泄漏的新 RV。 RV 永遠不會被釋放的事實意味着數組上的引用計數永遠不會減少。 您需要使 newRV_inc() 的返回值死亡。

另一條評論:當你將它死亡時,數組的引用計數不會減少到零; 它仍然是 1。我不確定你從哪里得到這個想法。 實際發生的情況是,當您調用 newAV() 時,您會得到一個引用計數為 1 的 AV,即 1 太高了。 保持原樣,它會泄漏。 sv_2mortal() 不會改變數組的引用計數,但它確實擁有一個引用的所有權,這“更正”了整體引用計數並且數組將不再泄漏。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM