[Chicago-talk] Speaking of threading
Steven Lembark
lembark at wrkhors.com
Sat Dec 6 16:45:44 CST 2003
-- "Scott T. Hildreth" <shild at sbcglobal.net>
>
> On 05-Dec-2003 Chris McAvoy wrote:
>> You know, if anyone ever wants to do a talk on threading or forking perl,
>
> ...and Steve can add to his talk by adding debugging fork() with
> $DB::fork_TTY, unless he already did. Unfortunately I could not
> make it.
>
>> I'd be very interested.
Lets say you debug something that forks. Without any preparation
for it you'll get:
main::(-e:1): 0
DB<1> fork
######### Forked, but do not know how to create a new TTY. #########
Since two debuggers fight for the same TTY, input is severely entangled.
I know how to switch the output to a different window in xterms
and OS/2 consoles only. For a manual switch, put the name of the created
TTY
in $DB::fork_TTY, or define a function DB::get_fork_TTY() returning this.
On UNIX-like systems one can get the name of a TTY for the given window
by typing tty, and disconnect the shell from TTY by sleep 1000000.
With X11 you can:
1. open an xterm (whatever).
2. xterm -e 'tty; sleep 1d';
3. perl -d as usual.
4. $DB::fork_TTY = '<tty output from step 2>';
5. Do whatever you like to debug the code.
A slightly cleaner solution is to add one item into your
#! code:
sub DB::get_fork_TTY { $ENV{fork_TTY} }
now you can:
1. open an xterm (whatever).
2. xterm -e 'tty; sleep 1d';
3. fork_TTY='<tty output>' perl -d foobar;
So far as forks themlselves, a fork just creates a mostly-duplicate
of the current process context. At the code level, the parent gets
back a childs PID for a successful fork, the child gets back zero
from the fork call, and failures return undef:
if( (my $pid = fork) > 0 )
{
# parent, this can wait, keep going, whatever.
}
elsif( defined $pid )
{
# child
do_the_deed;
exit $status;
}
else
{
confess "Phorkaphobia: $!"
}
Main thing is to make REAL DAMN SURE THE CHILD EXITS or you'll
probably cause phorkatosis, wherein the main loop keeps forking
children that fork children that fork chidren that... eventually
slurp up your symbol table and bring the SysAdmins breathing fire
at you :-)
If you want to just discharge the child proc's and don't really
care what they do then setting $SIG{CHLD} = 'IGNORE' will cause
the main code to reap the child proc's for you automically (instead
of leaving zombies around). This is described in perldoc perlipc if
you search for "CHLD".
The "wait" function is used to wait for child exits:
wait Behaves like the wait(2) system call on your system: it waits
for a child process to terminate and
returns the pid of the deceased process, or "-1" if there are no child
processes. The status is
returned in $?. Note that a return value of "-1" could mean that
child processes are being automati-
cally reaped, as described in perlipc.
If you care what happend to the child proc's you can use wait to
block until something exits:
my $logpath = "$logdir/blah";
if( (my $pid = fork) > 0 )
{
$childmeta{$pid} = { logpath => $logpath };
}
elsif( defined $pid )
{
open STDOUT, '>', $logpath
or die "$logpath: $!";
...
}
...
# block on sigchld, basically.
# wait hands back the child process
if( (my $pid = wait) > 0 )
{
if( $? )
{
# bad news, boss...
if( my $status = $? >> 8 )
{
carp "exit( $status ) by $pid";
}
elsif( my $signal = $? & 0xFF )
{
carp "kill SIG-$signal on $pid";
}
}
else
{
# don't leave around log files for completed jobs
# unless they contain something or the job exited
# nonzero.
my $path = $childmeta{$pid}->{logpath}
or die "Bogus childmeta: no data for $pid";
unlink $path unless -s $path;
}
}
else
{
print "All child proc's exited...";
exit 0;
}
This is more-or-less what happens in Schedule::Parallel, which
keeps a fixed number of jobs running in parallel:
my @jobz = foo_hook;
my $jobcount = bar_hook;
forkify splice @jobz, 0, $jobcount;
while( (my $pid = wait) > 0 )
{
forkify shift @jobz if @jobz;
}
The if-block on fork is basically all that forkify does.
Pre-forking however many jobs are supposed to run in parallel
leaves wait blocking on the first job that exits. When that
happens a new job is forked off if there is anything left to
do. Then @jobs is exhausted the loop waits for the last few
jobs to finish and falls out of the loop when wait returns
-1 (no jobs to wait for).
The "system" function is basically a fork/exec/wait all wrapped
in a nice, neat bundle for you so that you don't have to deal with
this stuff, just the return values.
--
Steven Lembark 2930 W. Palmer
Workhorse Computing Chicago, IL 60647
+1 888 359 3508
More information about the Chicago-talk
mailing list