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

Hello Venerable Monks,

I want to start off this post by sincerely thanking you guys here. Always there with some guidance or direction, a code snippet or even just the proper terminology to google. Without you guys I would have given up my hackish style of programming and kicked in the door of the local computer store, postal style long ago.

Now having bared my soul I once again come looking for your wise guidance. As the title says fork windows as far as threading is concerned. I am trying to fork or pre-fork a little webserver to serve as a local front-end for my applications. Worked great until I started adding images and some of the connections started getting lost.

So I asked for help and was told that forking was what I was looking for. So I started to dig and found this page that lays out very nicely how to do exactly what I want.. on linux (or any other POSIX system). http://www.petercooper.co.uk/archives/000702.html

So I went back digging and found a great set of tools from http://www.cygwin.com/ This solved my POSIX::setsid(); problem by replacing that line with system('setsid ' . $pid); and adding setsid.exe to the environment path.

Now we come to the part were I'm unsure were to go. I'm now getting the error FATAL! POSIX::sigemptyset not implemented on this architecture at http.pl line 99. Line 99 is my $sigset = POSIX::SigSet->new(); and I can't for the life of me find a windows executable to replace the functionality like I did with setsid.

A point in the right direction to get me back on my feet and dusted off would be amazing right now. Very new to threads and it turns out the OS I'm stuck with sucks. Full code of the webserver below:
use strict; $SIG{'PIPE'} = 'IGNORE'; use HTTP::Daemon; use POSIX; # Number of child processes to keep going my $totalChildren = 5; # Number of requests each child handles before dying my $childLifetime = 100; my $logFile = "C:\\rrships\\daemon.log"; my %children; my $children = 0; &daemonize; # Create an HTTP daemon my $d = HTTP::Daemon->new( LocalPort => 80, LocalAddr => 'localhost', +Reuse => 1, Timeout => 180, Proto => 'tcp' ) || die "Cannot create socket: $!\n"; # Log the URL used to access our daemon logMessage ("master is ", $d->url); &spawnChildren; &keepTicking; sub daemonize { my $pid = fork; defined ($pid) or die "Cannot star +t daemon: $!"; # If we're the shell-called process, # let the user know the daemon is now running. print "Parent daemon running.\n" if $pid; # If we're the shell-called process, exit back. exit if $pid; # Now we're a daemonized parent process! # Detach from the shell entirely by setting our own # session and making our own process group # as well as closing any standard open filehandles. # POSIX::setsid(); system('setsid ' . $pid); print '1'; #close (STDOUT); close (STDIN); close (STDERR); print '2'; # Set up signals we want to catch. Let's log # warnings, fatal errors, and catch hangups # and dying children $SIG{__WARN__} = sub { &logMessage ("NOTE! " . join(" ", @_)); }; print '3'; $SIG{__DIE__} = sub { &logMessage ("FATAL! " . join(" ", @_)); exit; }; print '4'; $SIG{HUP} = $SIG{INT} = $SIG{TERM} = sub { # Any sort of death trigger results in death of all my $sig = shift; $SIG{$sig} = 'IGNORE'; kill 'INT' => keys %children; die "killed by $sig\n"; exit; }; print '5'; # We'll handle our child reaper in a separate sub $SIG{CHLD} = \&REAPER; } sub REAPER { my $stiff; while (($stiff = waitpid(-1, &WNOHANG)) > 0) { warn ("child $stiff terminated -- status $?"); $children--; delete $children{$stiff}; } $SIG{CHLD} = \&REAPER; } sub spawnChildren { for (1..$totalChildren) { &newChild(); } } sub keepTicking { while ( 1 ) { sleep; for (my $i = $children; $i < $totalChildren; $i++ ) { &newChild(); } }; } sub newChild { # Daemonize away from the parent process. my $pid; my $sigset = POSIX::SigSet->new(); # Dies here sigprocmask(SIG_BLOCK, $sigset) or die "Can't block SIGINT for fork: + $!"; die "Cannot fork child: $!\n" unless defined ($pid = fork); if ($pid) { $children{$pid} = 1; $children++; warn "forked new child, we now have $children children"; return; } # Loop for a certain number of times my $i = 0; while ($i < $childLifetime) { $i++; # Accept a connection from HTTP::Daemon my $c = $d->accept or last; $c->autoflush(1); logMessage ("connect:". $c->peerhost . "\n"); # Get the request my $r = $c->get_request(1) or last; # Process the request.. # you can do whatever you like here.. # we blindly respond to anything for now.. if (($r->method eq 'GET') && ($r->url->path =~ m/\.(html|cgi|htm|p +l)/)) { my $response = HTTP::Response->new(200); $response->content(&Hello_World($r->url->path, $r->url->query) +); $response->header("Content-Type" => "text/html"); $c->send_response($response); } elsif (($r->method eq 'GET') && ($r->url->path =~ m/\.(gif|jpg|bmp +|png)/)) { open (IMAGE, 'C:\\rrships' . $r->url->path) or warn 'Couldn\'t + open image: ' . $r->url->path; my $response = HTTP::Response->new(200); $response->push_header('Content-Type','image/' . $1); my $content = ''; while (<IMAGE>) { $content .= $_; } close IMAGE; $response->content($content); $c->send_response($response); } elsif (($r->method eq 'GET') && ($r->url->path eq '/')) { my $response = HTTP::Response->new(200); $response->content(&Hello_World($r->url->path, $r->url->query) +); $response->header("Content-Type" => "text/html"); $c->send_response($response); } else { $c->send_error('RC_FORBIDDEN'); } logMessage ($c->peerhost . " " . $d->url . $r->url . "\n"); } warn "child terminated after $i requests"; exit; } sub logMessage { my $message = shift; print $message . "\n"; }

