mrakus has asked for the wisdom of the Perl Monks concerning the following question:

I have a problem. I cant print to fastcgi output stream in the Inline::C function. When i use puts("TEXT") its print to STDOUT, not fastcgi stream. I think i need to setup parameter $out in function "my_print", but i can't understand how to use this parameter in C function? ( i mark it as "???") Do you have ideas?
use strict; use IO::Handle; use CGI::Fast; use FCGI::ProcManager; use POSIX qw(setsid); use Inline (Config => DIRECTORY => '/tmp/Inline', ); use Inline 'C'; my $PROCESSES = 10; my $SOCKET = "localhost:8000"; &main; sub main { my $proc_manager = new FCGI::ProcManager({ n_processes => $PRO +CESSES}); my $socket = FCGI::OpenSocket($SOCKET, 100); my $request = FCGI::Request(\*STDIN, \*STDOUT, \*STDERR, \%ENV +, $socket); $proc_manager->pm_manage(); while($request->Accept() >= 0){ $proc_manager->pm_pre_dispatch(); my $cgi = CGI::Fast->new(); print $out "Content-type: text/html\r\n\r\n"; my_print_c(); $proc_manager->pm_post_dispatch(); } FCGI::CloseSocket($socket); } __END__ __C__ void my_print_c() { char *text = "HELLO WORLD!"; GV* const gv = PL_defoutgv; IO* io; MAGIC* mg; int success; SV* sv; sv = sv_2mortal(newSVpv(text,0)); io = GvIO(gv); if (!io && GvEGV(gv)) { io = GvIO(GvEGV(gv)); if (!io) { return FALSE; } } mg = SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar); dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(SvTIED_obj((SV*)io, mg)); XPUSHs(sv); PUTBACK; call_method("PRINT", G_SCALAR); SPAGAIN; success = 0 != POPi; PUTBACK; FREETMPS; LEAVE; return success; }
Thanks to you ,ikegami. I wrote C function my_print_c() with your help! But i understand that in this function we emulate perl "print". I glad to see that this C function is more fast that perl... but i think if we'll find method to output C char * to FastCGI stream it will be more faster.

Replies are listed 'Best First'.
Re: Inline C + IO::Handle
by ikegami (Patriarch) on Apr 15, 2009 at 13:53 UTC
    If you wan to use Perl's IO system, you'll have to use Perl's IO API
      I understand it but could you help me more? :) I try this:
      ... __END__ __C__ void my_print(FILE *f) { fputs("TEXT",f); }
      But its not work too :( Do you have some simple code for me? :)
        Don't use puts/fputs. clib doesn't know anything about Perl. Use the functions such as PerlIO_puts in the linked document instead.
Re: Inline C + IO::Handle
by almut (Canon) on Apr 15, 2009 at 19:05 UTC

    The problem with FCGI is that it uses tied handles. As a quick check of the source reveals, it attaches "magic" to the filehandles in order to bind read/write calls, etc. to its own stream implementation (underneath PerlIO) — see NewStream() in fcgiapp.c, and FCGI_Bind() in FCGI.XL, if interested.

    In other words, the simple approach specifying PerlIO * fh in combination with PerlIO_puts() doesn't work here, as it fails to resolve the associated magic.  For example, the following piece of code doesn't work for essentially the same reasons:

    #!/usr/bin/perl use IO::Handle; use Inline (Config => DIRECTORY => '/tmp/Inline', ); use Inline 'C'; my $out = new IO::Handle(); tie *$out, "MyFH"; print $out "Content-type: text/html\r\n\r\n"; my_print($out); package MyFH; sub TIEHANDLE { my $class = shift; open my $fh, ">", "test.out" or die $!; bless $fh, $class; } sub PRINT { my $self = shift; print $self @_; } __END__ __C__ void my_print(PerlIO *fh) { PerlIO_puts(fh, "TEXT"); }

    While the "Content-type..." line ends up in "test.out" as expected, the string "TEXT" does not.  OTOH, with a normal filehandle as $out, everything works fine (as already shown by oshalla).

    Unfortunately, I don't have the time at the moment to work out the details of how to properly handle that magic on the XS side (tied filehandles are kinda special beasts, with hardly any documentation available besides the sources...) — in particular as this would entail not only figuring out how to do it in XS, but also how to then have Inline::C auto-generate the required code...

    If someone else feels like tackling this, please go ahead, and let us know what you come up with :)

      doh! I suspected they were tied handles, but I figured PerlIO_* would handle that. Otherwise, you end up with the following:
      /* Returns TRUE on success. Sets errno on error. */ int my_print(SV* sv) { GV* const gv = PL_defoutgv; IO* io; MAGIC* mg; PerlIO* fp; int success; const char* buf; STRLEN to_write; io = GvIO(gv); if (!io && GvEGV(gv)) { io = GvIO(GvEGV(gv)); } if (!io) { return FALSE; } mg = SvTIED_mg((SV*)io, PERL_MAGIC_tiedscalar); if (mg) { dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(SvTIED_obj((SV*)io, mg)); XPUSHs(sv); PUTBACK; call_method("PRINT", G_SCALAR); SPAGAIN; success = 0 != POPi; PUTBACK; FREETMPS; LEAVE; return success; } fp = IoOFP(io); if (!fp) { return FALSE; } buf = SvPV(sv, to_write); while (to_write > 0) { int written = PerlIO_write(fp, buf, to_write); if (written < 0) { return FALSE; } buf += written; to_write -= written; } return TRUE; }

      (Untested. Based in part on code from pp_print, the function that implements the print operator.)

      That writes the the currently selected handle. It shouldn't need much work to work with any handle. Why isn't this already supplied by Perl? Or did I overlook it?

      Update: Arg! It's not complete. There are encoding issues.

      If s is assumed to be bytes,

      • If PerlIO_isutf8(fp) is true, s needs to be converted to UTF-8 (i.e. perform an utf8::upgrade).

      If s is assumed to be UTF-8,

      • If PerlIO_isutf8(fp) is false and the bytes pragma isn't in effect, s needs to be converted to iso-8859-1 (i.e perform an utf8::downgrade).

      Does calling CORE::print work from XS? That would be simpler! If it doesn't work, it would still be simpler to write a Perl wrapper for print and call that.

      Update: Fixed compilation errors. Didn't address the problem mentioned above.

      If someone else feels like tackling this, please go ahead, and let us know what you come up with :)

      Your demo script works for me as is, if you change the XSub (my_print) to:
      void my_print(SV * fh) { dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(sv_2mortal(fh)); XPUSHs(sv_2mortal(newSVpv("TEXT", 0))); PUTBACK; call_pv("MyFH::PRINT", G_DISCARD); FREETMPS; LEAVE; }
      Update: Changed PUSHs(fh) to XPUSHs(sv_2mortal(fh)). (Still sems to do the required task.)

      This can quite possibly be improved (there are a few things that I ought to check regarding sins that are perhaps being committed) ... but, given the convoluted nature of what it does, I'm not even sure that it's helpful.

      Cheers,
      Rob
      ikegami: I can't compile your C code with my perl code :(
        I fixed the compilation errors. But like I said, it would make more sense to actually call print:
        package MyModule; use Inline Config => ( DIRECTORY => './Inline', ); use Inline 'C'; sub _fprint { print { shift } @_ } # ... 1; __END__ __C__ int my_print(SV* fh, SV* sv) { int result; dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(fh); XPUSHs(sv); PUTBACK; call_pv("MyModule::_fprint", G_SCALAR); SPAGAIN; result = POPi; PUTBACK; FREETMPS; LEAVE; return result; }