Skip Menu |

This queue is for tickets about the Win32-Daemon CPAN distribution.

Report information
The Basics
Id: 50020
Status: resolved
Priority: 0/
Queue: Win32-Daemon

People
Owner: Nobody in particular
Requestors: dolmen [...] cpan.org
Cc: dave [...] roth.net
jand [...] ActiveState.com
AdminCc:

Bug Information
Severity: Critical
Broken in: (no value)
Fixed in: 20100921



Subject: Incompatibility with fork()
If the service uses 'fork()' to start a child process (implemented as a thread on Win32), when the child process ends the service change state to SERVICE_STOP_PENDING and shutdown. The consequence is that we can not implement a cron-like service. I'm attaching a test case: - PerlService.inf is the script to install the service (right click, "Install" from the Windows Explorer) and uninstall (use the Control Panel to uninstall "Perl service demo"). - test-PCDW.pl: the daemon Here is the output: Fri Sep 25 17:05:55 2009 Begin Fri Sep 25 17:05:56 2009 2 Fri Sep 25 17:05:57 2009 4 Fri Sep 25 17:05:58 2009 4 Fri Sep 25 17:05:59 2009 4 Fri Sep 25 17:06:00 2009 4 Child start Fri Sep 25 17:06:01 2009 4 Fri Sep 25 17:06:02 2009 4 Fri Sep 25 17:06:03 2009 4 Fri Sep 25 17:06:04 2009 4 Fri Sep 25 17:06:05 2009 4 Child end Fri Sep 25 17:06:06 2009 3 Fri Sep 25 17:06:07 2009 1 Fri Sep 25 17:06:07 2009 End -- Olivier Mengué - http://o.mengue.free.fr/
Subject: test-PCDW.pl
#!/usr/bin/perl use utf8; # vim:set sts=4 sw=4 et: use strict; use warnings; use POE qw(Component::Daemon::Win32); my $log_file = @ARGV ? $ARGV[0] : "$0.log"; open(my $log, '>:utf8', $log_file); select( (select($log), $| = 1 )[0] ); # Autoflush close(STDOUT); close(STDERR); *STDOUT = *STDERR = $log; my $count = 0; sub service_state { my ($state, $message) = @_[ARG0, ARG1]; print scalar localtime(), ' ', $state, "\n"; # service start pending if ($state == SERVICE_START_PENDING) { # do some sort of initialization here } if ($state == SERVICE_RUNNING) { $count++; if ($count == 4) { my $pid = fork(); unless ($pid) { print "Child start\n"; sleep 5; print "Child end\n"; exit 0; } } } $poe_kernel->yield ('next_state'); } print scalar localtime(), " Begin\n"; POE::Component::Daemon::Win32->spawn( Alias => 'svc', Callback => \&service_state, PollInterval => 1, ); POE::Kernel->run; print scalar localtime(), " End\n";
Subject: PerlService.inf
Download PerlService.inf
application/octet-stream 2.8k

Message body not shown because it is not plain text.

The problem may be in the Win32::Daemon implementation Also, POE::Wheel::Run::Win32 uses fork() internally, so it is affected. -- Olivier Mengué - http://o.mengue.free.fr/
Subject: Win32::Daemon incompatible with fork()
Le Ven. Sep. 25 11:22:55 2009, DOLMEN a écrit : Show quoted text
> The problem may be in the Win32::Daemon implementation
I have been able to reproduce the bug with the bare Win32::Daemon 20080324 on ActivePerl 5.10 Build 1003. Test case is attached. Here is the output: Fri Sep 25 18:11:01 2009 0 Fri Sep 25 18:11:02 2009 2 Fri Sep 25 18:11:02 2009 4 Fri Sep 25 18:11:03 2009 4 Fri Sep 25 18:11:04 2009 4 Fri Sep 25 18:11:05 2009 4 Fri Sep 25 18:11:05 2009 4 Child start Fri Sep 25 18:11:06 2009 4 Fri Sep 25 18:11:07 2009 4 Fri Sep 25 18:11:08 2009 4 Fri Sep 25 18:11:09 2009 4 Child end Fri Sep 25 18:11:10 2009 4 Fri Sep 25 18:11:11 2009 3 -- Olivier Mengué - http://o.mengue.free.fr/
Download fork-bug.inf
application/octet-stream 2.8k

Message body not shown because it is not plain text.

use vars qw( $VERSION ); use Win32::Daemon; my $log_file = @ARGV ? $ARGV[0] : "$0.log"; open(my $log, '>', $log_file); select( (select($log), $| = 1 )[0] ); # Autoflush close(STDOUT); close(STDERR); *STDOUT = *STDERR = $log; my $count = 0; # Start the service... Win32::Daemon::StartService() or die "StartService failure!"; my $PrevState = SERVICE_STARTING; while( SERVICE_STOPPED != ( $State = Win32::Daemon::State() ) ) { print scalar localtime(), ' ', $State, "\n"; if( SERVICE_START_PENDING == $State ) { # Initialization code Win32::Daemon::State( SERVICE_RUNNING ); $PrevState = SERVICE_RUNNING; } elsif( SERVICE_PAUSE_PENDING == $State ) { # "Pausing..."; Win32::Daemon::State( SERVICE_PAUSED ); $PrevState = SERVICE_PAUSED; next; } elsif( SERVICE_CONTINUE_PENDING == $State ) { # "Resuming..."; Win32::Daemon::State( SERVICE_RUNNING ); $PrevState = SERVICE_RUNNING; next; } elsif( SERVICE_STOP_PENDING == $State ) { # "Stopping..."; Win32::Daemon::State( SERVICE_STOPPED ); $PrevState = SERVICE_STOPPED; next; } elsif( SERVICE_RUNNING == $State ) { # The service is running as normal... $PrevState = SERVICE_RUNNING; $count++; if ($count == 4) { my $pid = fork(); unless ($pid) { print "Child start\n"; sleep 5; print "Child end\n"; exit 0; } } else { sleep(1); } } else { # We have some unknown state... # reset it back to what we last knew the state to be... Win32::Daemon::State( $PrevState ); sleep( 1 ); } } Win32::Daemon::StopService();
The problem lies probably in the END {} block of Daemon.pm: this END {} block is run in the child process and so sends the state change to the main thread.
Here is my own patched version of Daemon.pm that fixes the bug. -- Olivier Mengué - http://o.mengue.free.fr/

