While I have several unresolved issues with fork on Windows I think they biggest problem with your script is that the fork logic backwards.

Something like the following will give you better results.

       my $pid;
       if($pid = fork()) {
         print(FOUT "running ($pid)\n");
         wait();
       } else {
         open(FOR, '>>', 'C:\delme-kid.txt');
         thread();
         close(FOR);
         sleep(10);
         exit(0);
       }

-Andrew

Andreas Altergott wrote:
Hi,

this will be especially interesting for dolman :-)

as already described in previous emails there is a big problem when
using POE::Wheel::Run::Win32 with Win32::Daemon as a windows service.
Your service will get a termination request from windows, as soon as any
child process terminates.

This happens because POE::Wheel::Run::Win32 is using the fork() method.
 I do not know how exactly ActiveStates Perl handles the fork() method,
but it does it different than the system() method.  The system() method
also creates a child process, but it waits for the child until it
terminates.  This does not initiate a termination request to the
service.  Although both do create sub processes.

Speaking in windows terminology one has to say threads instead of
processes.  So system() is using fork() according to the perldocs.  And
fork() tries to do a real fork() if it is available.  On windows it is
not.  So it has to create a thread.  I don't know how it does terminate
a thread though.  It must be different when you use system(), else it
wouldn't terminate your service.

Here's some more detailed information on perls fork() and system()

 o http://perldoc.perl.org/perlfork.html

 o http://perldoc.perl.org/functions/fork.html

 o http://perldoc.perl.org/functions/system.html

Luckily, using threads is not a blocking process.  It means that your
POE program can continue working, while the thread is also doing it's
work.  The only drawback is, that your programs will be windows
specific.  You can not do a 'use threads;' inside of eval, require or
any other trick to make it system independent.

 o http://perldoc.perl.org/threads.html

The fast solution is to use threads instead of POE::Wheel::Run::Win32.

use threads;

my $thr = 0;

sub thread {
    return(system("C:/Progs/any.exe"));
}

...

    # inside a POE subroutine that's getting called over and over again
    unless ($thr) {
        print("creating thread\n");
        $thr = threads->create('thread');
    }

    if ($thr->is_joinable()) {
        print("thread returned " . $thr->join() . "\n");
        $thr = 0;
    }

...

I've been reading in some previous mails, that POE::Wheel::Run and
POE::Wheel::Run::Win32 have been merged.  This is nice to hear :-)
But in this case it complicates things.  Because now POE::Wheel::Run is
system independent by using fork().  As described this is not possible
with Win32::Daemon.  So either it should be noted somewhere, that
POE::Wheel::Run is not usable from a windows service, or it should be
split again :-p into POE::Wheel::Run::Win32 using threads instead of
fork(). ;-)

Maybe this issue is easier to fix, by attacking fork() itself.
Well, you do not strictly need POE::Wheel::Run, if you do use threads.
They are not blocking anyway, by doing the test is_joinable().

I'm not sure if this concerns only Win32::Daemon or if it is a global
windows service problem with Perl using fork().

This is not POE, but the core of the /fork()/ is the same.  You can use
POE::Component::Daemon::Win32 to simplify things, but don't forget to
use threads.

#!/usr/bin/perl
########################################################################
# parent.pl

use strict;
use warnings;

use threads;
use Win32;
use Win32::Daemon;

sub thread {
    return(system("C:/Perl/bin/perl.exe C:/kid.pl"));
}

my $SERVICE_SLEEP_TIME = 20;
my $PrevState = SERVICE_START_PENDING;
my $thr = 0;

Win32::Daemon::StartService();

open(FOUT, '>', 'C:/delme-parent.txt');

while (SERVICE_STOPPED != (my $State = Win32::Daemon::State())) {
    if (SERVICE_START_PENDING == $State) {
        print(FOUT "start pending\n");
        Win32::Daemon::State(SERVICE_RUNNING);
        $PrevState = SERVICE_RUNNING;
    } elsif ( SERVICE_STOP_PENDING == $State ) {
        print(FOUT "stop pending\n");
        Win32::Daemon::State(SERVICE_STOPPED);
    } elsif (SERVICE_PAUSE_PENDING == $State) {
        print(FOUT "pause pending\n");
        Win32::Daemon::State(SERVICE_PAUSED);
        $PrevState = SERVICE_PAUSED;
        next;
    } elsif (SERVICE_CONTINUE_PENDING == $State) {
        print(FOUT "continue pending\n");
        Win32::Daemon::State(SERVICE_RUNNING);
        $PrevState = SERVICE_RUNNING;
        next;
    } elsif (SERVICE_STOP_PENDING == $State) {
        print(FOUT "stop pending\n");
        Win32::Daemon::State(SERVICE_STOPPED);
        $PrevState = SERVICE_STOPPED;
        next;
    } elsif (SERVICE_RUNNING == $State) {
        print(FOUT "running\n");

        # Runs as it should run :-)
        unless ($thr) {
            print(FOUT "creating thread\n");
            $thr = threads->create('thread');
        }

        if ($thr->is_joinable()) {
            print(FOUT "thread returned " . $thr->join() . "\n");
            $thr = 0;
        }

        # Runs only once and kills the parent process on sigchld.
        # One should use child signals to harvest children, so that it
        # does not block the parent process.  This here is only to
        # demonstrate the termination of the parent process.
        #if(fork()) {
        #    open(FOR, '>>', 'C:/delme-kid.txt');
        #    print(FOR "running\n");
        #    close(FOR);
        #    sleep(10);
        #    exit(0);
        #} else {
        #    wait();
        #}

        # Runs perfectly, but freezes the parent process.
        #system("C:/Perl/bin/perl.exe C:/kid.pl");
    } else {
        print(FOUT "unknown request\n");
        Win32::Daemon::State($PrevState);
    }

    if (SERVICE_CONTROL_NONE != (my $Message =
            Win32::Daemon::QueryLastMessage(1))) {
        if (SERVICE_CONTROL_INTERROGATE == $Message) {
            print(FOUT "request received\n");
            Win32::Daemon::State($PrevState);
        } elsif (SERVICE_CONTROL_SHUTDOWN == $Message) {
            print(FOUT "shutdown!!!\n");
            Win32::Daemon::State(SERVICE_STOP_PENDING, 25000);
        }
    }

    Win32::Sleep( $SERVICE_SLEEP_TIME );
}

Win32::Daemon::StopService();
close(FOUT);

# EOF
########################################################################

#!/usr/bin/perl
########################################################################
# C:\kid.pl

use strict;
use warnings;

open(FOR, '>>', 'C:/delme-kid.txt');

print(FOR "starting\n");

for (my $i = 0; $i < 10; $i++) {
    print(FOR "running\n");
    sleep(1);
}

print(FOR "terminating\n");

close(FOR);

# EOF
########################################################################


Regards,
Andreas

Reply via email to