We help IT Professionals succeed at work.

Pause during HTML generation gives odd results

241 Views
Last Modified: 2012-05-11
Hi Experts,

For technical reasons I may not have to go into, I'm pausing for 5 secs, part way through generating HTML, with Perl.  Here's a very simplied version of the code, which demonstrates the problem I'm having:
#!/usr/bin/perl

$| = 1;  # Turn off buffered output

print <<EOF;
Content-Type: text/html; charset=utf-8\n
<html>
<body>
  Before
EOF

sleep 4;

print <<EOF;
  After
</body>
</html>
EOF

Open in new window

When viewed in a browser, I would have expected that the above code would have done this:
- "Before" appears.
- A 4 sec pause.
- " After" appears.
And that's almost what happens, except the pause happens first, instead, regardless of whether I have the non-buffering line ("$| = 1;") in place.

Questions:

1. Why does the pause occur after all the text appears, instead of between the 2 words?

When I change the sleep to be 5 secs, I get even more unexpected results, as described in the following questions:

2. If the non-buffering line is included, the result is:
- A 5 sec pause.
- Then "Before Before" appears (not "Before After").
If I view Page Source in Firefox, it looks like this:
    <html>
    <body>
    Before
    <html>
    <body>
    Before

Why does this happen?

3. When I remove the non-buffering line, do I get this error:
    Internal Server Error
    The server encountered an internal error or misconfiguration and was unable to complete your request.
    Please contact the server administrator, webmaster@mydomain.com and inform them of the time the error occurred, and anything you might have done that may have caused the error.
    More information about this error may be available in the server error log.
    Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.
    Apache/2.0.63 (Unix) mod_ssl/2.0.63 OpenSSL/0.9.7a DAV/2 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635 Server at crs.tospeirs.net Port 80

Why does this happen?

4. How can I get around the above problems, with a 5 sec sleep in place?

The web server is run by a commerical hosting company, so I'm limited as to what changes I can make to the environment.  The control panel is cPanel.

Thanks.
Comment
Watch Question

CERTIFIED EXPERT
Commented:
Unlock this solution and get a sample of our free trial.
(No credit card required)
UNLOCK SOLUTION
CERTIFIED EXPERT

Author

Commented:
Thanks for your advice, wilcoxon.

Here are my responses:

1. OK - thanks.

2. My feelings exactly.  And furthermore, if I move the "sleep 5;" to the end of the script, leaving the "$| =1;" in tact, I get this output:
   Before After Before After
And the Page Source looks like this:
    <html>
   <body>
    Before
    After
    </body>
    </html>
    <html>
    <body>
    Before
    After
    </body>
    </html>

Anyone got any ideas on why this would happen?

3. Me neither.  I've added your suggested code to the beginning, i.e.:
    use strict;
    use warnings;
    use CGI::Carp qw(fatalsToBrowser);
