Skip to content

Commit 2f920c2

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 98f6c10 commit 2f920c2

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

0 commit comments

Comments
 (0)