views:

491

answers:

2

Hi,

I'm mostly familiar with Java, C and C++ in which there are ways to control that only one thread is accessing a resource at any given time. Now I'm in search for something similar but in PHP 5.x.

To formulate my problem with one example:

I have an ASCII-file which only stores a number, the value of a page load counter. At application deployment the file will simply hold a 0. For each access the value will be incremented by one. The goal is to keep track of page loads.

The problem comes when many users are concurrently accessing the page containing the counter. When thread A has read the current value, let's say it is 11, another thread which we call B reads the value, still 11. Then the first thread A increments the read value and writes 12 in the file and closes it. Then the second thread B, increments the read value, which was 11, gets 12 and writes that into the file. The value 12 is stored in the file, when it really should have been 13.

In another programming language I would have solved this using a mutex. I understand there are mutexes, shared memory and other funcionality as part of modules. But I would like a solution which works on "most servers" out there. Platform independent. Installed on most cheap web hosts. Is there a good solution to this problem? And if there isn't, which way would you take if using a database is not an option?

+5  A: 

You could try php's variant of flock (http://www.php.net/flock)

I would envision something similar to (this assumes that the file /tmp/counter.txt already exists and has a counter in the file):

<?php

$fp = fopen("/tmp/counter.txt", "r+");

echo "Attempt to lock\n";
if (flock($fp, LOCK_EX)) {
  echo "Locked\n";
  // Read current value of the counter and increment
  $cntr = fread($fp, 80);
  $cntr = intval($cntr) + 1;

  // Pause to prove that race condition doesn't exist
  sleep(5);

  // Write new value to the file
  ftruncate($fp, 0);
  fseek($fp, 0, SEEK_SET);
  fwrite($fp, $cntr);
  flock($fp, LOCK_UN); // release the lock
  fclose($fp);
}

?>
terson
Informative answer with a solution to the example problem, thank you! I think calling flock($fp, LOCK_UN) is nice even though fclose($fp) also releases the lock. I have one question though, why do you append the spaces before the newline in fwrite($fp, $cntr . " \n")?
DeletedAccount
Oops, that was a left over of my original development. I was going to just use fseek without the ftruncate. Adding the spaces, guarantees that it erases any other characters that were on the line (more important for decrementing.) Edited above, since it is not needed anymore.
terson
+4  A: 

PHP's flock() function is the route to go. However, you have to make sure that all accesses to the file are protected by a call to flock() first. PHP won't check if the file is locked unless you explicitly make the call to do so.

The concept is virtually identical as with mutexes (protecting shared resources, et al), but it's important enough to bear special emphasis.

Magsol