in reply to Re^3: semi-panic: attempt to dup freed string?
in thread semi-panic: attempt to dup freed string?

Hypothesis: SVs are owned by the tread in which they were created. When a thread exits, any SVs it owns are freed.

Nice hypothesis!++

By the way, using a string buffer to store a pointer is a waste. An IV is guaranteed to be big enough. Or you can use the PV itself (using SvPV_set) if you set LEN to zero.

Indeed. Or, as RVs and IVs are now aliases for the same thing, skip the intermediary completely, which appears to fix the problems Update: No it doesn't. I was so happy to see the back of the panic, that I missed that the contained value is updated by the thread :(

#! perl -slw use strict; package O; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'END_C', NAME => 'xso', CLEAN_AFTER_BUILD => 0; typedef struct { SV**sv; } O; void *mem( size_t size ) { void *p; Newx( p, size, char ); return p; } SV *new( char *package ) { O *o = (O*)mem( sizeof( O ) ); SV *rv = newRV( o ); o->sv = mem( sizeof( SV* ) ); sv_bless( rv, gv_stashpv( package, 0 ) ); SvREADONLY_on( rv ); printf( "N:rv:%p o:%p\n", rv, o ); return rv; } int set( SV *rv, SV *in ) { O *o = *(O**)SvRV( rv ); printf( "S:rv;%p o:%p\n", rv, o ); o->sv = newSVsv( in ); return 1; } SV *get( SV *rv ) { O *o = *(O**)SvRV( rv ); SV *old = newSVsv( o->sv ); SvREFCNT_inc( old ); printf( "G:rv;%p o:%p old:%p\n", rv, o, old ); return old; } void DESTROY( SV *rv ) { printf( "DESTROY:%s\n", SvPV_nolen( rv ) ); } void CLONE( SV *rv ) { printf( "CLONE:%s\n", SvPV_nolen( rv ) ); } END_C package main; use threads; use Devel::Peek; my $o = O->new(); print $o; $o->set( "abcde" ); print $o->get(); print "\nthreaded\n"; async { $o->set( "12345" ); }->join; print $o->get(); __END__ C:\test>xso N:rv:000000000025E128 o:00000000040D6738 O=SCALAR(0x40d6738) S:rv;000000000025E248 o:0000000003CB6088 G:rv;000000000025E248 o:0000000003CB6088 old:00000000002B7980 abcde threaded CLONE:O S:rv;0000000004361010 o:000000000432F5F8 DESTROY:O=SCALAR(0x4361028) G:rv;000000000025E248 o:0000000003CB6088 old:00000000002B79E0 abcde DESTROY:O=SCALAR(0x40d6738)

Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

Replies are listed 'Best First'.
Re^5: semi-panic: attempt to dup freed string?
by ikegami (Patriarch) on Mar 24, 2011 at 22:40 UTC

    I'm guessing you're trying to avoid the overhead of :shared magic? Are you trying to share scalar between two threads or just transfer a scalar from one thread to another? Does it have to be a scalar, or can it just be a string?

      I'm looking to share a blessed reference to any arbitrary lump of memory--the struct in this example.

      Skipping the intermediate SV, ie. RV->struct rather than RV->SvUV->struct seemed like a good idea as it avoided the subject error, but it doesn't work because when the RV is cloned, Perl assumes it's value is a SV* and clones that "SV" also, which screws things completely.

      Using RV->SvUV->struct recreates the original problem.

      I think that I should be able to prevent the SvUV being destroyed prematurely by having it's refcount incremented by the cloning process and decremented by the DESTROY method. The problem is that the CLONE method is called as a class method, not an instance method making it impossible to take full control of the cloning method.

      I don;t currently see a way around this?


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        I'm looking to share a blessed reference to any arbitrary lump of memory--the struct in this example.

        That's not very clear. There is no shared reference, and the blessed scalar contains a string, not a reference.

        SV = IV(0x982f560) at 0x982f564 REFCNT = 1 FLAGS = (PADMY,ROK) RV = 0x9814874 SV = PVMG(0x9863320) at 0x9814874 REFCNT = 2 FLAGS = (OBJECT,POK,READONLY,pPOK) IV = 0 NV = 0 PV = 0x982a504 "T&\316\t"\0 CUR = 4 LEN = 8 STASH = 0x982f534 "O"

        I'm going to assume you meant "I'm looking to share an arbitrary lump of memory--the struct in this example."

        You already succeeded in doing that. «o» in both threads and thus «*o» is the same too.

        int set( SV *rv, SV *in ) { O *o = *(O**)SvPV( SvRV( rv ), PL_na ); o->sv = newSVsv( in ); printf( "S:rv;%p o:%p o->sv:%p\n", rv, o, o->sv ); return 1; } SV *get( SV *rv ) { O *o = *(O**)SvPV( SvRV( rv ), PL_na ); printf( "G:rv;%p o:%p o->sv:%p\n", rv, o, o->sv ); return newSVsv( o->sv ); } S:rv;0x8d1c9f4 o:0x87736f4 o->sv:0x8d1d374 G:rv;0x8641564 o:0x87736f4 o->sv:0x8d1d374

        The problem isn't with sharing an arbitrary lump of memory. The problem is you're trying to share something you don't own and gets destroyed by the owner (the thread's Perl interpreter) when it exits.

        I think that I should be able to prevent the SvUV being destroyed prematurely by having it's refcount incremented

        Reference counts don't matter in global destruction. Well, it might affect the order in which scalars get destroyed, but it won't prevent it from getting destroyed.

        If you were to create the SV in the receiver, then it would work.

        Update: Minor touch-ups for clarity.

        Since you cannot change the ownership of an SV from one interpreter to another, this creates the SV in the receiver.

        #! perl -slw use strict; package O; #use Inline qw( FORCE NOISY NOCLEAN ); use Inline C => <<'END_C', NAME => 'xso'; typedef struct { SV *sv; } O; void *mem( size_t size ) { void *p; Newx( p, size, char ); return p; } SV *new( char *package ) { O *o = (O*)mem( sizeof( O ) ); SV *oh = newSVuv( (UV)o ); SV *rv = newRV( oh ); o->sv = newSV(0); sv_bless( rv, gv_stashpv( package, 0 ) ); SvREADONLY_on( oh ); SvREADONLY_on( rv ); printf( "Creating oh:%p o:%p o->sv:%p\n", oh, o, o->sv ); return rv; } int set( SV *rv, SV *in ) { SV *oh = SvRV( rv ); O *o = (O*)SvUV( oh ); sv_setsv( o->sv, in ); printf( "Setting oh:%p o:%p o->sv:%p\n", oh, o, o->sv ); return 1; } SV *get( SV *rv ) { SV *oh = SvRV( rv ); O *o = (O*)SvUV( oh ); printf( "Getting from oh:%p o:%p o->sv:%p\n", oh, o, o->sv ); return newSVsv( o->sv ); } void DESTROY( SV *rv ) { SV *oh = SvRV( rv ); O *o = (O*)SvUV( oh ); printf( "Destroying oh:%p o:%p o->sv:%p\n", oh, o, o->sv ); /* TODO: We can only destroy oh and o in the original. SvREFCNT_dec( o->sv ); free(o); */ } END_C package main; use threads; use Devel::Peek; my $o = O->new(); $o->set( "abcde" ); print $o->get(); print "\nthreaded\n"; async { $o->set( "12345" ); }->join; #<>; print $o->get(); #<>;

        The problem is that the CLONE method is called as a class method,

        I think you have to use magic.