Skip to content

Instantly share code, notes, and snippets.

@gyuchang
Created November 29, 2011 22:18
Show Gist options
  • Select an option

  • Save gyuchang/1406835 to your computer and use it in GitHub Desktop.

Select an option

Save gyuchang/1406835 to your computer and use it in GitHub Desktop.
Achieving local mutex via a lock file
<?php
class LocalLock {
const CONST_IO_TIMEOUT = 2; // in sec, 2 sec
const CONST_IO_SLEEP = 50000; // in usec, 50 msec
public static $base = '/tmp';
public static $debug = false;
private static $_lockfiles = array();
private $fname_lock;
public static function instantiate($tag) {
// check $tag for illegal characters
if (preg_match('/[^0-9a-z.]/i', $tag)) {
if (self::$debug) {
throw new Exception('illegal character in tag');
}
return false;
}
return new self($tag);
}
private function __construct($tag) {
$this->fname_lock = (sprintf('%s/%s.lock', self::$base, $tag));
}
public function is_active() {
if (!file_exists($this->fname_lock)) { return false; }
// file exists, check if it's still active
// get PID of the process the lock file is supposed to be opened by
$pid = @file_get_contents($this->fname_lock);
if ($pid === false) {
return file_exists($this->fname_lock); // does the lock file still exist?
} elseif (!$pid) {
// PID hasn't been written yet. try again after sleep
usleep(self::CONST_IO_SLEEP);
$pid = @file_get_contents($this->fname_lock);
if ($pid === false) {
return file_exists($this->fname_lock); // does the lock file still exist?
} elseif (!$pid) {
// if there's no PID (or no file) even after sleep, it's inactive for sure
@unlink($this->fname_lock);
// see if another process just created a new lock file
return file_exists($this->fname_lock);
}
}
// see if the owner process is still running and holding the lock file open
if (is_dir("/proc/{$pid}/fd")) {
$fd_files = @scandir("/proc/{$pid}/fd", $SCANDIR_SORT_DESCENDING=true); // lower numbers for standard IO
if ($fd_files) {
$rpath_lock = realpath($this->fname_lock); // resolve symbolic links
foreach ($fd_files as $file) {
if ($file[0] == '.') continue;
if (realpath("/proc/{$pid}/fd/{$file}") == $rpath_lock) { return true; }
}
}
}
// it's either the process was just terminated or we don't have permission
// either way, the lock file should be regarded as inactive
@unlink($this->fname_lock);
// see if another process just created a new lock file
return file_exists($this->fname_lock);
}
public function get($timeout=0) {
// set timeout
$until = microtime(true) + $timeout;
// create a new lock file
do {
if (!$this->is_active() && ($fp_lock = @fopen($this->fname_lock, 'x'))) {
@fwrite($fp_lock, getmypid());
@fflush($fp_lock);
// successfully created a new file
self::$_lockfiles[$this->fname_lock] = $fp_lock;
return true;
}
// if timeout is NOT specified, attempt once and return (non-blocking)
if (empty($timeout)) { return false; }
// lock file exists, sleep 50m sec and try again
usleep(self::CONST_IO_SLEEP);
} while (microtime(true) <= $until);
if (self::$debug) {
if (file_exists($this->fname_lock)) {
$message = 'LocalLock::get() - lock file exists:'.$this->fname_lock;
} else {
$message = 'LocalLock::get() - fail to create a new lock file:'.$this->fname_lock;
}
throw new Exception($message);
}
return false;
}
public function release() {
if (empty(self::$_lockfiles[$this->fname_lock])) { return true; }
@fclose(self::$_lockfiles[$this->fname_lock]);
@unlink($this->fname_lock);
unset(self::$_lockfiles[$this->fname_lock]);
return true;
}
public static function shutdown() {
foreach (self::$_lockfiles as $fname => $fp) {
@fclose($fp);
@unlink($fname);
}
}
}
register_shutdown_function(array('LocalLock', 'shutdown'));
#!/bin/bash
exec 0>&- # close stdin
exec 1>&- # close stdout - comment out this line to see debug message
exec 2>&- # close stderr
base='/tmp'
list=`find $base -type f -name '*.lock' -print 2>/dev/null`
for lockfile in $list; do
# see if any process is opening this file
`/usr/sbin/lsof $lockfile 2>/dev/null 1>/dev/null`
if [[ "$?" -gt 0 ]]; then
# delete
echo "$lockfile is not in use, attempting to delete"
`rm -r $lockfile`
if [[ "$?" -eq 0 ]]; then
echo "$lockfile deleted"
fi
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment