in reply to Re: Inline C: using stderr segfaults?
in thread Inline C: using stderr segfaults?

Good call++ This is what the call fprintf( stderr, "Got:'%s'\n", text ); gets translated into:

fprintf( ( *( *Perl_IStdIO_ptr( ( ( PerlInterpreter *)Perl_get_context() ) ) )->pStderr ) ( ( *Perl_IStdIO_ptr( ( ( PerlInterpreter* ) Perl_get_context() ) ) ) ), "Get:'%s'\n", text );

And if you can make sense of that your a better man that I.

It's no wonder function calls are slow in perl. Some single lines of XS code end up calling Perl_get_context() 7 or 8 times. There has to be a better way.


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.
"Too many [] have been sedated by an oppressive environment of political correctness and risk aversion."

Replies are listed 'Best First'.
Re^3: Inline C: using stderr segfaults?
by salva (Canon) on Nov 05, 2007 at 23:16 UTC
    And if you can make sense of that...

    The only explanation I can find to that code is that

    (*Perl_IStdIO_ptr(((PerlInterpreter *)Perl_get_context())))->pStderr
    returns a function pointer, and the next parenthesized expresion,
    ((*Perl_IStdIO_ptr((( PerlInterpreter* ) Perl_get_context()))))
    are the arguments for calling that function.

    It's no wonder function calls are slow in perl. Some single lines of XS code end up calling Perl_get_context() 7 or 8 times. There has to be a better way

    Well, yes, there is a better way!... at least for XS, I don't know about Inline::C, though probably not.

    You have to start your XS files defining the macro ...

    #define PERL_NO_GET_CONTEXT 1
    ... and then pass the Perl context manually from function to function using pTHX, aTHX, pTHX_ and aTHX_. See perlguts for the details.
      Well, yes, there is a better way!...You have to start your XS files defining the macro ... #define PERL_NO_GET_CONTEXT 1

      Interesting. Tracking this through, which is no easy task, this is the path taken:

      1. I use stderr
      2. which as we saw, gets translated into
        (*Perl_IStdIO_ptr(((PerlInterpreter *)Perl_get_context()))) ->pStderr((*Perl_IStdIO_ptr((( PerlInterpreter* ) Perl_get_context())) +))
      3. Perl_get_context() translates into
        Perl_get_context(void) { DWORD err = GetLastError(); void *result = TlsGetValue(PL_thr_key); SetLastError(err); return result; }

        Which looks up a thread-specific dispatch table base address, indexes into it to find the function (at offset pStderr) that returns the address of the stderr FILE* struct, and then invokes it (passing the address of the dispatch table as it's only argument).

      4. The function looked up is
        FILE* PerlStdIOStderr(struct IPerlStdIO* piPerl) { return win32_stderr(); }

        Note that the second call to Perl_get_context() is wasted anyway because it is not used.

      5. So then we call the platform specific function to return the required FILE * and we find:
        DllExport FILE * win32_stderr(void) { return (stderr); }
      6. And that, after preprocessing ends up as
        win32_stderr(void){ return ((&_iob[2])); }

      So, all of that to get the address of the third entry of the global IO block table.

      Now the questions you have to ask yourself are:

      • Will that address ever change?

        Under the influence of threads? On any platform?

      • How many other, similarly tortuous routes are there to obtain simple values?
      • Why have the dispatch table (a runtime entity), go through platform independent functions in order to get to the platform specific functions?

        By the time the dispatch table is used, we are platform specific, so why not assign the platform specific function addresses directly into the table?

      • Why have thread-invariant values looked up through a thread-specific dispatch table?
      • Why look up the thread context all the time?

        Why not use the fact that on any platform, a thread context switch will save & restore the thread-specific register contents. Have a platform specific assembler hack that loads a known register with the thread-specific context address (called when a thread is spawned) and allow the OS to manage the context switching directly.

        Thread context references then become a macro to indirect through that known register.

      Like I say, there has to be a better way.


      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.
      Well, yes, there is a better way!... at least for XS, I don't know about Inline::C, though probably not

      Not that I've applied the "better way" (in either XS or Inline::C), but I would expect that Inline::C *does* lend itself to this improvement.

      There's certainly no problem in Inline::C as regards defining macros - nor would I expect any problem with using pTHX, aTHX, pTHX_ and aTHX_. (But I don't know for sure :-)

      Cheers,
      Rob