Skip to content

Commit 9961aac

Browse files
committed
scope.c - add mortal_destructor_sv() and mortal_svfunc_x()
The function SAVEDESTRUCTOR_X() (save_destructor_x) can be used to execute a C function at the end of the current psuedo-block. Prior to this patch there was no "mortal" equivalent that would execute at the end of the current statement. We offer a collection of functions which are intended to free SV's at either point in time, but only support callbacks at the end of the current pseudo-block. This patch adds two such functions, "mortal_destructor_sv" which can be used to trigger a perl code reference to execute at the end of the current statement, and "mortal_svfunc_x" which can be used to trigger an SVFUNC_t C function at the end of the current statement. Both functions differ from save_destructor_x() in that instead of supporting a void pointer argument they both require their argument to be some sort of SV pointer. The Perl callback function triggered by "mortal_destructor_sv" may be provided no arguments, a single argument or a list of arguments, depending on the type of argument provided to mortal_destructor_sv(): when the argument is a raw AV (with no SV ref wrapping it), then the contents of the AV are passed in as a list of arguments. When the argument is anything else but NULL, the argument is provided as a single argument, and when it is NULL the perl function is called with no arguments. Both functions are implemented on top of a mortal SV (unseen by the user) which has PERL_MAGIC_destruct magic associated with it, which triggers the destructor behavior when the SV is freed. Both functions are provided with macros to match the normal SAVExx() API, with MORTALDESTRUCTOR_SV() wrapping mortal_destructor_sv() and MORTALSVFUNC_X() wrapping mortal_svfunc_x(). The heart of this logic cribbed from Leon Timmermans' Variable-OnDestruct. See the code at: https://metacpan.org/dist/Variable-OnDestruct/source/lib/Variable/OnDestruct.xs#L6-17 I am very grateful to him for his help on this. Any errors or omissions in this code are my fault, not his.
1 parent 7dceac4 commit 9961aac

File tree

15 files changed

+292
-7
lines changed

15 files changed

+292
-7
lines changed

MANIFEST

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4879,6 +4879,7 @@ ext/XS-APItest/t/lvalue.t Test XS lvalue functions
48794879
ext/XS-APItest/t/magic.t test attaching, finding, and removing magic
48804880
ext/XS-APItest/t/magic_chain.t test low-level MAGIC chain handling
48814881
ext/XS-APItest/t/Markers.pm Helper for ./blockhooks.t
4882+
ext/XS-APItest/t/mortal_destructor.t Test mortal_destructor api.
48824883
ext/XS-APItest/t/mro.t Test mro plugin api
48834884
ext/XS-APItest/t/multicall.t XS::APItest: test MULTICALL macros
48844885
ext/XS-APItest/t/my_cxt.t XS::APItest: test MY_CXT interface

embed.fnc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,9 @@ p |int |magic_existspack \
18081808
p |int |magic_freearylen_p \
18091809
|NN SV *sv \
18101810
|NN MAGIC *mg
1811+
dp |int |magic_freedestruct \
1812+
|NN SV *sv \
1813+
|NN MAGIC *mg
18111814
p |int |magic_freemglob|NN SV *sv \
18121815
|NN MAGIC *mg
18131816
p |int |magic_freeovrld|NN SV *sv \
@@ -1970,7 +1973,12 @@ Cop |void * |more_bodies |const svtype sv_type \
19701973
|const size_t arena_size
19711974
Cp |const char *|moreswitches \
19721975
|NN const char *s
1976+
Adp |void |mortal_destructor_sv \
1977+
|NN SV *coderef \
1978+
|NULLOK SV *args
19731979
CRTXip |char * |mortal_getenv |NN const char *str
1980+
Cdp |void |mortal_svfunc_x|SVFUNC_t f \
1981+
|NULLOK SV *p
19741982
Adop |const struct mro_alg *|mro_get_from_name \
19751983
|NN SV *name
19761984
Adp |AV * |mro_get_linear_isa \

