Created
January 22, 2012 18:57
-
-
Save PeterJCLaw/1658197 to your computer and use it in GitHub Desktop.
PHP Lock Handler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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