icius has asked for the wisdom of the Perl Monks concerning the following question:

I have a very simple task that I am trying to accomplish. I have a child process that gets forked from a parent. The child runs an infinite loop. In each loop iteration I want it to check a value being set in the parent. If the parent value changes I want it to exit the loop. I am on Win32 Activeperl so please no %SIG solutions unless you know of some that work. Here is my current code:

use strict; $|++; my $stream = *STDOUT; my $thingy = [ "\\", "|", "/", "-" ]; my $rate = 0.175; my $step = 0; my $spin_stop = 0; FORK: { if( my $code_pid = fork() ) { sleep(5); $spin_stop = 1; } elsif( defined $code_pid ) { print "Process executing.."; _spin(); exit 0; } elsif( $! =~ /No more process/ ) { sleep 2; redo FORK; } else { die "ERROR: can't fork! $!"; } } print "\nProcess Complete\n"; sub _spin { SPIN: while(1) { my $old_fh = select($stream); local $| = 1; print $stream $$thingy[$step], chr(8) x length($$thingy[$step]); select($old_fh); $step = ( $step+1 > $#$thingy ? 0 : $step+1 ); select(undef,undef,undef, $rate); last SPIN if($spin_stop == 1); } return; }

Obviously the $spin_stop variable in the forked child seems to have no relationship whatsoever to the $spin_stop in the parent process, but you can see what I am trying to do.

Replies are listed 'Best First'.
Re: Child reading from Parent
by Jenda (Abbot) on Feb 19, 2003 at 23:42 UTC

    I'd probably use a Win32::Semaphore for the signal.

    use Win32::Semaphore; my $sem = new Win32::Semaphore(0,1) ... if( my $code_pid = fork() ) { sleep(5); $sem->release(); } elsif( defined $code_pid ) { print "Process executing.."; _spin(); bless $sem, 'do not destroy'; exit 0; ... sub _spin { SPIN: while(! $sem->wait(0)) { ...

    The bless $sem, 'do not destroy'; hack is necessary because the fork() did not create a new process, but a new thread under Windows and if you allowed Win32::Semaphore to do proper destroy you'd destroy even the parent's object. (The $sem objects in both threads share the same handle to the semaphore.)

    Another option would be to start using "proper" threads. (See perldoc threads. That would allow you to share variables between threads.

    HTH, Jenda

      I liked the thread idea. How are threads on overhead and processing? The code below seems to work well. However I get the following message when the "Processing Complete" message is printed: "A thread exited while 2 other threads were still running.". Any idea why?
      use threads; use threads::shared; use strict; $|++; my $stream = *STDOUT; my $thingy = [ "\\", "|", "/", "-" ]; my $rate = 0.175; my $step = 0; my $spin_stop : shared; $spin_stop = 0; print "Processing..."; my $thread = threads->create(\&_spin); select(undef,undef,undef, 5); print "\nProcessing Completed\n"; $spin_stop = 1; sub _spin { SPIN: while(1) { my $old_fh = select($stream); local $| = 1; print $stream $$thingy[$step], chr(8) x length($$thingy[$step]); select($old_fh); $step = ( $step+1 > $#$thingy ? 0 : $step+1 ); select(undef,undef,undef, $rate); last SPIN if($spin_stop == 1); } return; }
        OK, I added a $thread->join(); after the $spin_stop = 1; statement which gets rid of the error, but is it good practice?

        If you are running the script under Windows even the fork() uses threads. See perldoc perlfork.

        The big difference is that with fork() you do not get the shared variables and the ->join(). If you use the threads you SHOULD use the ->join(), if you use fork() you should use waitpid(). Though in this case since you do not create many threads the waitpid() is not that important.

        Jenda

Re: Child reading from Parent
by perrin (Chancellor) on Feb 19, 2003 at 22:57 UTC
    Just use a file. It's by far the simplest way to share data. Have your child process check the -M time on a "stop" file to exit if it was touched after the process was forked.

      As a simple flag, a file may work but I'm not crazy about that one. If someone else touches it they affect the process ... Files, in general, make poor IPC choices. For a simple flag, pick a semaphore model. Depends on your platform which one fits -- I'm only familiar with the UNIX System V semapahores but have not used them from Perl. A signal can be used as a semaphore, but that also keeps me awake at night. It's asynchronous which means that anything called from the handler has to be reentrant. You can sometimes get away with setting a global flag and checking it in your main loop in a pinch though. Again, I have not done any of that in Perl so just ideas ...

        I'm not sure what your statement about files making a poor IPC choice is based on. I consider the ability to stop the process by touching the file a feature.

        Files are simple and portable, and don't require a lot of extra modules to be compiled. This monk stated he is on Windows, so his options are limited, especially if he cares about being cross-platform.

Re: Child reading from Parent
by steves (Curate) on Feb 19, 2003 at 22:51 UTC

    Once you fork, each process has its own copy of variables. The easy way to remember that is that a fork creates a new copy of the running parent. In most process models (most notably UNIX) no process can access another's data without explicitly setting up a mechanism to do that. The two common choices are to use threads instead of forking; or to set up some sort of interprocess communication (IPC) between the two processes.