embed.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,9 @@
359359
# define mg_size(a) Perl_mg_size(aTHX_ a)
360360
# define mini_mktime Perl_mini_mktime
361361
# define moreswitches(a) Perl_moreswitches(aTHX_ a)
362+
# define mortal_destructor_sv(a,b) Perl_mortal_destructor_sv(aTHX_ a,b)
362363
# define mortal_getenv Perl_mortal_getenv
364+
# define mortal_svfunc_x(a,b) Perl_mortal_svfunc_x(aTHX_ a,b)
363365
# define mro_get_linear_isa(a) Perl_mro_get_linear_isa(aTHX_ a)
364366
# define mro_method_changed_in(a) Perl_mro_method_changed_in(aTHX_ a)
365367
# define msbit_pos32 Perl_msbit_pos32
@@ -961,6 +963,7 @@
961963
# define magic_copycallchecker(a,b,c,d,e) Perl_magic_copycallchecker(aTHX_ a,b,c,d,e)
962964
# define magic_existspack(a,b) Perl_magic_existspack(aTHX_ a,b)
963965
# define magic_freearylen_p(a,b) Perl_magic_freearylen_p(aTHX_ a,b)
966+
# define magic_freedestruct(a,b) Perl_magic_freedestruct(aTHX_ a,b)
964967
# define magic_freemglob(a,b) Perl_magic_freemglob(aTHX_ a,b)
965968
# define magic_freeovrld(a,b) Perl_magic_freeovrld(aTHX_ a,b)
966969
# define magic_freeutf8(a,b) Perl_magic_freeutf8(aTHX_ a,b)

ext/XS-APItest/APItest.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use strict;
44
use warnings;
55
use Carp;
66

7-
our $VERSION = '1.31';
7+
our $VERSION = '1.32';
88

99
require XSLoader;
1010

ext/XS-APItest/APItest.xs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1515,6 +1515,11 @@ test_bool_internals_func(SV *true_sv, SV *false_sv, const char *msg) {
15151515
}
15161516
#include "const-c.inc"
15171517

1518+
void
1519+
destruct_test(pTHX_ void *p) {
1520+
warn("In destruct_test: %" SVf "\n", (SV*)p);
1521+
}
1522+
15181523
MODULE = XS::APItest PACKAGE = XS::APItest
15191524

15201525
INCLUDE: const-xs.inc
@@ -4908,6 +4913,24 @@ sv_refcnt(SV *sv)
49084913
OUTPUT:
49094914
RETVAL
49104915

4916+
void
4917+
test_mortal_destructor_sv(SV *coderef, SV *args)
4918+
CODE:
4919+
MORTALDESTRUCTOR_SV(coderef,args);
4920+
4921+
void
4922+
test_mortal_destructor_av(SV *coderef, AV *args)
4923+
CODE:
4924+
/* passing in an AV cast to SV is different from a SV ref to an AV */
4925+
MORTALDESTRUCTOR_SV(coderef, (SV *)args);
4926+
4927+
void
4928+
test_mortal_svfunc_x(SV *args)
4929+
CODE:
4930+
MORTALSVFUNC_X(&destruct_test,args);
4931+
4932+
4933+
49114934

49124935
MODULE = XS::APItest PACKAGE = XS::APItest
49134936

ext/XS-APItest/t/mortal_destructor.t

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use XS::APItest;
2+
use Test::More tests => 1;
3+
use Data::Dumper;
4+
my $warnings = "";
5+
$SIG{__WARN__} = sub { $warnings .= $_[0]; };
6+
7+
warn "Before test_mortal_destructor_sv\n";
8+
test_mortal_destructor_sv(sub { warn "in perl callback: ", $_[0],"\n" }, {});
9+
warn "After test_mortal_destructor_sv\n";
10+
11+
warn "Before test_mortal_destructor_av\n";
12+
test_mortal_destructor_av(sub { warn "in perl callback: @_\n" }, ["a","b","c"]);
13+
warn "After test_mortal_destructor_av\n";
14+
15+
warn "Before test_mortal_destructor_x\n";
16+
test_mortal_svfunc_x("this is an argument");
17+
warn "After test_mortal_destructor_x\n";
18+
19+
$warnings=~s/0x[A-Fa-f0-9]+/0xDEADBEEF/g;
20+
is($warnings, <<'EXPECT');
21+
Before test_mortal_destructor_sv
22+
in perl callback: HASH(0xDEADBEEF)
23+
After test_mortal_destructor_sv
24+
Before test_mortal_destructor_av
25+
in perl callback: a b c
26+
After test_mortal_destructor_av
27+
Before test_mortal_destructor_x
28+
In destruct_test: this is an argument
29+
After test_mortal_destructor_x
30+
EXPECT

