使用 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()
or 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 );
运行ning 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()
),
- 在第 58 行,调用了
FREETMPS
宏,但这不会影响(?)array
的引用计数,因为它是在我们设置的 SAVETMPS
边界之外创建的准备回调临时工。另一方面,我们在第 41 行推送的引用在这里被释放(因为它是凡人),这导致它放弃其对 array
的所有权,因此 array
的引用计数将是再次减少到零。
- 在第 60 行,XSUB 退出并且
array
将被释放(在 perl 运行op 循环对 FREETMPS
的后续调用中),因为它的引用计数为零。数组中的所有标量也将在此时被释放 (?)。
上述推理的问题在于它与SvREFCNT()
的输出不一致,如上所示。根据输出,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 永远不会被释放的事实意味着数组的引用计数永远不会减少。您需要使 newRV_inc() 的 return 值死亡。
另一条评论:数组的引用计数并没有在你死亡时减少到零;它保持为 1。我不确定你从哪里得到这个想法。实际发生的是,当您调用 newAV() 时,您会得到一个引用计数为 1 的 AV,这 1 太高了。保持原样,它会泄漏。 sv_2mortal() 不会改变数组的引用计数,但它确实拥有一个引用的所有权,"corrects" 整体引用计数和数组将不再泄漏。
我有这个 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()
or 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 );
运行ning 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()
、 时, - 然后在同一数组上调用
sv_2mortal()
时,它也在第 28 行减少为零, - 在第 41 行我使用
newRV_inc()
创建了一个引用并且array
的引用计数再次从 0 增加到 1(由于_inc
在newRV_inc()
), - 在第 58 行,调用了
FREETMPS
宏,但这不会影响(?)array
的引用计数,因为它是在我们设置的SAVETMPS
边界之外创建的准备回调临时工。另一方面,我们在第 41 行推送的引用在这里被释放(因为它是凡人),这导致它放弃其对array
的所有权,因此array
的引用计数将是再次减少到零。 - 在第 60 行,XSUB 退出并且
array
将被释放(在 perl 运行op 循环对FREETMPS
的后续调用中),因为它的引用计数为零。数组中的所有标量也将在此时被释放 (?)。
array
的引用计数在第28行设置为1
上述推理的问题在于它与SvREFCNT()
的输出不一致,如上所示。根据输出,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 永远不会被释放的事实意味着数组的引用计数永远不会减少。您需要使 newRV_inc() 的 return 值死亡。
另一条评论:数组的引用计数并没有在你死亡时减少到零;它保持为 1。我不确定你从哪里得到这个想法。实际发生的是,当您调用 newAV() 时,您会得到一个引用计数为 1 的 AV,这 1 太高了。保持原样,它会泄漏。 sv_2mortal() 不会改变数组的引用计数,但它确实拥有一个引用的所有权,"corrects" 整体引用计数和数组将不再泄漏。