Date: | Fri, 20 May 2005 23:16:41 +0200 |
From: | Merijn Broeren <merijnb [...] iloquent.com> |
To: | bug-poe [...] rt.cpan.org |
CC: | poe [...] perl.org |
Subject: | POE::Wheel::SocketFactory changes all sockets in the same program to non-blocking |
Hi,
Here is a patch that fixes the issue :
--- SocketFactory.orig 2005-05-20 22:05:27.000000000 +0200
+++ SocketFactory.pm 2005-05-20 22:05:41.000000000 +0200
@@ -46,55 +46,6 @@
# Provide dummy constants for systems that don't have them.
BEGIN {
- if ($^O eq 'MSWin32') {
-
- # Constants are evaluated first so they exist when the code uses
- # them.
- eval( '*F_GETFL = sub { 0 };' .
- '*F_SETFL = sub { 0 };' .
-
- # Garrett Goebel's patch to support non-blocking connect()
- # or MSWin32 follows. His notes on the matter:
- #
- # As my patch appears to turn on the overlapped attributes
- # for all successive sockets... it might not be the optimal
- # solution. But it works for me ;)
- #
- # A better Win32 approach would probably be to:
- # o create a dummy socket
- # o cache the value of SO_OPENTYPE
- # o set the overlapped io attribute
- # o close dummy socket
- #
- # o create our sock
- #
- # o create a dummy socket
- # o restore previous value of SO_OPENTYPE
- # o close dummy socket
- #
- # This way we'd only be turning on the overlap attribute for
- # the socket we created... and not all subsequent sockets.
-
- '*SO_OPENTYPE = sub () { 0x7008 };' .
- '*SO_SYNCHRONOUS_ALERT = sub () { 0x10 };' .
- '*SO_SYNCHRONOUS_NONALERT = sub () { 0x20 };'
- );
- die if $@;
-
- # Turn on socket overlapped IO attribute per MSKB: Q181611. This
- # concludes Garrett's patch.
-
- eval( 'socket(POE, AF_INET, SOCK_STREAM, getprotobyname("tcp"))' .
- 'or die "socket failed: $!";' .
- 'my $opt = unpack("I", getsockopt(POE, SOL_SOCKET, SO_OPENTYPE));' .
- '$opt &= ~(SO_SYNCHRONOUS_ALERT|SO_SYNCHRONOUS_NONALERT);' .
- 'setsockopt(POE, SOL_SOCKET, SO_OPENTYPE, $opt);' .
- 'close POE;'
-
- # End of Garrett's patch.
- );
- die if $@;
- }
unless (exists $INC{"Socket6.pm"}) {
eval "*Socket6::AF_INET6 = sub () { ~0 }";
@@ -579,6 +530,37 @@
$default_socket_type{$abstract_domain}->{$protocol_name};
}
+ my $win32_socket_opt;
+ # o create a dummy socket
+ # o cache the value of SO_OPENTYPE in $win32_socket_opt
+ # o set the overlapped io attribute
+ # o close dummy socket
+ if ( POE::Kernel::RUNNING_IN_HELL) {
+
+ # Constants are evaluated first so they exist when the code uses
+ # them.
+ eval {
+ *SO_OPENTYPE = sub () { 0x7008 };
+ *SO_SYNCHRONOUS_ALERT = sub () { 0x10 };
+ *SO_SYNCHRONOUS_NONALERT = sub () { 0x20 };
+ };
+ die "Could not install SO constants [$@]" if $@;
+
+ # Turn on socket overlapped IO attribute per MSKB: Q181611.
+
+ eval {
+ socket(POE, AF_INET, SOCK_STREAM, getprotobyname("tcp"))
+ or die "socket failed: $!";
+ my $opt = unpack("I", getsockopt(POE, SOL_SOCKET, SO_OPENTYPE()));
+ $win32_socket_opt = $opt;
+ $opt &= ~(SO_SYNCHRONOUS_ALERT()|SO_SYNCHRONOUS_NONALERT());
+ setsockopt(POE, SOL_SOCKET, SO_OPENTYPE(), $opt);
+ close POE;
+ };
+
+ die if $@;
+ }
+
# Create the socket.
unless (socket( $socket_handle, $self->[MY_SOCKET_DOMAIN],
$self->[MY_SOCKET_TYPE], $self->[MY_SOCKET_PROTOCOL]
@@ -590,6 +572,23 @@
return $self;
}
+ # o create a dummy socket
+ # o restore previous value of SO_OPENTYPE
+ # o close dummy socket
+ #
+ # This way we'd only be turning on the overlap attribute for
+ # the socket we created... and not all subsequent sockets.
+ if ( POE::Kernel::RUNNING_IN_HELL) {
+
+ eval {
+ socket(POE, AF_INET, SOCK_STREAM, getprotobyname("tcp"))
+ or die "socket failed: $!";
+ setsockopt(POE, SOL_SOCKET, SO_OPENTYPE(), $win32_socket_opt);
+ close POE;
+ };
+
+ die if $@;
+ }
DEBUG && warn "socket";
#------------------#
And here is a test program:
#!/ms/dist/perl5/bin/perl5.8 -w
use POE;
$|=1;
my $obj = new MyDebug;
POE::Session->create(
object_states => [ $obj => [ '_start', 'next', 'reaper', 'output' ]]);
POE::Kernel->run;
exit(0);
# ------------------------------------------------
# Now define our class which does all of the work.
# ------------------------------------------------
package MyDebug;
use POE;
use POE::Wheel::Run;
use POE::Wheel::SocketFactory; # Just adding this line breaks the
# program, the child will die
# prematurely
use IO::Handle;
use File::Spec;
use POSIX qw(dup);
sub new {
my $class = shift;
return bless {};
}
sub _start {
my ($self, $heap, $kernel) = @_[OBJECT, HEAP, KERNEL];
warn "_start\n";
$kernel->sig(CHLD => 'reaper');
$self->{subprocess} = POE::Wheel::Run->new(Program =>
sub {
my $buffer;
my $input_stream = IO::Handle::->new_from_fd(dup(fileno(STDIN)), "r");
my $output_stream = IO::Handle::->new_from_fd(dup(fileno(STDOUT)), "w");
my $devnull = File::Spec->devnull();
open(STDIN, "$devnull");
open(STDOUT, "> $devnull");
open(STDERR, "> $devnull");
while (sysread($input_stream, $buffer, 1024 * 32)) {
last if substr($buffer, 0, 4) eq 'kill';
syswrite($output_stream, "child [$$] read: $buffer");
}
}, StdoutEvent => 'output');
warn "have a subprocess\n" if $self->{subprocess};
$heap->{counter} = 3;
$kernel->delay_set('next', 3);
}
sub output {
my ($self, $output) = @_[OBJECT, ARG0];
chomp $output;
warn "received data from subprocess: [$output]\n";
}
sub reaper {
my ($self, $kernel, $heap) = @_[OBJECT, KERNEL, HEAP];
if ($heap->{counter}) {
warn "child has died prematurely\n";
} else {
warn "child has completed when the counter ran out\n";
}
$self->{subprocess} = undef;
$kernel->sig_handled;
}
sub next {
my ($self, $kernel, $heap) = @_[OBJECT, KERNEL, HEAP];
warn "next [" . $heap->{counter}. "]\n";
if ($self->{subprocess}) {
$self->{subprocess}->put("Can you hear me $heap->{counter}");
}
if (--$heap->{counter}) {
$kernel->delay_set('next', 4)
} else {
if ($self->{subprocess}) {
warn "Trying to kill [" . $self->{subprocess}->PID . "]\n";
kill $self->{subprocess}->put("kill");
}
}
}
--
Merijn Broeren | Sometime in the middle ages, God got fed up with us
Software Geek | and put earth at sol.milky-way.univ in his kill-file.
| Pray all you want, it just gets junked.