mg_names.inc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
{ PERL_MAGIC_vstring, "vstring(V)" },
4545
{ PERL_MAGIC_vec, "vec(v)" },
4646
{ PERL_MAGIC_utf8, "utf8(w)" },
47+
{ PERL_MAGIC_destruct, "destruct(X)" },
4748
{ PERL_MAGIC_substr, "substr(x)" },
4849
{ PERL_MAGIC_nonelem, "nonelem(Y)" },
4950
{ PERL_MAGIC_defelem, "defelem(y)" },

mg_raw.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
"/* vec 'v' vec() lvalue */" },
7777
{ 'w', "want_vtbl_utf8 | PERL_MAGIC_VALUE_MAGIC",
7878
"/* utf8 'w' Cached UTF-8 information */" },
79+
{ 'X', "want_vtbl_destruct | PERL_MAGIC_VALUE_MAGIC",
80+
"/* destruct 'X' destruct callback */" },
7981
{ 'x', "want_vtbl_substr | PERL_MAGIC_VALUE_MAGIC",
8082
"/* substr 'x' substr() lvalue */" },
8183
{ 'Y', "want_vtbl_nonelem | PERL_MAGIC_VALUE_MAGIC",

mg_vtable.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#define PERL_MAGIC_vstring 'V' /* SV was vstring literal */
5252
#define PERL_MAGIC_vec 'v' /* vec() lvalue */
5353
#define PERL_MAGIC_utf8 'w' /* Cached UTF-8 information */
54+
#define PERL_MAGIC_destruct 'X' /* destruct callback */
5455
#define PERL_MAGIC_substr 'x' /* substr() lvalue */
5556
#define PERL_MAGIC_nonelem 'Y' /* Array element that does not exist */
5657
#define PERL_MAGIC_defelem 'y' /* Shadow "foreach" iterator variable /
@@ -69,6 +70,7 @@ enum { /* pass one of these to get_vtbl */
6970
want_vtbl_dbline,
7071
want_vtbl_debugvar,
7172
want_vtbl_defelem,
73+
want_vtbl_destruct,
7274
want_vtbl_env,
7375
want_vtbl_envelem,
7476
want_vtbl_hints,
@@ -107,6 +109,7 @@ EXTCONST char * const PL_magic_vtable_names[magic_vtable_max] = {
107109
"dbline",
108110
"debugvar",
109111
"defelem",
112+
"destruct",
110113
"env",
111114
"envelem",
112115
"hints",
@@ -168,6 +171,7 @@ EXT_MGVTBL PL_magic_vtables[magic_vtable_max] = {
168171
{ 0, Perl_magic_setdbline, 0, 0, 0, 0, 0, 0 },
169172
{ Perl_magic_getdebugvar, Perl_magic_setdebugvar, 0, 0, 0, 0, 0, 0 },
170173
{ Perl_magic_getdefelem, Perl_magic_setdefelem, 0, 0, 0, 0, 0, 0 },
174+
{ 0, 0, 0, 0, Perl_magic_freedestruct, 0, 0, 0 },
171175
{ 0, Perl_magic_set_all_env, 0, Perl_magic_clear_all_env, 0, 0, 0, 0 },
172176
{ 0, Perl_magic_setenv, 0, Perl_magic_clearenv, 0, 0, 0, 0 },
173177
{ 0, 0, 0, Perl_magic_clearhints, 0, 0, 0, 0 },
@@ -214,6 +218,7 @@ EXT_MGVTBL PL_magic_vtables[magic_vtable_max];
214218
#define PL_vtbl_dbline PL_magic_vtables[want_vtbl_dbline]
215219
#define PL_vtbl_debugvar PL_magic_vtables[want_vtbl_debugvar]
216220
#define PL_vtbl_defelem PL_magic_vtables[want_vtbl_defelem]
221+
#define PL_vtbl_destruct PL_magic_vtables[want_vtbl_destruct]
217222
#define PL_vtbl_env PL_magic_vtables[want_vtbl_env]
218223
#define PL_vtbl_envelem PL_magic_vtables[want_vtbl_envelem]
219224
#define PL_vtbl_fm PL_magic_vtables[want_vtbl_fm]

pod/perldiag.pod

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,14 @@ a C<given> block. You probably meant to use C<next> or C<last>.
832832

833833
(F) You called C<break>, but you're not inside a C<given> block.
834834

835+
=item Can't call destructor for 0x%p in global destruction
836+
837+
(S) This should not happen. Internals code has set up a destructor
838+
using C<mortal_destructor_sv> or C<mortal_destructor_x> which is firing
839+
during global destruction. Please attempt to reduce the code that triggers
840+
this warning down to a small an example as possible and then report the
841+
problem to L<https://github.com/Perl/perl5/issues/new/choose>
842+
835843
=item Can't call method "%s" on an undefined value
836844

837845
(F) You used the syntax of a method call, but the slot filled by the

pod/perlguts.pod

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1510,17 +1510,17 @@ will be lost.
15101510
tables
15111511
< PERL_MAGIC_backref vtbl_backref For weak ref data
15121512
@ PERL_MAGIC_arylen_p (none) To move arylen out of XPVAV
1513-
B PERL_MAGIC_bm vtbl_regexp Boyer-Moore
1513+
B PERL_MAGIC_bm vtbl_regexp Boyer-Moore
15141514
(fast string search)
1515-
c PERL_MAGIC_overload_table vtbl_ovrld Holds overload table
1515+
c PERL_MAGIC_overload_table vtbl_ovrld Holds overload table
15161516
(AMT) on stash
1517-
D PERL_MAGIC_regdata vtbl_regdata Regex match position data
1517+
D PERL_MAGIC_regdata vtbl_regdata Regex match position data
15181518
(@+ and @- vars)
15191519
d PERL_MAGIC_regdatum vtbl_regdatum Regex match position data
15201520
element
15211521
E PERL_MAGIC_env vtbl_env %ENV hash
15221522
e PERL_MAGIC_envelem vtbl_envelem %ENV hash element
1523-
f PERL_MAGIC_fm vtbl_regexp Formline
1523+
f PERL_MAGIC_fm vtbl_regexp Formline
15241524
('compiled' format)
15251525
g PERL_MAGIC_regex_global vtbl_mglob m//g target
15261526
H PERL_MAGIC_hints vtbl_hints %^H hash
@@ -1548,6 +1548,7 @@ will be lost.
15481548
V PERL_MAGIC_vstring (none) SV was vstring literal
15491549
v PERL_MAGIC_vec vtbl_vec vec() lvalue
15501550
w PERL_MAGIC_utf8 vtbl_utf8 Cached UTF-8 information
1551+
X PERL_MAGIC_destruct vtbl_destruct destruct callback
15511552
x PERL_MAGIC_substr vtbl_substr substr() lvalue
15521553
Y PERL_MAGIC_nonelem vtbl_nonelem Array element that does not
15531554
exist
@@ -1575,6 +1576,7 @@ will be lost.
15751576
=for apidoc_item ||PERL_MAGIC_dbline
15761577
=for apidoc_item ||PERL_MAGIC_debugvar
15771578
=for apidoc_item ||PERL_MAGIC_defelem
1579+
=for apidoc_item ||PERL_MAGIC_destruct
15781580
=for apidoc_item ||PERL_MAGIC_env
15791581
=for apidoc_item ||PERL_MAGIC_envelem
15801582
=for apidoc_item ||PERL_MAGIC_ext
@@ -1978,19 +1980,53 @@ this:
19781980
=item C<SAVEDESTRUCTOR(DESTRUCTORFUNC_NOCONTEXT_t f, void *p)>
19791981

19801982
At the end of I<pseudo-block> the function C<f> is called with the
1981-
only argument C<p>.
1983+
only argument C<p> which may be NULL.
19821984

19831985
=for apidoc Ayh||DESTRUCTORFUNC_NOCONTEXT_t
19841986
=for apidoc Amh||SAVEDESTRUCTOR|DESTRUCTORFUNC_NOCONTEXT_t f|void *p
19851987

19861988
=item C<SAVEDESTRUCTOR_X(DESTRUCTORFUNC_t f, void *p)>
19871989

19881990
At the end of I<pseudo-block> the function C<f> is called with the
1989-
implicit context argument (if any), and C<p>.
1991+
implicit context argument (if any), and C<p> which may be NULL.
1992+
1993+
Note the I<end of the current pseudo-block> may occur much later than
1994+
the the I<end of the current statement>. You may wish to look at the
1995+
C<MORTALDESTRUCTOR_X()> macro instead.
19901996

19911997
=for apidoc Ayh||DESTRUCTORFUNC_t
19921998
=for apidoc Amh||SAVEDESTRUCTOR_X|DESTRUCTORFUNC_t f|void *p
19931999

2000+
=item C<MORTALSVFUNC_X(SVFUNC_t f, SV *sv)>
2001+
2002+
At the end of I<the current statement> the function C<f> is called with
2003+
the implicit context argument (if any), and C<sv> which may be NULL.
2004+
2005+
Be aware that the parameter argument to the destructor function differs
2006+
from the related C<SAVEDESTRUCTOR_X()> in that it MUST be either NULL or
2007+
an C<SV*>.
2008+
2009+
Note the I<end of the current statement> may occur much before the
2010+
the I<end of the current pseudo-block>. You may wish to look at the
2011+
C<SAVEDESTRUCTOR_X()> macro instead.
2012+
2013+
=for apidoc Amh||MORTALDESTRUCTOR_X|DESTRUCTORFUNC_t f|SV *sv
2014+
2015+
=item C<MORTALDESTRUCTOR_SV(SV *coderef, SV *args)>
2016+
2017+
At the end of I<the current statement> the Perl function contained in
2018+
C<coderef> is called with the arguments provided (if any) in C<args>.
2019+
See the documentation for C<mortal_destructor_sv()> for details on
2020+
the C<args> parameter is handled.
2021+
2022+
Note the I<end of the current statement> may occur much before the
2023+
the I<end of the current pseudo-block>. If you wish to call a perl
2024+
function at the end of the current pseudo block you should use the
2025+
C<SAVEDESTRUCTOR_X()> API instead, which will require you create a
2026+
C wrapper to call the Perl function.
2027+
2028+
=for apidoc Amh||MORTALDESTRUCTOR_SV|SV *coderef|SV *args
2029+
19942030
=item C<SAVESTACK_POS()>
19952031

19962032
The current offset on the Perl internal stack (cf. C<SP>) is restored

proto.h

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

regen/mg_vtable.pl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ BEGIN
212212
vtable => 'debugvar' },
213213
lvref => { char => '\\', vtable => 'lvref',
214214
desc => "Lvalue reference constructor" },
215+
destruct => {
216+
char => "X",
217+
vtable => 'destruct',
218+
desc => "destruct callback",
219+
value_magic => 1,
220+
},
215221
);
216222

217223

@@ -288,6 +294,7 @@ BEGIN
288294
'checkcall' => {copy => 'copycallchecker'},
289295
'debugvar' => { set => 'setdebugvar', get => 'getdebugvar' },
290296
'lvref' => {set => 'setlvref'},
297+
'destruct' => {free => 'freedestruct'},
291298
);
292299

293300

@@ -428,6 +435,7 @@ BEGIN
428435
($desc, @cont) = $desc =~ /(.{1,$desc_wrap})(?: |\z)/g
429436
}
430437
}
438+
s/\s+\z// for $desc, @cont;
431439
printf $format, $type, $vtbl, $desc;
432440
printf $format, '', '', $_ foreach @cont;
433441
}

0 commit comments

Comments
 (0)