Posted on 2009-02-26 20:18:54-08 by cfa4matt
CGI with threads throws error
So I've been trying to get threads working in a CGI script I'm writing. The gist of it is I'm making concurrent requests that have a time limit. If any of the requests takes too long I kill them and just use the data from the threads that finished. The issue is on a Windows box using IIS. The CGI will complete then IIS throws a 'The remote procedure call failed and did not execute' error. Every other call to the script will succeed but the odd one will fail with that error. I would try fork, alert or LWP's timeout feature but the requests are over https and there are well known and documented issues with these other methods on Windows. I'm using ActiveState Perl 5.8.8, threads 1.71 and XP sp2 w/various updates. Below is a minimal script that will reproduce the error. Any insight would be appreciated. Thanks.
#!/c/Perl/bin/perl use strict; use warnings; use threads; $|=1; local $SIG{'KILL'} = sub { threads->detach() if not threads->is_detached(); threads->exit(); }; sub main { hook(); print "Done with first call\n"; hook(); print "Done with second call\n"; } sub hook { my $hooks = [ { name => 'one', call => \&func1 }, { name => 'two', call => \&func2 }, { name => 'three', call => \&func3 }, ]; my $timelimit = 4; my ($call, $module, $addon, @threads); foreach my $i ( 0 .. $#{@$hooks} ) { my $name = $hooks->[$i]{'name'}; my $call = $hooks->[$i]{'call'}; eval { push @threads, { call => $name, func => threads->create({ 'exit' => 'thread_only' }, sub { $call->() }), }; }; print $@ if $@; } my %out; my $now = time; while( scalar keys %out != scalar @threads ) { foreach my $i ( 0 .. $#threads ) { next if defined $out{$i}; if( ref $threads[$i]->{'func'} eq 'threads' ) { next unless $threads[$i]->{'func'}->is_joinable; $out{$i} = $threads[$i]->{'func'}->join; } else { $out{$i} = $threads[$i]->{'func'}; } $out{$i} = (ref $threads[$i]->{'func'} eq 'threads' ? '[thread ' : '[') . $threads[$i]->{'call'} . ' '. (time - $now) .' sec]'. $out{$i} .'[/'. $threads[$i]->{'call'} .']'; } last if (time - $now) > $timelimit; select(undef, undef, undef, 0.5); # Alarm-friendly sleep for one half second } kill_loose_threads() if threads->tid == 0; print (join '', @out{sort keys %out}); print "\nTotal time: " . (time - $now) . "\n"; } sub kill_loose_threads { foreach my $thread ( threads->list ) { $thread->kill('KILL')->detach; } } sub func1 { sleep 1; return "I slept 1 second"; } sub func2 { sleep 3; return "I slept 3 seconds"; } sub func3 { sleep 8; return "I slept 8 seconds"; } main();
Direct Responses: 10091 | Write a response
Posted on 2009-02-27 20:56:07-08 by jdhedden in response to 10078
Re: CGI with threads throws error
Try joining the threads after you 'kill' them instead of detaching:
local $SIG{'KILL'} = sub { threads->exit(); }; .... sub kill_loose_threads { # Send signals first foreach my $thread (threads->list()) { $thread->kill('KILL'); } # Join up with the terminating threads foreach my $thread (threads->list()) { $thread->join(); } }
Also, add an explicit 'exit(0);' after the 'main();' call at the bottom of the script.
Direct Responses: 10100 | Write a response
Posted on 2009-03-02 21:35:02-08 by cfa4matt in response to 10091
Re: CGI with threads throws error
Well, making those changes did stop the RPC error from happening but the main program didn't return until after all threads finish, ie. the whole 8 seconds for the longest thread. This makes the timeout feature useless if I have to wait for each thread to complete however long it takes. As long as I use detach, I still get the RPC errors and the wonky behavior for the program when run as a CGI.
Direct Responses: 10102 | Write a response
Posted on 2009-03-03 16:19:47-08 by jdhedden in response to 10100
Re: CGI with threads throws error
Sorry, but this is beyond me. It's a problem with the exit value returned by Perl when there are detached threads, and it may be a Windows specific problem. Try the Perl5 Porters mailing list for further assistance.
Direct Responses: Write a response
Perl Weekly newsletter
A free weekly newsletter for people who are busy to read all the blogs. click here to check it out.