in reply to Re^5: Perl crash during perl_clone
in thread Perl crash during perl_clone

I expected the thread-ids to be different by virtue of my design. Anycase, I logged the 'current' thread-id and confirmed that the id when I get the perl-callback address (in the SWIG wrapper) is different (it must be a thread created by perl.exe) from when the perl cback is invoked (the thread I created). No surprise.

However, I got thinking about what you mean by the 'right' interpreter..if I have two interpreters then it is not clear what is the 'right' interpreter. Can you elaborate which you think is the right interpreter?

Then I decided to question the premise/info under which I went to the cloning method and did an experiment: I removed the perl_clone completely (and the related SvSHARE, sv_dup etc). Instead, when my "BGThread-equivalent" is created, I just forced a PERL_SET_CONTEXT() to the interpreter created by perl.exe

This forces the callback thread to invoke the perl sub in the same interpreter context as that created by perl.exe itself.

In my limited tests, very surprisngly for me, so far that seems to work as well as the perl_clone approach I used for almost a yr now.

I'll be testing more, but does this surprise you?

Replies are listed 'Best First'.
Re^7: Perl crash during perl_clone
by BrowserUk (Patriarch) on Oct 28, 2010 at 12:31 UTC
    Can you elaborate which you think is the right interpreter?

    The right interpreter, is whichever one the perl callback address originated in. That is, if your perl code--running in your main (perl.exe) thread, is the one that passes the address of a perl sub into your C code, then that is the right interpreter.

    Because, whilst call code and data--perl or C--is accessible to every thread at the C level, at the Perl source level, perl relies upon segregating memory and code on a per interpreter/thread basis. It does this for both internal reasons, and for those of simplifying the use of threads by imposing this "restricted view" on the programmer. That is the raison d'etre of the iThread model.

    To restate that another way; the reason Perl won't allow you to pass a coderef from one thread to another via shared memory, is because allowing you to do so would require imposing even greater performance penalties upon threads than currently. It would be necessary to apply internal locking to every perl subroutine, because they might be called concurrently on two or more threads.

    The approach iThreads uses to preventing both the programmer discipline that would be required were coderefs shareable (see the :lock keyword in the 5005 threads model), and the additional internal locking that would be required, is to require each interpreter/thread to only use subroutines compiled within that interpreter.

    Your use of C/XS is bypassing that protection mechanism--hence the crashes.

    I just forced a PERL_SET_CONTEXT() to the interpreter created by perl.exe.... but does this surprise you?

    No. By forcing the same context as was in use when the coderef was taken, you ensure that the code and data (closures etc.) behind that coderef is accessible. I would anticipate that, so long as your program remains single-threaded, that using this method to force the "correct context" for the callback will work reliably.

    If you wanted to ensure multi-threaded compatibility--then you can extend this mechanism by:

    • When your C/XS code receives the callback address from Perl, take a copy of the current context and save it away in a suitable place.
    • Later, when the triggering event occurs, save a copy of the then current context in a local var;
    • do a set_context() using the context you saved earlier;
    • call the coderef.
    • when the coderef returns; reset the current context.

    Note: All of the above is untested--by me; and probably by anyone else--so you are the pathfinder here. I'll willingly try and help with any problems that arise, but please understand that at this point, the only test-bed I have is my brain. And that is a poor substitute for a modern processor :)

    As an (interesting to me but probably not to you at this point) aside, this discussion leads to the possibility that could further reduce the overheads of the iThreads model. If coderefs explicitly carried their compile-time contexts with them, then it might be possible to allow a single copy of a Perl coderef--opcode tree--to be safely shared by multiple interpreters. But that's a thought to explore another time.


    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 not following completely..so, I'm glad you are able to talk me through it. Also, I'll have to try the "caching the context" idea tomorrow.

      When your C/XS code receives the callback address from Perl, take a copy of the *current* context and save it away in a suitable place.

      At this point, my script is just a single line of execution (no ithreads or anything else), except for the subs that are the callacks. So, can there still be more than one context when I get the callback address in XS?

      With the "force context" solution, I encountered a crash (actually a "Not a CODE ref" assert) in a scenario when a 2nd callback was called from a 2nd thread. I was puzzled at that..but, perhaps perl.exe has more than one 'context' by default? Or perhaps its because I didnt "save" & "restore" contexts after the 1st callback that the context for the 2nd is messed up?

        Maybe this will explain the save&restore and context caching mechanism better than I've achieved using words:

        #! perl -slw use strict; use Inline C => Config => BUILD_NOISY => 1; use Inline C => <<'END_C', NAME => '_867652', CLEAN_AFTER_BUILD => 0; PerlInterpreter *saved; SV *callback; VOID CALLBACK cbProc( DWORD time ) { PerlInterpreter *temp = Perl_get_context(); printf( "C-Callback: %u\n", time ); PERL_SET_CONTEXT( saved ); { dSP; ENTER; SAVETMPS; PUSHMARK( SP ); XPUSHs( sv_2mortal( newSVuv( (UV)time ) ) ); PUTBACK; call_sv( callback, G_DISCARD ); FREETMPS; LEAVE; } PERL_SET_CONTEXT( temp ); return; } void __cdecl thread( VOID *arg ) { while( Sleep( 5000 ), 1 ) { printf( "Here\n" ); cbProc( GetTickCount() ); } } void setCallback( SV* cv ) { dTHX; saved = Perl_get_context(); callback = cv; // Start callback timer thread _beginthread( &thread, 0, NULL ); return; } END_C $|++; sub callback { print "Timer value is: $_[0]"; return; } setCallback( 'callback' ); sleep 100; __END__ C:\test>867652 Here C-Callback: 1355573480 Timer value is: 1355573480 Here C-Callback: 1355578472 Timer value is: 1355578472 Here C-Callback: 1355583479 Timer value is: 1355583479 Terminating on signal SIGINT(2)

        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.