Skip to content

Instantly share code, notes, and snippets.

@PeterJCLaw
Created January 22, 2012 18:57
Show Gist options
  • Select an option

  • Save PeterJCLaw/1658197 to your computer and use it in GitHub Desktop.

Select an option

Save PeterJCLaw/1658197 to your computer and use it in GitHub Desktop.
PHP Lock Handler
<?php
/**
* A helper class for file locking stuff.
* Locks obtained through this class are shared with the entire request,
* allowing multiple objects to get their own locks on the same file
* without deadlocking internally.
*/
class LockHandler
{
public static function getInstance()
{
static $instance = null;
if ($instance === null)
{
$instance = new LockHandler();
}
return $instance;
}
private $handles = array();
/**
* Gets an internally shared lock on the given file.
* @param lockfile: The path to the file to lock.
* It is recommended that this be an absolute path to avoid
* attempting to lock the same file twice.
* @returns: The resource from opening the file, opened with file_open.
*/
public function lock($lockfile)
{
if (!isset($this->handles[$lockfile]))
{
$resource = file_lock($lockfile);
$this->handles[$lockfile] = new stdClass();
$this->handles[$lockfile]->count = 0;
$this->handles[$lockfile]->handle = $resource;
}
$this->handles[$lockfile]->count++;
return $this->handles[$lockfile]->handle;
}
/**
* Releases a lock on the given file or resource.
* @param resource: The path to the file to lock, or the resource from opening it.
* It is recommended that this be the resource.
*/
public function unlock($resource)
{
$wrapper = null;
$lockfile = null;
if (is_resource($resource))
{
foreach ($this->handles as $path => $res)
{
if ($res->handle === $resource)
{
$wrapper = $res;
$lockfile = $path;
break;
}
}
}
else if (is_string($resource))
{
$lockfile = $resource;
$wrapper = @$this->handles[$lockfile];
}
if ($wrapper === null)
{
throw new Exception("Cannot unlock unknown item '$resource'.", E_INTERNAL_ERROR);
}
$wrapper->count--;
$ret = true;
if ($wrapper->count === 0 && ($ret = file_unlock($wrapper->handle)))
{
unset($this->handles[$lockfile]);
}
return $ret;
}
/**
* Testing method.
* Returns the number of active repo hanldes.
* Intended to help catch potential deadlocks with other threads.
*/
public function handleCount()
{
return count($this->handles);
}
}
<?php
$lh = LockHandler::getInstance();
// $testWorkPath
$file = $testWorkPath.'/bacon';
$lock1 = $lh->lock($file);
test_nonempty($lock1, 'Failed to get first lock on file!');
$lock2 = $lh->lock($file);
test_nonempty($lock2, 'Failed to get second lock on file!');
test_true($lock1 === $lock2, 'Simultaneous locks on the same file should be the same resource');
test_equal($lh->handleCount(), 1, 'Two locks on the same file should be stored as a single handle');
$lh->unlock($lock2);
test_equal($lh->handleCount(), 1, 'Releasing one lock on the file should not remove the lock completely');
$lh->unlock($file);
test_equal($lh->handleCount(), 0, 'Releasing the remaining lock on the file should remove the lock completely');
$lock3 = $lh->lock($file);
test_nonempty($lock3, 'Failed to get lock on file after closing all previous handles');
test_equal($lh->handleCount(), 1, 'Getting another lock on the file should be stored as a single handle');
$lh->unlock($file);
test_equal($lh->handleCount(), 0, 'Releasing third lock on the file should remove the lock completely');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment