1

Thanks to some other great posts here (like this and that), I have several reliable methods to background a process in a Perl script.

However, in my environment (IIS6 on Windows Server 2003), it doesn't seem to completely background. Take this script:

use CGI::Pretty qw(:standard);
print header;
print start_html;
print "hi<br>";
system(1,qw(sleep 10));
print "bye";
print end_html;

The browser will stay "connected" for 10 seconds while the sleep process runs. "hi" and "bye" will both be printed on the screen, but the browser seems to be waiting for something more from the server (Firefox 4's and IE6's progress bars keep moving slowly for those 10 seconds).

I'd like the user (for my real app) to believe the process is fully back-grounded and be fully comfortable being able to close the webpage. With the progress bar still moving, I'm sure they'll sit and wait for it to stop before they close it or navigate away.

Just to be clear - the process appears backgrounded. I can close the web browser. The real background job continues to run (sometimes for up to an hour) with no issues. I'd just like to clean this up from the UI perspective.

Thanks!

jimtut
  • 2,366
  • 2
  • 16
  • 37

2 Answers2

1

Your problem is that the CGI program is done, but the webserver hasn't been told it is done, and is waiting for that before actually saying to the browser that it is done. You'll need to figure out how to communicate that, or else come up with a hack to work around the bug. Here are a few possible strategies.

  1. In Unix the right way to tell the webserver that it is time to go away is to daemonize the background process. The details of how to do this are pretty Unix specific. However Googling for Win32 perl daemon lead me to Win32::Daemon and then Win32::Service, which may help you do something similar for Windows. I've seen comments without details suggesting that this will work. If you need help figuring this out, I would suggest going to Perlmonks and asking there. There is more Perl specific expertise there than here.

  2. Suppose that you have some sort of batch job scheduler available to you. Then instead of launching a job, you can just schedule a task to be done. The task gets picked up and run. This strategy is particularly useful if the job that you need to run is intensive enough that you'd like it to run somewhere other than your webserver.

  3. Your CGI script can turn around and issue an http request to the webserver for the job to start, which it doesn't wait for. Here is a brief example showing you how to do that:

    #! /usr/bin/perl -w
    use strict;
    use Carp qw(croak);
    use Data::Dumper qw(Dumper);
    use LWP::UserAgent;
    
    my $ua = LWP::UserAgent->new();
    $ua->add_handler(response_header => sub {croak "complete"}, m_code => 200);
    my $response = $ua->get("http://www.perl.com/");
    
    if ($response->is_success) {
        print "Request backgrounded\n";
    }
    else {
        print "Uh, oh.\n";
    }
    
daxim
  • 39,270
  • 4
  • 65
  • 132
btilly
  • 43,296
  • 3
  • 59
  • 88
  • Thanks for the suggestions. Regarding the options: 1) too complex of a solution for this particular little UI "nit"; 2) definitely considering it, but we have enough Scheduled Tasks running on this server that I hated adding another one that's just polling for work to be done; 3) that's very interesting, almost like a 1-way web service - thanks! – jimtut Apr 04 '11 at 17:59
  • 1
    A service is really the only way you can background stuff on Windows, due to its process model which doesn't allow processes to be detached from and run independently of their parent. But we found it much easier to use the Windows resource kit tool instsrv.exe and srvany.exe, which make it fairly easy to set up a Perl script as a Windows service. We solved this problem using this approach, and daemonized an HTTP::Daemon script with a REST-like API to manage it. – Stuart Watt Jun 03 '11 at 21:10
  • @morungos: My option 3 would also work, though I freely admit that it is a kludge. The job is not truly backgrounded, but from the point of view of the web browser that is looking at it, it is. – btilly Jun 03 '11 at 22:49
0

CGI scripts are run with standard output redirected, either directly to the TCP socket or to a pipe. Typically, the connection won't close until the handle, and all copies of it, are closed. By default, the subprocess will inherit a copy of the handle.

There are two ways to prevent the connection from waiting on the subprocess. One is to prevent the subprocess from inheriting the handle, the other is for the subprocess to close its copy of the handle when it starts.

If the subprocess is in Perl, I think you could close the handle very simply:

close(STDOUT);

If you want to prevent the subprocess from inheriting the handle, you could use the SetHandleInformation function (if you have access to the Win32 API) or set bInheritHandles to FALSE in the call to CreateProcess. Alternatively, close the handle before launching the subprocess.

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • I tried using your close() idea in both the Perl CGI and the Perl subprocess. I'm afraid it didn't work, but I appreciate the suggestion. Sorry it took so long to try it out! – jimtut May 31 '12 at 16:31