file locking

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
rehfeld
Forum Regular
Posts: 741
Joined: Mon Oct 18, 2004 8:14 pm

file locking

Post by rehfeld »

ive written this function for writing to a file in append mode only. thats its only purpose.

can anyone see where it might fail?

the only possible failures i see are:
1- if its trying to write to a file on a network, because mkdir() isnt atomic in that situation,
and i dont think you can lock a file over a network regardless.

2- if the script ended before the lockdir was able to be removed, and somehow the permissions of the lockdir were changed,
making the lockdir unable to be deleted by php, indefinately locking the file until the lockdir was manually removed. but i dont see how that could happen

ive tried to break it, but cant. it seems to work very good. if someone can break it, please let me know how you did it.

default $timelimit is 3/10ths of a second (how long it will keep trying to acheive a lock before it gives up)
default $stale_age is 5 seconds(if it finds a lock_dir that is older than 5 seconds, it considers it an abandoned lock, and removes it)

Code: Select all

<?php

// emulates php5 microtime(true)
function microtime_float()
{
   list($usec, $sec) = explode(' ', microtime());
   return ((float)$usec + (float)$sec);
}



function locked_filewrite($filename, $data, $time_limit = 0.3, $stale_age = 5)
{
    ignore_user_abort(1);
    $lock_dir = $filename . '.lock';

    // if a dir already exists w/ this name, either another process is writing,
    // or another process failed to delete the lock dir. 
    // if the dir is more than $stale_age seconds old, we assume
    // another process failed to delete it, and so we clean up for it.
    if (is_dir($lock_dir)) {
        if ((time() - filemtime($lock_dir)) > $stale_age) {
            rmdir($lock_dir);
        }
    }

    // mkdir is supposed to be atomic, so theoretically, 
    // there is no race condition
    $locked = @mkdir($lock_dir);

    // if we failed to create a lock_dir, we will keep trying 
    // for up to $time_limit seconds,
    if ($locked === false) {
        $time_start = microtime_float();
        do {
            if ((microtime_float() - $time_start) > $time_limit) {
                break; // give up, couldnt acheive a lock
            }
            $locked = @mkdir($lock_dir);
        } while ($locked === false);
    }

    $bytes_written = false;

    // if we were able to create a lock_dir, we have an exclusive lock, 
    // and open the file and write to it.
    if ($locked === true) {
        if ($fp = fopen($filename, 'a')) {
            $bytes_written = fwrite($fp, $data);
            fclose($fp);
        }
        rmdir($lock_dir); // remove our lock
    }

    ignore_user_abort(0);
    return $bytes_written;
}

?>
Post Reply