First of all, this is cool stuff ... thanks for posting.
The problem you are having (from what I can tell) is a extremely
subtle one. Your program is working almost as designed, but
there are a few issues. First you are not printing enough characters to cause the print
buffer to actually print to the screen. So all of your characters
are getting pushed into the buffer, then they over write each
other in the last instant because the buffer is not getting flushed
during the execution of the program. So to fix this problem
add:
local $| = 1 inside the do loop. $| is the
'autoflush' variable. 'local'
will prevent the global $| from being overwritten in case
you need buffered output elsewhere in your program.
The next issue is that your waitpid is actually hanging ... waiting for th
child to die. You want to keep looping, so you need to change
the second paramater to 64 or POSIX::WNOHANG
use POSIX;
... code here ...
$child_pid = waitpid(-1, WNOHANG);
This prevents the waitpid from hanging.
So for your complete functioning code:
use POSIX;
if ($pid = fork)
{
# parent
do
{
local $| = 1;
print "\b-"; sleep 1;
print "\b\\"; sleep 1;
print "\b|"; sleep 1;
print "\b/"; sleep 1;
$child_pid = waitpid(-1,WNOHANG);
} until $child_pid == -1;
}
else
{
# child
#exec("cmd");
sleep 15;
exit(0);
}
The last waitpid was not necessary. I also changed exec "cmd" to sleep 15 for testing.
Now there is one more minor thing I would suggest. You only check for the child
death every 4 seconds. You probably should be checking for this constantly with
a signal handler and do the waitpid in the signal handler.
My signal handler just sets a global flag to tell the
parent to stop looping:
use POSIX;
$keep_going = 1;
#set the SIGCHLD signal handler
$SIG{CHLD} = \&REAPER;
if ($pid = fork)
{
# parent
while($keep_going)
{
local $| = 1;
print "\b-"; sleep 1;
print "\b\\"; sleep 1;
print "\b|"; sleep 1;
print "\b/"; sleep 1;
}
}
else
{
# child
sleep 15;
exit(0);
}
#SIGCHLD signal handler
sub REAPER
{
#test if all childred are dead
unless(waitpid(-1, WNOHANG) == -1 )
{
$keep_going = 0;
}
#reset the signal handler for more than
#one child death.
$SIG{CHLD} = \&REAPER
}
So now REAPER will get called whenever there is a signal from
any child, and the waitpid will not have to be staged every 4 seconds.
I hope this helps.