note
afoken
<blockquote>am I missing something?</blockquote>
<p>Yes. I did not write about the [doc://system] function, but about the system (as in "operating system"). I cited <c>alarm</c> in [doc://perlport]: "<i>Emulated using timers that must be explicitly polled whenever Perl wants to dispatch "safe signals" and therefore <b>cannot interrupt blocking system calls</b>. (Win32)</i>" (Emphasis mine) I wrote "<i> Replace sleep with something that blocks for 5 seconds outside perl (i.e. <b>in the system</b>) and alarm will no longer work.</i>"</p>
<p><b>Demo, using [doc://flock], a function using a system call that may block for a long time:</b></p>
<c>#!/usr/bin/perl
use strict;
use warnings;
use v5.10;
use Fcntl qw( LOCK_EX LOCK_UN );
use autodie qw( open flock close );
sub main
{
# create a tempfile
open my $h,'>>','tempfile.tmp';
close $h;
# start a background process that locks the tempfile for 10 seconds
if ($^O eq 'MSWin32') {
system 1,$^X,$0,'locker';
} else {
my $pid=fork() // die "Can't fork: $!";
unless ($pid) {
exec $^X,$0,'locker' or die "Can't exec: $!";
}
}
sleep 1; # wait one second for the helper process;
open $h,'>>','tempfile.tmp';
$@='';
my $start=time();
eval {
local $SIG{'ALRM'}=sub { die "timeout" };
say 'main: alarm 5';
alarm(5);
say 'main: flock LOCK_EX';
flock($h,LOCK_EX);
say 'main: alarm 0';
alarm(0);
};
my $err=$@ || 'successfully locked';
my $stop=time();
say "main: $err";
say 'main: ',$stop-$start,' seconds have passed';
close $h;
# allow locker() to finish before returning to command prompt
($^O eq 'MSWin32') ? sleep 1 : wait;
}
sub locker
{
open my $h,'>>','tempfile.tmp';
say 'locker: flock LOCK_EX';
flock($h,LOCK_EX);
say 'locker: locked';
say 'locker: sleep 10';
sleep 10;
say 'locker: flock LOCK_UN';
flock($h,LOCK_UN);
say 'locker: unlocked';
close $h;
}
@ARGV ? locker() : main();</c>
<p><b>How the demo works:</b></p>
<p>Without arguments, <c>main()</c> is invoked, <c>main()</c> creates a background process (using <c>system(1,@args)</c> on Windows, <c>fork()</c> and <c>exec()</c> on Linux), waits a second, then tries to lock a temp file with a classic timeout construct (<c>eval</c>, <c>$SIG{'ALRM'}=sub { die }</c>, <c>alarm($timeout)</c>, system call, <c>alarm(0)</c>). Time for this is measured. The background process is the same script, but invoked with an argument, so that <c>locker()</c> is invoked instead of <c>main()</c>. Locker locks the temp file for 10 seconds, and because <c>main()</c> waits a second, it will succeed. In <c>main()</c>, the temp file is already locked, so <c>flock()</c> will block until <c>locker()</c> releases the lock OR the system call is interrupted by the ALRM signal caused by <c>alarm()</c>.</p>
<p>On Linux, signals just work. <c>flock()</c> in <c>main()</c> is interrupted by the ARLM signal, the signal handler in <c>$SIG{'ALRM'}</c> is invoked, that terminales the <c>eval</c> block using <c>die()</c> after 5 seconds.</p>
<p>On Windows, signals are emulated. <c>alarm($timeout)</c> sets up a timer that must be polled. This is impossible during a blocking system call like <c>flock()</c>. So <c>flock()</c> blocks until it can aquire the lock after about 9 seconds (<c>locker()</c> waits 10 seconds after locking, <c>main()</c> waits 1 second before trying to lock, 9 seconds remain). <c>$SIG{'ALRM'}</c> is not invoked. <c>alarm(0)</c> disables the timer. The <c>eval</c> block terminates without an error.</p>
<p><b>Output on Linux:</b></p>
<c>>perl lockdemo.pl
locker: flock LOCK_EX
locker: locked
locker: sleep 10
main: alarm 5
main: flock LOCK_EX
main: timeout at lockdemo.pl line 30.
main: 5 seconds have passed
locker: flock LOCK_UN
locker: unlocked
>
</c>
<p><b>Output on Windows:</b></p>
<c>H:\tmp>perl lockdemo.pl
locker: flock LOCK_EX
locker: locked
locker: sleep 10
main: alarm 5
main: flock LOCK_EX
locker: flock LOCK_UN
main: alarm 0
main: successfully locked
main: 10 seconds have passed
locker: unlocked
H:\tmp></c>
<p><b>So why did <c>alarm($timeout)</c> "work" with <c>system "sleep 10"</c>?</b></p>
<p><c>system @args</c> (unlike <c>system 1,@args</c>) <b>waits</b> for the sub-process to terminate <b>in perl</b>. (flock() waits in the operating system!) During that time, perl can poll the timer and thus can emulate the ARLM signal.</p>
<p>Alexander</p>
<div class="pmsig"><div class="pmsig-747201">
--<br>
Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
</div></div>
1127475
1127487