cLive ;-) has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

I have a script that forks when run. The child goes off and does a few things before exiting. After the first task, the child updates a file before pottering off and doing a few other things. The file itself will be at least a month old before being updated.

I could wait() for the child to exit, but I'd rather the parent read the updated file before then if possible - the child may be a while before exiting.

I've thought of two very similar options so far, both using mtime from 'stat'ing the file:

my $timeout = time + 10; # method 1 # --file still old-- --under 10 secs passed-- 1 while (time - stat($file)[9] < 100 and $timeout > time); # number 2 - very similar sleep(1) while (time - stat($file)[9] < 100 and $timeout > time); # then proceed if (time - stat($file)[9] < 100) { # file updated, so do stuff } else { # child screwed up somewhere so log error }
Not writing a lot of forked stuff that does things like this, I was wondering what the "best" approach was? Either of the above, or something completely different that I'm missing by a mile...

mind... boggled... arghhh...

cLive ;-)

Replies are listed 'Best First'.
Re: waiting until a file updates...
by Zaxo (Archbishop) on Dec 21, 2002 at 03:13 UTC

    You could have the child hold a lock on the file. Have the parent try to get the lock after noticing that the child has touched it through stat. The blocking flock call will effectively sleep until it obtains the lock. The -M $file test operator may be tidier than direct use of stat.

    use Fcntl qw(:flock); #... # Child does { open my $foo, '+<', $file or die $!; flock $foo, LOCK_EX | LOCK_NB or die $!; # Reading and Writing close $foo or die $!; } # child goes on... # Parent does sleep 1 while 1 < -M $file; { open my $foo, '<', $file or die $!; flock $foo, LOCK_EX or die $!; sleep 1; unless (kill 0, $child_pid) { # close file, restart child, and redo } # do stuff close $foo or die $!; }

    The child uses non-blocking flock to break the narrow race window between parent and child, occuring between open and flock.

    Alternatively, you could have the child raise a signal when it's done.

    After Compline,
    Zaxo

Re: waiting until a file updates...
by tachyon (Chancellor) on Dec 21, 2002 at 05:52 UTC

    As an alternative to flocking you could write a lock file before the fork. This is removed by the child after the update is complete. The parent just has to wait arround until the lock file is gone to be sure that the update has gone OK. Lockfiles are a bit more portable than flock.

    my $lockfile = '/var/lock/mylockfile'; open L, $lockfile; close L; die "Can't generate lockfile $!" unless -e $lockfile; my $pid = fork(); if ( $pid == 0 ) { # child my $error = update_somefile(); die $error if $error; unlink $lockfile or die $!; # more stuff } elsif ( $pid ) { # parent my $timeout = 0; while ( -e lockfile ) { sleep 1; $timeout++; die "of old age waiting for child to finish update" if $timeou +t > $whatever_time; } # lockfile gone, child has completed update so... } else { die "Fork failed!\n"; }

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: waiting until a file updates...
by Aristotle (Chancellor) on Dec 21, 2002 at 00:31 UTC
    You could open a socket pair before forking, then have the parent wait on the reader handle for the child to write something into the writer handle. With alarm or select you can implement a timeout doing so.

    Makeshifts last the longest.

Re: waiting until a file updates...
by waswas-fng (Curate) on Dec 21, 2002 at 07:08 UTC
    Why not fork after you write the file? It sounds as though that step will always block the parent -- so why even fork before that step is done?

    -Waswas
      because the parent is doing something else while the child is processing to the point of writing the file, and will always take less time to do it's task (a few hundred DB queries) than the child (who is reading and parsing a web page, and then storing some data from it).

      cLive ;-)

      PS - thank you Zaxo and tachyon - exactly the idea I was missing :)