Created
September 10, 2012 22:54
-
-
Save nickyleach/3694555 to your computer and use it in GitHub Desktop.
Simple redis-backed Mutex
This file contains 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
<? | |
class Lock { | |
const TIMEOUT = 20; | |
const SLEEP = 100000; | |
/** | |
* Stores the expire time of the currently held lock | |
* @var int | |
*/ | |
protected static $expire; | |
/** | |
* Gets a lock or waits for it to become available | |
* @param mixed $key Item to lock | |
* @param int $timeout Time to wait for the key (seconds) | |
* @return mixed The key | |
* @throws LockException If the key is invalid | |
* @throws LockTimeoutException If the lock is not acquired before the method times out | |
*/ | |
public static function get($key, $timeout = null){ | |
if(!$key) throw new LockException("Invalid Key"); | |
$start = time(); | |
do{ | |
self::$expire = self::timeout(); | |
if($acquired = (Redis::setnx("Lock:{$key}", self::$expire))) break; | |
if($acquired = (self::recover($key))) break; | |
if($timeout === 0) break; | |
usleep(self::SLEEP); | |
} while(!is_numeric($timeout) || time() < $start + $timout); | |
if(!$acquired) throw new LockTimeoutException("Timeout exceeded"); | |
return $key; | |
} | |
/** | |
* Releases the lock | |
* @param mixed $key Item to lock | |
* @throws LockException If the key is invalid | |
*/ | |
public static function release($key){ | |
if(!$key) throw new LockException("Invalid Key"); | |
// Only release the lock if it hasn't expired | |
if(self::$expire > time()) Redis::del("Lock:{$key}"); | |
} | |
/** | |
* Generates an expire time based on the current time | |
* @return int timeout | |
*/ | |
protected static function timeout(){ | |
return (int) (time() + self::TIMEOUT + 1); | |
} | |
/** | |
* Recover an abandoned lock | |
* @param mixed $key Item to lock | |
* @return bool Was the lock acquired? | |
*/ | |
protected static function recover($key){ | |
if(($lockTimeout = Redis::get("Lock:{$key}")) > time()) return false; | |
$timeout = self::timeout(); | |
$currentTimeout = Redis::getset("Lock:{$key}", $timeout); | |
if($currentTimeout != $lockTimeout) return false; | |
self::$expire = $timeout; | |
return true; | |
} | |
} | |
class LockException extends RuntimeException {} | |
class LockTimeoutException extends LockException {} | |
?> |
This file contains 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
<? | |
Lock::get('foo'); | |
$foo = $api->get('foo'); | |
$foo['bar'] = 'baz'; | |
$api->put('foo', $foo); | |
Lock::release('foo'); | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
$timout has a typo in line 35