(which I have used in some scripts before, but hadn't bothered here), and I get exactly the same "Internal Server Error..." message.  I checked the log, as the error suggests, but that just says:
   [Sat Apr 23 00:30:59 2011] [client xxx.xx.xxx.xxx] File does not exist: /home/myname/public_html/mydir/500.shtml

4. Oh.  Is this an Apache configuration change?

My current thoughts are, I'm going to have to avoid the pause by changing my current strategy.
Unless...  Does Perl have a way of running a subroutine as a background process, or do I need to put the code in a separate script and run that in the background?  The OS is Linux, so I assume I could do something like:
    `script2.pl&`;
if I had to, but passing values would be easier if it wasn't an external script.  If I put the pause at the beginning of the subroutine I'm calling, and run that in the background, then the rest of the script won't be slowed down.  The pause seems to be required to allow an external process to keep up with my script's processing.

Thanks.
Tel2
CERTIFIED EXPERT

Author

Commented:
Hi again wilcoxon,

I tried the same kind of thing on different webhosts (OpenHost and HD, for my own reference), and it worked fine with them.  My test was with and without the "$| = 1;" line, and with sleeps of at least 50 secs.

I'm not planning to change webhost just for this though.  Any comments on my previous post, including the background processing option?

Or is there a way to get the output to go to the browser, while the script keeps doing a bit more processing (a bit like having the sleep at the end, if you see my update to question 2, in my last post).

Thanks.
CERTIFIED EXPERT
Commented:
Unlock this solution and get a sample of our free trial.
(No credit card required)
UNLOCK SOLUTION
CERTIFIED EXPERT

Author

Commented:
Hi wilcoxon,

Thanks for your reply, and sorry for the delay in getting back to you.

Yes, I was considering taking that up with my webhost, and I finally did that yesterday, and they have "increased the Timeout value for scripts execution in Apache configuration".  Looks as if they've probably changed it from 5 to 30 secs, which is fine.

However, the above strategy still leaves my user waiting extra time for the sleep to complete, so it's less than ideal, and I'm interested in trying that Perl fork you've suggested, for running the sleep, etc, in the background, but had a look at the doco, and you're right - it's confusing, and hasn't got many examples, either.  Do you know how I could just run this sub in the background?
mysub('add', '1,2,3', 'a@bc.com', 'Tel2');  # Call it

sub mysub
{
  my ($action, $lists, $emails, $name) = @_;
  sleep 2;
  ...etc...
}

Open in new window


Thanks.
CERTIFIED EXPERT
Commented:
Unlock this solution and get a sample of our free trial.
(No credit card required)
UNLOCK SOLUTION
CERTIFIED EXPERT

Author

Commented:
Thanks wilcoxon,

So does the "fork" command run everything in the subsequent {block} in the background?

> Before the end of your parent code, you probably want to do a wait.  Otherwise, you can end up with zombie processes.

Wouldn't waiting defeat my purpose of forking?  I'm trying to make the parent code finish quickly, so it returns the webpage to the user, which is why I've moved the sleep to the subroutine, which will be run by the child process.  Are you with me?  Any suggestions?

Thanks.
CERTIFIED EXPERT

Commented:
You can try the %SIG trick mentioned in the fork docs and not do a wait (but it's noted to only work on some systems).

Making the wait the last command in the script after it's already done all of the output for the web page should also work.  This should give the user the web page and let them do other things while the script is waiting.  This definitely works for straight CGI - I assume it works for mod_perl but I'm not positive.
CERTIFIED EXPERT

Author

Commented:
Hi wilcoxon,

Thanks for your comment about fork.

> "...this definitely works for straight CGI"
Just before raising this post, I tried having the sleep even at the very end of the script, and it strangely didn't make any difference to how long the web page took to appear.  So looking at the script I originally posted, I get the same delay behaviour whether I move the sleep to the beginning, the middle (where it is in that post), or the end.  I just tested it again now, on 3 different webhosts, and 2 different browsers (Firefox 3 & IE 6), and the same delay happens.  Are you saying it works differently for you?

Here's the code, to make sure we're on the same page:
#!/usr/bin/perl

use strict;
use warnings;
use CGI::Carp qw(fatalsToBrowser);

$| =1;

print <<EOF;
Content-Type: text/html; charset=utf-8\n
<html>
<body>
Before
EOF

print <<EOF;
After
</body>
</html>
EOF

sleep 5;

Open in new window

Thanks.
CERTIFIED EXPERT

Commented:
I guess I haven't tried in a long time.  It certainly used to work that way.  I don't have a personal web server right now to test on.  I wonder if that behavior has to do with Apache (and likely other web servers) doing things to speed up CGI scripts?
CERTIFIED EXPERT

Author

Commented:
Thanks for all your effort with this, wilcoxon.

Good to have you on the EE team!
Unlock the solution to this question.
Thanks for using Experts Exchange.

Please provide your email to receive a sample view!

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.