I'm working on a Client/Server project where a "progress file" gets updated (potentially quite often) and read from a separate (CGI) process.
I was pretty sure having the writer reopening the progress file could cause the reader to have occasional problems, and also fairly sure that
writing to a tempfile instead (and then moving the tempfile over the progress file) would be much safer (ie. atomic).
But why Google it when you can write code to test it instead? ;-)
Here's the result, which indicates I was correct on both counts, and happily the latter seems to be atomic enough that an error never occurs.
Set or clear the value of $unsafe to try the different algorithms.
#!/usr/bin/perl
###############
## Libraries ##
###############
use strict;
use warnings;
use File::Copy;
use Function::Parameters;
use IO::File;
##################
## User-defined ##
##################
my $file = 'file.txt';
my $rdelay = 0.03; # Read delay: 3/100th of a second
my $wdelay = 0.01; # Write delay: 1/100th of a second
my $unsafe = 1; # Set to zero to call the "safe" write algorit
+hm
##################
## Main Program ##
##################
$| = 1;
if (fork) {
writer($unsafe);
} else {
reader();
}
#################
## Subroutines ##
#################
#
# Writes to a file many times per second.
#
fun writer($unsafe) {
my $count = 0;
while (1) {
if ($unsafe) {
writefile1($file, $count++);
} else {
writefile2($file, $count++);
}
# Sleep for 1/100th of a second
select(undef, undef, undef, $wdelay);
}
}
#
# Reads from the file, displaying number of total errors
# (each time the $count was undefined).
#
fun reader() {
sleep 1; # Give the writer time to create the file initiall
+y
my $nerrs = 0; # How many total errors did we get?
while (1) {
my $count = readfile($file);
if ($count) {
printf "%8d, ", $count;
} else {
printf "\nTotal errors = %d\n", ++$nerrs;
sleep 1;
}
}
select(undef, undef, undef, $rdelay);
}
#
# Algorithm 1
#
# Writes the $value directly to the file
# This turns out to be quite prone to error when the file is read.
#
fun writefile1($file, $value) {
my $fh = IO::File->new;
open($fh, '>', $file) or die "Can't write '$file' ($!)\n";
print $fh "$value\n";
close($fh);
}
#
# Algorithm 2
#
# Writes the $value to a temp file, then moves the tempfile over the
# actual destination. This turns out to be quite safe for reading.
#
fun writefile2($file, $value) {
my $fh = IO::File->new;
my $tmp = 'tmp.txt';
open($fh, '>', $tmp) or die "Can't write '$file' ($!)\n";
print $fh "$value\n";
close($fh);
move($tmp, $file);
}
#
# Reads the $value from the $file
#
fun readfile($file) {
my $fh = IO::File->new;
open($fh, '<', $file) or die "Can't read '$file' ($!)\n";
my $value = <$fh>;
defined($value) or return 0;
chomp($value);
close($fh);
return $value;
}
say
substr+lc crypt(qw $i3 SI$),4,5
Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
Read Where should I post X? if you're not absolutely sure you're posting in the right place.
Please read these before you post! —
Posts may use any of the Perl Monks Approved HTML tags:
- a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
|
For: |
|
Use: |
| & | | & |
| < | | < |
| > | | > |
| [ | | [ |
| ] | | ] |
Link using PerlMonks shortcuts! What shortcuts can I use for linking?
See Writeup Formatting Tips and other pages linked from there for more info.
|
|