Replies are listed 'Best First'.
Re: Fork Windows
by tilly (Archbishop) on Jan 15, 2009 at 05:18 UTC
    What kind of volume do your applications have? Assuming a basic intranet scenario, I would highly recommend just going out and getting a free webserver that works on Windows. You can even run (albeit very inefficiently) Apache with mod_perl in pre-fork mode.

    That said, the likely cause of your problem is that Windows doesn't really support the fork command. So it is emulated in Perl using threads. That works OK for very simple stuff. But that emulation breaks down when you start doing complex signal handling. For instance you can't predict which thread will receive a signal, so how can you reliably handle signals differently in child threads and in the controlling thread?

    If you want to go down this route I would suggest using a module like Win32::Process to spawn your child processes. That way you have real processes that you can manipulate with the Win32 API.

Re: Fork Windows
by BrowserUk (Patriarch) on Jan 15, 2009 at 09:11 UTC
    1. Windows doesn't do signals!

      Perl emulates (badly) a very limited set of (just 4 trappable) signals under Win32. Any application that relies upon signals for IPC will fail to work reliably under Windows.

    2. Windows doesn't do fork.

      Perl emulates fork using threads under Win32. It uses USER space code, to emulate the COW sharing of memory via CLONING (wholesale copying) of data. The result is relatively slow and memory consuming.

    Cygwin does a similar emulation of signals and fork but uses real processes for the latter. It still involves wholesale copying of memory to emulate COW, and the signals emulation whilst more complete is still an emulation; incomplete and far from transparently compatible.

    I have to concur with tilly that you would be far better off separating the webserver from your application and using a lightweight dedicated HTTP server, rather than trying to roll your own cross-platform solution in Perl.

    Something like TinyWEB or TinySSL is a 53k executable with a 2MB memory footprint, comes with source (albiet Delphi), and is surprisingly fast and robust non-forking (select loop driven) server that can handle Perl cgis.

    There are lots of others, some platform-specific, some cross-platform. Writing & maintaining your own in Perl doesn't make sense.


    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.