faqts : Computers : Programming : Languages : PHP : Common Problems : Tips and Tricks

+ Search
Add Entry AlertManage Folder Edit Entry Add page to http://del.icio.us/
Did You Find This Entry Useful?

11 of 17 people (65%) answered Yes
Recently 4 of 6 people (67%) answered Yes

Entry

Do I need to use locking when keeping a counter in a file?
Why does my counter keep resetting to zero?
Will I have concurrency problems with my counter?

Jun 29th, 1999 23:26
Nathan Wallace, M.Brands


You need to be aware of concurrency problems when writing a counter.  If
several people visit your page at the same time, things can go wrong.
Here is a short example from the mailing list which explains some of the
possible problems.

(Assume the counterfile exists and contains 6)

process 1                       process 2
----------------------------------------------------------------------
$counterFile = "...";
if (!file_exists($counterFile))
$fp = fopen($counterFile,rw);
$num = fgets($fp,20);
$num += 1;
print "$num";
exec("rm -rf $counterFile");
                                $counterFile = "...";
                                if (!file_exists($counterFile)) {
                                    exec("echo $num > $counterFile");
                                    exec("echo 1 > $counterFile");
                                }
                                $fp = fopen($counterFile,rw);
                                $num = fgets($fp,20);
                                $num += 1;
                                print "$num";
                                exec("rm -rf $counterFile");
                                exec("echo $num > $counterFile");

Each line represends one little step in time. I'm assume you machine
only has one cpu, so that's why only one line of code is executed
at a time. (In reality, lines may be interrupted in the middle
of doing something, not conveniently after they've finished.)

Process 1 encounters a valid file, reads it, inreases the number
and erases the file. By this time, process 2 tries to read it to.
After that, process 1 creates a new file with the value 7 in it.
Process 1 is now finished. Process 2, having failed to open the
counterfile, creates a new one (overwrites actually) with the
value 1. It then reads the file, increments the number (now 2)
and erases the value. Then, a new file with the value 2 is created.
So, after both process 1 and 2 have finished, the counter contains
2 and not the expected 8!

This may seem far fetched, but if you have enough processes trying
to update counter.text at the same time, something like this is
going to happen.

So, to build a better mousetrap, you need to make sure only one
process can update counter.text at one. Concurrent reading is not
a problem, as long as a read cannot combined with a write. There
are several ways to solve this common problem, such as using a
semaphore or locking counter.text with flock. (There are more
ways.) Also, you should try not to use the exec's.
One, because it's slow, and two, because it's safer to do as much
as possible from PHP. (If you did it completely in PHP, you might
not have noticed this problem so soon, although it would occur
if you waited long enough.) On, and you might want to leave out
the 'rm -rf' bit, since it doesn't do anything useful. Even worse,
it causes more diskaccess than simply overwriting the content of
the counter. If you were to remove the rm, this script would
already run a lot better (although it would still be faulty).

Btw. you may want to use 0 as a starting value for your counter,
since you're starting to count at 2 and not 1 visitor. But only
an complete ass (like myself ;) would complain about that...