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

Hello,

I have a pretty big perl/tk application that uses HTTP connections and is cross-platform. I've tried many times to fork out the HTTP stuff so the GUI doesn't appear to hang while it waits for the HTTP request to come back, but Win32's limitations have until now caused major problems (cannot do the standard open(PIPE, "|-"), etc) ...

There's bigger problems here, too - Perl/Tk is not thread safe, and forking within a Tk application causes wierd errors. So I've got around this by forking at the start, before I use Tk;, and running a permanent child process with pipes to and from the parent.

So far, so good - the child recieves the http address to call from the parent, exits if this reads "exit" and otherwise fetches the http content. It then sends the content back to the parent, the parent reads this, and all is fine and dandy ...

... except I still end up with a blocked up GUI!!

I've tried using the standard method around this which is using fileevent() together with waitVariable(), but the catch here is that I need to read the content of the filehandle from the child in order to tell when to stop reading (i.e. - I send a signal from the child saying that all http content has been retrieved - since I can't close the child), and that's what blocks it all up! (if I don't do this everything runs as expected, but I can't get to the content since I never know when it's come!)

I need some way to check whether new data is available in the filehandle, without causing perl to wait until new data becomes available .... If anyone can think up a way around this, I'd really appreciate it!

Here's my test code...

#!/usr/bin/perl -w use strict; use LWP::UserAgent; use HTTP::Request; use IO::Handle; pipe(FROM_PARENT, TO_CHILD) or die "pipe: $!"; pipe(FROM_CHILD, TO_PARENT) or die "pipe: $!"; TO_CHILD->autoflush(1); TO_PARENT->autoflush(1); my $pid = fork; unless ($pid) { # Child process close FROM_CHILD; close TO_CHILD; die "cannot fork: $!" unless defined $pid; { chomp(my $address = <FROM_PARENT>); exit 0 if $address =~ /exit/; my $ua = LWP::UserAgent->new(); print "Child: getting $address ...\n"; # proxy server $ua->env_proxy(); my $req = HTTP::Request->new(GET => "$address"); my $response = $ua->request($req); my $http = $response->content; print "Child got the goods!\n"; print TO_PARENT $http,"\n"; print TO_PARENT "end_http_input\n"; redo; } exit 0; } # Parent process only ... use Tk; close FROM_PARENT; close TO_PARENT; my $top = MainWindow->new(-title=>"PerlPrimer v.test"); my $button = $top->Button(-text=>"try it!", -command=>\&do_it, )->pack(); my $quit = $top->Button(-text=>"Exit", -command=>sub { print TO_CHILD "exit\n"; exit 0; }, )->pack(); MainLoop(); sub do_it { # might as well put all these test http requests to good use . +.. :) my $address = "http://www.microsoft.com"; print TO_CHILD $address; my $unblock=0; my ($content, $data); $top->fileevent(\*FROM_CHILD, 'readable', sub { # This is where it hangs ... doesn't matter if I even go d +own to taking 1 byte at a time ... sysread(FROM_CHILD, $data, 32); $content.=$data; $unblock = 1 if $content =~ /end_http_input/; }); $top->waitVariable(\$unblock); $top->fileevent(\*FROM_CHILD, 'readable', ""); print "Parent: http content was $content\n"; print "out of loop ....\n"; }

BazB added readmore tags.

Replies are listed 'Best First'.
Re: Forking Win32 ...
by fizbin (Chaplain) on Feb 25, 2004 at 07:56 UTC
    I need some way to check whether new data is available in the filehandle, without causing perl to wait until new data becomes available .... If anyone can think up a way around this, I'd really appreciate it!
    Actually, that's exactly what Tk's "readable" fileEvent bit is supposed to be doing for you - it shouldn't get triggered until there's something to read.

    It sounds to me as though you're trying to do something very similar to Chapter 22 of O'Reilly's Mastering Perl/TK. (Where would copyright violation be without the Chinese?) I suggest that you look at that code and see what, if any, of it can be adapted/coerced to your purposes.

    Incidentally, I was going to mention the IO::Select package and how to use that (or the four argument form of the select builtin) to determine whether a filehandle has anything waiting to be read. However, Tk should be doing that internally already, so I'm not sure that gets you anything. (Still, I suppose it's worth trying at least once - there's an example of using it over here)

      Hi, thanks for your ideas ...

      I know that's what the readable fileevent should be doing for me, that's what's so frustrating! The problem seems to be that fileevent thinks the filehandle is readable as soon as the http request starts in the child, when there is nothing to read in the filehandle and may not be for some time, which then causes <>, sysread, whatever to pause. (Incidentally, it's not a case of the Child pausing all execution in the Parent - if I stop trying to read the filehandle (or try using IO::Select - see below) then the Parent is unblocked and fine) And then the filehandle doesn't become readable again, according to fileevent (so I can't even just ignore the first readable call! Although I'd really hate doing messy stuff like that anyway ....)

      I've already looked at the Mastering Perl/Tk section - the problem being that the author does not really have a solution: nothing he tries works on Win32, except using a third party module - and I'm specifically trying to avoid using any third party modules here. I actually think this is in some ways a neater solution anyway - setting up a dedicated child to grab http content, as opposed to forking every time you need to get data ... and it so almost works!!

      I also tried using IO::Select, but without much success - when I tied it in with fileevent (when fileevent thought that the filehandle was readable) I couldn't get it to ever report that the filehandle had readable data (I don't know why! I had it in an infinite loop within the fileevent call, broken only when it reads data, but it never claimed it could read anything) Maybe I'll give that another try ... I guess I could try ignoring fileevent and waitvariable entirely, and rely on IO::Select to do the work ... perhaps that's the best way forwards.

      Actually, I've just read a bit more and I now think what I need to do is make the filehandle non-blocking using fcntl (see the Tk::fileevent manpage) - activestate claims to have the fcntl module in their activeperl distro, so when I get to work I'll give that a try ...

        Well... The fcntl module is in the activeperl distro, but a non-blocking read is still impossible with that module on Win32, It just have some limitations :'(