Created
November 29, 2011 22:18
-
-
Save gyuchang/1406835 to your computer and use it in GitHub Desktop.
Achieving local mutex via a lock file
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 | |
| 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')); |
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
| #!/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