From: Vincent Pit Date: Fri, 11 Sep 2009 22:57:41 +0000 (+0200) Subject: Reset the callbacks when the root interpreter is destroyed X-Git-Tag: v0.09~16 X-Git-Url: http://git.vpit.fr/?p=perl%2Fmodules%2FLexical-Types.git;a=commitdiff_plain;h=333c198120153b0cfe076fcb54e100dcf0eb9fb4 Reset the callbacks when the root interpreter is destroyed This fix segfaults when the pragma is first loaded from inside a thread. The global state for the root interpreter is also freed, so it won't leak anymore. --- diff --git a/MANIFEST b/MANIFEST index 3d8ae87..40ec6ea 100644 --- a/MANIFEST +++ b/MANIFEST @@ -20,6 +20,7 @@ t/21-tie.t t/22-magic.t t/23-magic-uvar.t t/30-threads.t +t/31-threads-teardown.t t/40-stress.t t/91-pod.t t/92-pod-coverage.t diff --git a/Types.xs b/Types.xs index ff9e398..3865214 100644 --- a/Types.xs +++ b/Types.xs @@ -648,6 +648,60 @@ STATIC OP *lt_ck_padsv(pTHX_ OP *o) { STATIC U32 lt_initialized = 0; +STATIC void lt_teardown(pTHX_ void *root) { + dMY_CXT; + + if (!lt_initialized) + return; + +#if LT_MULTIPLICITY + if (aTHX != root) + return; +#endif + +#if LT_THREADSAFE + ptable_hints_free(MY_CXT.tbl); +#endif + SvREFCNT_dec(MY_CXT.default_meth); + + PL_check[OP_PADANY] = MEMBER_TO_FPTR(lt_old_ck_padany); + lt_old_ck_padany = 0; + PL_check[OP_PADSV] = MEMBER_TO_FPTR(lt_old_ck_padsv); + lt_old_ck_padsv = 0; + + lt_initialized = 0; +} + +STATIC lt_setup(pTHX) { +#define lt_setup() lt_setup(aTHX) + if (lt_initialized) + return; + + MY_CXT_INIT; +#if LT_THREADSAFE + MY_CXT.tbl = ptable_new(); + MY_CXT.owner = aTHX; +#endif + MY_CXT.pp_padsv_saved = 0; + MY_CXT.default_meth = newSVpvn("TYPEDSCALAR", 11); + SvREADONLY_on(MY_CXT.default_meth); + + lt_old_ck_padany = PL_check[OP_PADANY]; + PL_check[OP_PADANY] = MEMBER_TO_FPTR(lt_ck_padany); + lt_old_ck_padsv = PL_check[OP_PADSV]; + PL_check[OP_PADSV] = MEMBER_TO_FPTR(lt_ck_padsv); + +#if LT_MULTIPLICITY + call_atexit(lt_teardown, aTHX); +#else + call_atexit(lt_teardown, NULL); +#endif + + lt_initialized = 1; +} + +STATIC U32 lt_booted = 0; + /* --- XS ------------------------------------------------------------------ */ MODULE = Lexical::Types PACKAGE = Lexical::Types @@ -656,18 +710,9 @@ PROTOTYPES: ENABLE BOOT: { - if (!lt_initialized++) { + if (!lt_booted++) { HV *stash; - MY_CXT_INIT; -#if LT_THREADSAFE - MY_CXT.tbl = ptable_new(); - MY_CXT.owner = aTHX; -#endif - MY_CXT.pp_padsv_saved = 0; - MY_CXT.default_meth = newSVpvn("TYPEDSCALAR", 11); - SvREADONLY_on(MY_CXT.default_meth); - lt_op_map = ptable_new(); #ifdef USE_ITHREADS MUTEX_INIT(<_op_map_mutex); @@ -675,14 +720,11 @@ BOOT: PERL_HASH(lt_hash, __PACKAGE__, __PACKAGE_LEN__); - lt_old_ck_padany = PL_check[OP_PADANY]; - PL_check[OP_PADANY] = MEMBER_TO_FPTR(lt_ck_padany); - lt_old_ck_padsv = PL_check[OP_PADSV]; - PL_check[OP_PADSV] = MEMBER_TO_FPTR(lt_ck_padsv); - stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1); newCONSTSUB(stash, "LT_THREADSAFE", newSVuv(LT_THREADSAFE)); } + + lt_setup(); } #if LT_THREADSAFE diff --git a/t/31-threads-teardown.t b/t/31-threads-teardown.t new file mode 100644 index 0000000..632293b --- /dev/null +++ b/t/31-threads-teardown.t @@ -0,0 +1,62 @@ +#!perl + +use strict; +use warnings; + +use Config qw/%Config/; + +BEGIN { + if (!$Config{useithreads}) { + require Test::More; + Test::More->import; + plan(skip_all => 'This perl wasn\'t built to support threads'); + } +} + +use threads; + +use Test::More; + +BEGIN { + require Lexical::Types; + if (Lexical::Types::LT_THREADSAFE()) { + plan tests => 1; + defined and diag "Using threads $_" for $threads::VERSION; + } else { + plan skip_all => 'This Lexical::Types isn\'t thread safe'; + } +} + +sub run_perl { + my $code = shift; + + local %ENV; + system { $^X } $^X, '-T', map("-I$_", @INC), '-e', $code; +} + +SKIP: +{ + skip 'Fails on 5.8.2 and lower' => 1 if $] <= 5.008002; + + my $status = run_perl <<' RUN'; + { package IntX; package IntY; package IntZ; } + my ($code, @expected); + sub cb { + my $e = shift(@expected) || q{DUMMY}; + --$code if $_[0] eq $e; + () + } + use threads; + $code = threads->create(sub { + $code = @expected = qw/IntX/; + eval q{use Lexical::Types as => \&cb; my IntX $x;}; die if $@; + return $code; + })->join; + $code += @expected = qw/IntZ/; + eval q{my IntY $y;}; die if $@; + eval q{use Lexical::Types as => \&cb; my IntZ $z;}; die if $@; + $code += 256 if $code < 0; + exit $code; + RUN + is $status, 0, 'loading the pragma in a thread and using it outside doesn\'t segfault'; +}