PHP Atomic File Locks

Still staying away from my WordPress look and taking on a PHP topic.

Welcome to the world of caching, you need to write something to a file, you need something else to access it, but things go wrong if lots of processes all try writing to the same file.

The obvious solution, PHP’s own flock (file lock) function, can be used with a simple block of code:

<?php
$fp = fopen("/tmp/lock.txt", "r+");

if (flock($fp, LOCK_EX)) { // do an exclusive lock
    fwrite($fp, "Write something heren");
    flock($fp, LOCK_UN); // release the lock
} else {
    echo "Couldn't get the lock!";
}

fclose($fp);
?>

The catch with that lovely solution is that it doesn’t actually work if you’ve fired off multiple thread to do the processing… hit it with multiple concurrent requests and it’s more than likely that multiple of them will get past the file lock and write to the file.  This happens because the flock command is non-atomic in this case i.e. more that one instance of it can run at the same time against the same file. While one flock command is running in one process, another process can flock the file and they can both complete before the other sets the lock meaning that they both gain the file lock.

To avoid this we need to use an atomic function, about the only one that PHP has that is useful is link.  This means rather than using the purpose designed flock command for what it was designed for, we have to hack a solution using link instead, as follows:

<?PHP
if ( @link( '/tmp/null', "/tmp/lock.lck" ) ){

  $fp = fopen("/tmp/lock.txt", "r+");
  fwrite($fp, "Write something heren");
  fclose($fp);

  unlink( "/tmp/lock.lck" );
} else {
  echo "Failed at linking: /tmp/lock.lck";
}
?>

Here, we assume a file exists at /tmp/null, this can just be an empty file, and we create a hard link of it to /tmp/lock.lck, inside this check we can then open the file as normal and work on it knowing that no other process might be trying to do the same thing.  The advantage of link’s atomicity is that once one call to link starts, another will have to wait before starting hence multiple processes won’t be able to incorrectly get past the file lock stage.

This means we can now lock our file and use it in peace!