Skip Menu |

This queue is for tickets about the POE CPAN distribution.

Report information
The Basics
Id: 50025
Status: resolved
Priority: 0/
Queue: POE

People
Owner: Nobody in particular
Requestors: acferen [...] yahoo.com
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: 1.269
Fixed in: (no value)



Subject: Patch to merge Wheel::Run::Win32 into Wheel::Run
The attached patch Merges POE::Wheel::Run::Win32 into POE::Wheel::Run. All tests passed on linux. The patch works on win32, but I needed to back out r2628. r2628 touches different files, but changes something that causes perl to crash abruptly on win32. This patch does not include backing out r2628. It would be nice if there where tests to test for the reason for these changes, but I couldn't find any in Run::Win32. I settled for passing POE's test suite and "works for me"
Subject: merge_run_runwin32.patch
Index: lib/POE/Wheel/Run.pm =================================================================== --- lib/POE/Wheel/Run.pm (revision 2700) +++ lib/POE/Wheel/Run.pm (working copy) @@ -34,6 +34,15 @@ eval { require Win32API::File; }; if ($@) { die "Win32API::File but failed to load:\n$@" } else { Win32API::File->import( qw(FdGetOsFHandle) ); }; + + eval { require Win32::Process; }; + if ($@) { die "Win32::Process but failed to load:\n$@" } + + eval { require Win32::Job; }; + if ($@) { die "Win32::Job but failed to load:\n$@" } + + eval { require Win32; }; + if ($@) { die "Win32 but failed to load:\n$@" } } # Determine the most file descriptors we can use. @@ -76,6 +85,8 @@ sub EVENT_STDERR () { 23 } sub STATE_STDERR () { 24 } +sub MSWIN32_GROUP_PID () { 25 } + # Used to work around a bug in older perl versions. sub CRIMSON_SCOPE_HACK ($) { 0 } @@ -387,8 +398,10 @@ # Tell the parent that the stdio has been set up. close $sem_pipe_read; - print $sem_pipe_write "go\n"; - close $sem_pipe_write; + unless ( ref($program) ne 'CODE' and POE::Kernel::RUNNING_IN_HELL ) { + print $sem_pipe_write "go\n"; + close $sem_pipe_write; + } if (POE::Kernel::RUNNING_IN_HELL) { # The Win32 pseudo fork sets up the std handles in the child @@ -442,10 +455,54 @@ eval { exec("$^X -e 0"); }; }; exit(0); - } - else { + } else { # Windows! What I do for you! if (POE::Kernel::RUNNING_IN_HELL) { + my $exitcode = 0; + + my ($appname, $cmdline); + + if (ref $program eq 'ARRAY') { + $appname = $program->[0]; + $cmdline = join(' ', map { /\s/ && ! /"/ ? qq{"$_"} : $_ } (@$program, @$prog_args) ); + } + else { + $appname = undef; + $cmdline = join(' ', $program, map { /\s/ && ! /"/ ? qq{"$_"} : $_ } @$prog_args); + } + + my $w32job; + + unless ( $w32job = Win32::Job->new() ) { + print $sem_pipe_write "go\n"; + close $sem_pipe_write; + die Win32::FormatMessage( Win32::GetLastError() ); + } + + my $w32pid; + + unless ( $w32pid = $w32job->spawn( $appname, $cmdline ) ) { + print $sem_pipe_write "go\n"; + close $sem_pipe_write; + die Win32::FormatMessage( Win32::GetLastError() ); + } + else { + print $sem_pipe_write "$w32pid\n"; + close $sem_pipe_write; + my $ok = $w32job->watch( sub { 0 }, 60 ); + my $hashref = $w32job->status(); + $exitcode = $hashref->{$w32pid}->{exitcode}; + } + + # In case flushing them wasn't good enough. + close STDOUT if defined fileno(STDOUT); + close STDERR if defined fileno(STDERR); + + exit($exitcode); + } + + # Windows! What I do for you! + if (POE::Kernel::RUNNING_IN_HELL) { if (ref($program) eq 'ARRAY') { exec(@$program, @$prog_args); warn "can't exec (@$program) in child pid $$: $!"; @@ -519,6 +576,7 @@ $stderr_driver, # DRIVER_STDERR $stderr_event, # EVENT_STDERR undef, # STATE_STDERR + undef, # MSWIN32_GROUP_PID ], $type; # PG- I suspect <> might need PIPE @@ -526,7 +584,9 @@ # Wait here while the child sets itself up. { local $/ = "\n"; - <$sem_pipe_read>; + my $chldout = <$sem_pipe_read>; + chomp $chldout; + $self->[MSWIN32_GROUP_PID] = $chldout if POE::Kernel::RUNNING_IN_HELL and $chldout ne 'go'; } close $sem_pipe_read; close $sem_pipe_write; @@ -1106,7 +1166,12 @@ sub kill { my ($self, $signal) = @_; $signal = 'TERM' unless defined $signal; - eval { kill $signal, $self->[CHILD_PID] }; + if ( $self->[MSWIN32_GROUP_PID] ) { + Win32::Process::KillProcess( $self->[MSWIN32_GROUP_PID], 293 ); + } + else { + eval { kill $signal, $self->[CHILD_PID] }; + } } 1;
It occurred to me that POE::Wheel::Run did work on Win32 before being merged with Wheel::Run::Win32. Wheel::Run::Win32 uses mechanisms other than fork for creating processes in some cases. Perhaps there are use cases that expect the current fork()ing behavior. I don't know what they are, but for anyone needing that behavior... Attached is a modified patch which uses POE::Wheel::Run::LESS_FORK. If LESS_FORK returns true (default on Win32) Wheel::Run will use Win32 process creation for non coderef Wheels. If LESS_FORK returns false Wheel::Run will always fork. On platforms other than Win32 LESS_FORK makes no sense and is always false.
Index: lib/POE/Wheel/Run.pm =================================================================== --- lib/POE/Wheel/Run.pm (revision 2700) +++ lib/POE/Wheel/Run.pm (working copy) @@ -16,6 +16,17 @@ use base qw(POE::Wheel); BEGIN { + no strict 'refs'; + if ($^O eq 'MSWin32') { + unless (defined &LESS_FORK) { + *{ __PACKAGE__ . '::LESS_FORK' } = sub { 1 }; + } + } else { + *{ __PACKAGE__ . '::LESS_FORK' } = sub { 0 }; + } +} + +BEGIN { die "$^O does not support fork()\n" if $^O eq 'MacOS'; local $SIG{'__DIE__'} = 'DEFAULT'; @@ -34,6 +45,17 @@ eval { require Win32API::File; }; if ($@) { die "Win32API::File but failed to load:\n$@" } else { Win32API::File->import( qw(FdGetOsFHandle) ); }; + + if ( LESS_FORK ) { + eval { require Win32::Process; }; + if ($@) { die "Win32::Process but failed to load:\n$@" } + + eval { require Win32::Job; }; + if ($@) { die "Win32::Job but failed to load:\n$@" } + + eval { require Win32; }; + if ($@) { die "Win32 but failed to load:\n$@" } + } } # Determine the most file descriptors we can use. @@ -76,6 +98,8 @@ sub EVENT_STDERR () { 23 } sub STATE_STDERR () { 24 } +sub MSWIN32_GROUP_PID () { 25 } + # Used to work around a bug in older perl versions. sub CRIMSON_SCOPE_HACK ($) { 0 } @@ -387,8 +411,10 @@ # Tell the parent that the stdio has been set up. close $sem_pipe_read; - print $sem_pipe_write "go\n"; - close $sem_pipe_write; + unless ( ref($program) ne 'CODE' and LESS_FORK ) { + print $sem_pipe_write "go\n"; + close $sem_pipe_write; + } if (POE::Kernel::RUNNING_IN_HELL) { # The Win32 pseudo fork sets up the std handles in the child @@ -442,9 +468,53 @@ eval { exec("$^X -e 0"); }; }; exit(0); - } - else { + } else { # Windows! What I do for you! + if (LESS_FORK) { + my $exitcode = 0; + + my ($appname, $cmdline); + + if (ref $program eq 'ARRAY') { + $appname = $program->[0]; + $cmdline = join(' ', map { /\s/ && ! /"/ ? qq{"$_"} : $_ } (@$program, @$prog_args) ); + } + else { + $appname = undef; + $cmdline = join(' ', $program, map { /\s/ && ! /"/ ? qq{"$_"} : $_ } @$prog_args); + } + + my $w32job; + + unless ( $w32job = Win32::Job->new() ) { + print $sem_pipe_write "go\n"; + close $sem_pipe_write; + die Win32::FormatMessage( Win32::GetLastError() ); + } + + my $w32pid; + + unless ( $w32pid = $w32job->spawn( $appname, $cmdline ) ) { + print $sem_pipe_write "go\n"; + close $sem_pipe_write; + die Win32::FormatMessage( Win32::GetLastError() ); + } + else { + print $sem_pipe_write "$w32pid\n"; + close $sem_pipe_write; + my $ok = $w32job->watch( sub { 0 }, 60 ); + my $hashref = $w32job->status(); + $exitcode = $hashref->{$w32pid}->{exitcode}; + } + + # In case flushing them wasn't good enough. + close STDOUT if defined fileno(STDOUT); + close STDERR if defined fileno(STDERR); + + exit($exitcode); + } + + # Windows! What I do for you! if (POE::Kernel::RUNNING_IN_HELL) { if (ref($program) eq 'ARRAY') { exec(@$program, @$prog_args); @@ -519,6 +589,7 @@ $stderr_driver, # DRIVER_STDERR $stderr_event, # EVENT_STDERR undef, # STATE_STDERR + undef, # MSWIN32_GROUP_PID ], $type; # PG- I suspect <> might need PIPE @@ -526,7 +597,9 @@ # Wait here while the child sets itself up. { local $/ = "\n"; - <$sem_pipe_read>; + my $chldout = <$sem_pipe_read>; + chomp $chldout; + $self->[MSWIN32_GROUP_PID] = $chldout if LESS_FORK and $chldout ne 'go'; } close $sem_pipe_read; close $sem_pipe_write; @@ -1106,7 +1179,12 @@ sub kill { my ($self, $signal) = @_; $signal = 'TERM' unless defined $signal; - eval { kill $signal, $self->[CHILD_PID] }; + if ( $self->[MSWIN32_GROUP_PID] ) { + Win32::Process::KillProcess( $self->[MSWIN32_GROUP_PID], 293 ); + } + else { + eval { kill $signal, $self->[CHILD_PID] }; + } } 1;
Thank you for the patch. I've applied it as revision 2705, and it will be in the next release if it tests cleanly on Windows.