Message body is not shown because it is too large.

Hi, This change in Olivier patch is enough to create an endless look with my Strawberry Perl: -bootstrap $Package;^M +bootstrap(__PACKAGE__);^M AFAIR I understand, instead of reading the "Win32::Daemon" string, bootstrap() try to access Win32::Daemon namespace and so, call Daemon.pm and call again bootstrap(). Regarding this change: - Win32::Daemon::StopService();^M + # Stop the service only if we are in the main thread (not in a forked process)^M + Win32::Daemon::StopService() if $$ > 0;^M perlvar says: $$ The process number of the Perl running this script. You should consider this variable read-only, although it will be altered across fork() calls. (Mnemonic: same as shells.) I don't understand why $$ should be 0 in the sub-process. Regards Gonéri Le Bouder
CC: <dave [...] roth.net>
Subject: RE: [rt.cpan.org #50020] Incompatibility with fork()
Date: Thu, 4 Mar 2010 15:59:51 -0800
To: <bug-POE-Component-Daemon-Win32 [...] rt.cpan.org>, <dolmen [...] cpan.org>
From: "Jan Dubois" <jand [...] activestate.com>
On Thu, 04 Mar 2010, Gonéri LE BOUDER via RT wrote: Show quoted text
> > <URL: https://rt.cpan.org/Ticket/Display.html?id=50020 > > > This change in Olivier patch is enough to create an endless look with my > Strawberry Perl: > -bootstrap $Package; > +bootstrap(__PACKAGE__); > > AFAIR I understand, instead of reading the "Win32::Daemon" string, > bootstrap() try to access Win32::Daemon namespace and so, call Daemon.pm > and call again bootstrap().
That change should not make any difference; __PACKAGE__ should just be expanded to "Win32::Daemon" at compile time. Show quoted text
> Regarding this change: > - Win32::Daemon::StopService();^M > + # Stop the service only if we are in the main thread (not in a > forked process)^M > + Win32::Daemon::StopService() if $$ > 0;^M > > perlvar says: > $$ The process number of the Perl running this script. You > should consider this variable read-only, although it will be altered > across fork() calls. (Mnemonic: same as shells.) > > I don't understand why $$ should be 0 in the sub-process.
$$ will be less than 0 in the child-process (which is just a pseudo-process simulated by a thread on Windows). This is only true for Perl on Windows, but Win32::Daemon only works on Windows anyways, so that is not an issue. Cheers, -Jan
RT-Send-CC: libwin32 [...] perl.org, jand [...] activestate.com, dave [...] roth.net
The better place for this discussion is the libwin32 mailing list. Jan, as it seems you now have reviewed this patch, could you commit it to the libwin32 repository? Le Jeu 04 Mar 2010 19:01:46, jand@ActiveState.com a écrit : Show quoted text
> On Thu, 04 Mar 2010, Gonéri LE BOUDER via RT wrote:
> > > > <URL: https://rt.cpan.org/Ticket/Display.html?id=50020 > > > > > This change in Olivier patch is enough to create an endless look with my > > Strawberry Perl: > > -bootstrap $Package; > > +bootstrap(__PACKAGE__); > > > > AFAIR I understand, instead of reading the "Win32::Daemon" string, > > bootstrap() try to access Win32::Daemon namespace and so, call Daemon.pm > > and call again bootstrap().
> > That change should not make any difference; __PACKAGE__ should just be > expanded to "Win32::Daemon" at compile time. >
> > Regarding this change: > > - Win32::Daemon::StopService();^M > > + # Stop the service only if we are in the main thread (not in a > > forked process)^M > > + Win32::Daemon::StopService() if $$ > 0;^M > > > > perlvar says: > > $$ The process number of the Perl running this script. You > > should consider this variable read-only, although it will be altered > > across fork() calls. (Mnemonic: same as shells.) > > > > I don't understand why $$ should be 0 in the sub-process.
> > $$ will be less than 0 in the child-process (which is just a
pseudo-process Show quoted text
> simulated by a thread on Windows). This is only true for Perl on Windows, > but Win32::Daemon only works on Windows anyways, so that is not an issue. > > Cheers, > -Jan >
-- Olivier Mengué - http://o.mengue.free.fr/
RT-Send-CC: libwin32 [...] perl.org
My patch has been committed in r488: http://code.google.com/p/libwin32/source/detail?r=488 It has been released in Win32-Daemon-20100921. -- Olivier Mengué - http://search.cpan.org/~dolmen/ http://github.com/dolmen/