From 733dbbfb8b3a78b749f482005c48464e65d235e4 Mon Sep 17 00:00:00 2001 From: resurtm Date: Tue, 25 Jun 2013 09:54:07 +0600 Subject: [PATCH] Mutex WIP. --- extensions/mutex/yii/mutex/DbMutex.php | 41 +++++++++++ extensions/mutex/yii/mutex/FileMutex.php | 86 +++++++++++++++++++++++ extensions/mutex/yii/mutex/MssqlMutex.php | 56 +++++++++++++++ extensions/mutex/yii/mutex/Mutex.php | 73 ++++---------------- extensions/mutex/yii/mutex/MysqlMutex.php | 57 +++++++++++++++ extensions/mutex/yii/mutex/cache/Mutex.php | 89 ------------------------ extensions/mutex/yii/mutex/db/Mutex.php | 51 -------------- extensions/mutex/yii/mutex/db/mssql/Mutex.php | 56 --------------- extensions/mutex/yii/mutex/db/mysql/Mutex.php | 72 ------------------- extensions/mutex/yii/mutex/unix/Mutex.php | 99 --------------------------- 10 files changed, 254 insertions(+), 426 deletions(-) create mode 100644 extensions/mutex/yii/mutex/DbMutex.php create mode 100644 extensions/mutex/yii/mutex/FileMutex.php create mode 100644 extensions/mutex/yii/mutex/MssqlMutex.php create mode 100644 extensions/mutex/yii/mutex/MysqlMutex.php delete mode 100644 extensions/mutex/yii/mutex/cache/Mutex.php delete mode 100644 extensions/mutex/yii/mutex/db/Mutex.php delete mode 100644 extensions/mutex/yii/mutex/db/mssql/Mutex.php delete mode 100644 extensions/mutex/yii/mutex/db/mysql/Mutex.php delete mode 100644 extensions/mutex/yii/mutex/unix/Mutex.php diff --git a/extensions/mutex/yii/mutex/DbMutex.php b/extensions/mutex/yii/mutex/DbMutex.php new file mode 100644 index 0000000..3699c36 --- /dev/null +++ b/extensions/mutex/yii/mutex/DbMutex.php @@ -0,0 +1,41 @@ + + * @since 2.0 + */ +abstract class DbMutex extends Mutex +{ + /** + * @var Connection|string the DB connection object or the application component ID of the DB connection. + * After the Mutex object is created, if you want to change this property, you should only assign + * it with a DB connection object. + */ + public $db = 'db'; + + /** + * Initializes generic database table based mutex implementation. + * @throws InvalidConfigException if [[db]] is invalid. + */ + public function init() + { + parent::init(); + if (is_string($this->db)) { + $this->db = Yii::$app->getComponent($this->db); + } + if (!$this->db instanceof Connection) { + throw new InvalidConfigException('Mutex::db must be either a DB connection instance or the application component ID of a DB connection.'); + } + } +} diff --git a/extensions/mutex/yii/mutex/FileMutex.php b/extensions/mutex/yii/mutex/FileMutex.php new file mode 100644 index 0000000..4a949d0 --- /dev/null +++ b/extensions/mutex/yii/mutex/FileMutex.php @@ -0,0 +1,86 @@ + + * @since 2.0 + */ +class FileMutex extends Mutex +{ + /** + * @var string the directory to store mutex files. You may use path alias here. + * If not set, it will use the "mutex" subdirectory under the application runtime path. + */ + public $mutexPath = '@runtime/mutex'; + /** + * @var resource[] stores all opened lock files. Keys are lock names and values are file handles. + */ + private $_files = array(); + + + /** + * Initializes mutex component implementation dedicated for UNIX, GNU/Linux, Mac OS X, and other UNIX-like + * operating systems. + * @throws InvalidConfigException + */ + public function init() + { + if (stripos(php_uname('s'), 'win') === 0) { + throw new InvalidConfigException('FileMutex does not have MS Windows operating system support.'); + } + $this->mutexPath = Yii::getAlias($this->mutexPath); + if (!is_dir($this->mutexPath)) { + mkdir($this->mutexPath, 0777, true); + } + } + + /** + * This method should be extended by concrete mutex implementations. Acquires lock by given name. + * @param string $name of the lock to be acquired. + * @param integer $timeout to wait for lock to become released. + * @return boolean acquiring result. + */ + protected function acquireLock($name, $timeout = 0) + { + $file = fopen($this->mutexPath . '/' . md5($name) . '.lock', 'w+'); + if ($file === false) { + return false; + } + $waitTime = 0; + while (!flock($file, LOCK_EX | LOCK_NB)) { + $waitTime++; + if ($waitTime > $timeout) { + fclose($file); + return false; + } + sleep(1); + } + $this->_files[$name] = $file; + return true; + } + + /** + * This method should be extended by concrete mutex implementations. Releases lock by given name. + * @param string $name of the lock to be released. + * @return boolean release result. + */ + protected function releaseLock($name) + { + if (!isset($this->_files[$name]) || !flock($this->_files[$name], LOCK_UN)) { + return false; + } else { + fclose($this->_files[$name]); + unset($this->_files[$name]); + return true; + } + } +} diff --git a/extensions/mutex/yii/mutex/MssqlMutex.php b/extensions/mutex/yii/mutex/MssqlMutex.php new file mode 100644 index 0000000..d267699 --- /dev/null +++ b/extensions/mutex/yii/mutex/MssqlMutex.php @@ -0,0 +1,56 @@ + + * @since 2.0 + */ +class MssqlMutex extends Mutex +{ + /** + * Initializes Microsoft SQL Server specific mutex component implementation. + * @throws InvalidConfigException if [[db]] is not Microsoft SQL Server connection. + */ + public function init() + { + parent::init(); + $driverName = $this->db->driverName; + if ($driverName !== 'sqlsrv' && $driverName !== 'dblib' && $driverName !== 'mssql') { + throw new InvalidConfigException('In order to use MssqlMutex connection must be configured to use MS SQL Server database.'); + } + } + + /** + * This method should be extended by concrete mutex implementations. Acquires lock by given name. + * @param string $name of the lock to be acquired. + * @param integer $timeout to wait for lock to become released. + * @return boolean acquiring result. + * @throws \BadMethodCallException not implemented yet. + * @see http://msdn.microsoft.com/en-us/library/ms189823.aspx + */ + protected function acquireLock($name, $timeout = 0) + { + throw new \BadMethodCallException('Not implemented yet.'); + } + + /** + * This method should be extended by concrete mutex implementations. Releases lock by given name. + * @param string $name of the lock to be released. + * @return boolean release result. + * @throws \BadMethodCallException not implemented yet. + * @see http://msdn.microsoft.com/en-us/library/ms178602.aspx + */ + protected function releaseLock($name) + { + throw new \BadMethodCallException('Not implemented yet.'); + } +} diff --git a/extensions/mutex/yii/mutex/Mutex.php b/extensions/mutex/yii/mutex/Mutex.php index 9f47501..297abaf 100644 --- a/extensions/mutex/yii/mutex/Mutex.php +++ b/extensions/mutex/yii/mutex/Mutex.php @@ -18,7 +18,8 @@ abstract class Mutex extends Component { /** * @var boolean whether all locks acquired in this process (i.e. local locks) must be released automagically - * before finishing script execution. Defaults to true. Setting this property to true + * before finishing script execution. Defaults to true. Setting this property to true means that all locks + * acquire in this process must be released in any case (regardless any kind of errors or exceptions). */ public $autoRelease = true; /** @@ -33,14 +34,13 @@ abstract class Mutex extends Component public function init() { if ($this->autoRelease) { - $references = new stdClass(); - $references->mutex = $this; - $references->locks = &$this->_locks; - register_shutdown_function(function ($refs) { - foreach ($refs->locks as $lock) { - $refs->mutex->release($lock); + $mutex = $this; + $locks = &$this->_locks; + register_shutdown_function(function () use ($mutex, &$locks) { + foreach ($locks as $lock) { + $mutex->release($lock); } - }, $references); + }); } } @@ -50,9 +50,9 @@ abstract class Mutex extends Component * false immediately in case lock was already acquired. * @return boolean lock acquiring result. */ - public function acquireLock($name, $timeout = 0) + public function acquire($name, $timeout = 0) { - if ($this->acquire($name, $timeout)) { + if ($this->acquireLock($name, $timeout)) { $this->_locks[] = $name; return true; } else { @@ -65,9 +65,9 @@ abstract class Mutex extends Component * @param string $name of the lock to be released. This lock must be already created. * @return boolean lock release result: false in case named lock was not found.. */ - public function releaseLock($name) + public function release($name) { - if ($this->release($name)) { + if ($this->releaseLock($name)) { $index = array_search($name, $this->_locks); if ($index !== false) { unset($this->_locks[$index]); @@ -79,62 +79,17 @@ abstract class Mutex extends Component } /** - * Checks whether named lock was already opened. - * @param string $name of the lock to be checked. This lock must be already created. - * @return boolean|null whether named lock was already opened. Returns `null` value in case concrete - * mutex implementation does not support this operation. - */ - public function getIsLockAcquired($name) - { - if (in_array($name, $this->_locks)) { - return true; - } else { - return $this->getIsAcquired($name); - } - } - - /** - * Checks whether given lock is local. In other words local lock means that it was opened in the current - * PHP process. - * @param string $name of the lock to be checked. This lock must be already created. - * @return boolean whether named lock was locally acquired. - */ - public function getIsLockLocal($name) - { - return in_array($name, $this->_locks); - } - - /** * This method should be extended by concrete mutex implementations. Acquires lock by given name. * @param string $name of the lock to be acquired. * @param integer $timeout to wait for lock to become released. * @return boolean acquiring result. */ - abstract protected function acquire($name, $timeout = 0); + abstract protected function acquireLock($name, $timeout = 0); /** * This method should be extended by concrete mutex implementations. Releases lock by given name. * @param string $name of the lock to be released. * @return boolean release result. */ - abstract protected function release($name); - - /** - * This method may optionally be extended by concrete mutex implementations. Checks whether lock has been - * already acquired by given name. - * @param string $name of the lock to be released. - * @return null|boolean whether lock has been already acquired. Returns `null` in case this feature - * is not supported by concrete mutex implementation. - */ - protected function getIsAcquired($name) - { - return null; - } - - /** - * This method should be extended by concrete mutex implementations. Returns whether current mutex - * implementation can be used in a distributed environment. - * @return boolean whether current mutex implementation can be used in a distributed environment. - */ - abstract public function getIsDistributed(); + abstract protected function releaseLock($name); } diff --git a/extensions/mutex/yii/mutex/MysqlMutex.php b/extensions/mutex/yii/mutex/MysqlMutex.php new file mode 100644 index 0000000..b04427a --- /dev/null +++ b/extensions/mutex/yii/mutex/MysqlMutex.php @@ -0,0 +1,57 @@ + + * @since 2.0 + */ +class MysqlMutex extends Mutex +{ + /** + * Initializes MySQL specific mutex component implementation. + * @throws InvalidConfigException if [[db]] is not MySQL connection. + */ + public function init() + { + parent::init(); + if ($this->db->driverName !== 'mysql') { + throw new InvalidConfigException('In order to use MysqlMutex connection must be configured to use MySQL database.'); + } + } + + /** + * This method should be extended by concrete mutex implementations. Acquires lock by given name. + * @param string $name of the lock to be acquired. + * @param integer $timeout to wait for lock to become released. + * @return boolean acquiring result. + * @see http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock + */ + protected function acquireLock($name, $timeout = 0) + { + return (boolean)$this->db + ->createCommand('SELECT GET_LOCK(:name, :timeout)', array(':name' => $name, ':timeout' => $timeout)) + ->queryScalar(); + } + + /** + * This method should be extended by concrete mutex implementations. Releases lock by given name. + * @param string $name of the lock to be released. + * @return boolean release result. + * @see http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock + */ + protected function releaseLock($name) + { + return (boolean)$this->db + ->createCommand('SELECT RELEASE_LOCK(:name)', array(':name' => $name)) + ->queryScalar(); + } +} diff --git a/extensions/mutex/yii/mutex/cache/Mutex.php b/extensions/mutex/yii/mutex/cache/Mutex.php deleted file mode 100644 index 73e277b..0000000 --- a/extensions/mutex/yii/mutex/cache/Mutex.php +++ /dev/null @@ -1,89 +0,0 @@ - - * @since 2.0 - */ -class Mutex extends \yii\mutex\Mutex -{ - /** - * @var Cache|string the cache object or the application component ID of the cache object. - * The messages data will be cached using this cache object. Note, this property has meaning only - * in case [[cachingDuration]] set to non-zero value. - * After the Mutex object is created, if you want to change this property, you should only assign - * it with a cache object. - */ - public $cache = 'cache'; - - - /** - * Initializes the DbMessageSource component. Configured [[cache]] component will be initialized. - * @throws InvalidConfigException if [[cache]] is invalid. - */ - public function init() - { - parent::init(); - if (is_string($this->cache)) { - $this->cache = Yii::$app->getComponent($this->cache); - } - if (!$this->cache instanceof Cache) { - throw new InvalidConfigException('Mutex::cache must be either a cache object or the application component ID of the cache object.'); - } - } - - /** - * This method should be extended by concrete mutex implementations. Acquires lock by given name. - * @param string $name of the lock to be acquired. - * @param integer $timeout to wait for lock to become released. - * @return boolean acquiring result. - */ - protected function acquire($name, $timeout = 0) - { - - } - - /** - * This method should be extended by concrete mutex implementations. Releases lock by given name. - * @param string $name of the lock to be released. - * @return boolean release result. - */ - protected function release($name) - { - return $this->cache->delete("mutex.{$name}"); - } - - /** - * This method may optionally be extended by concrete mutex implementations. Checks whether lock has been - * already acquired by given name. - * @param string $name of the lock to be released. - * @return null|boolean whether lock has been already acquired. Returns `null` in case this feature - * is not supported by concrete mutex implementation. - */ - protected function getIsAcquired($name) - { - - } - - /** - * This method should be extended by concrete mutex implementations. Returns whether current mutex - * implementation can be used in a distributed environment. - * @return boolean whether current mutex implementation can be used in a distributed environment. - */ - public function getIsDistributed() - { - return $this->cache instanceof DbCache || $this->cache instanceof MemCache; - } -} diff --git a/extensions/mutex/yii/mutex/db/Mutex.php b/extensions/mutex/yii/mutex/db/Mutex.php deleted file mode 100644 index 17dd68a..0000000 --- a/extensions/mutex/yii/mutex/db/Mutex.php +++ /dev/null @@ -1,51 +0,0 @@ - - * @since 2.0 - */ -abstract class Mutex extends \yii\mutex\Mutex -{ - /** - * @var Connection|string the DB connection object or the application component ID of the DB connection. - * After the Mutex object is created, if you want to change this property, you should only assign - * it with a DB connection object. - */ - public $db = 'db'; - - /** - * Initializes generic database table based mutex implementation. - * @throws InvalidConfigException if [[db]] is invalid. - */ - public function init() - { - parent::init(); - if (is_string($this->db)) { - $this->db = Yii::$app->getComponent($this->db); - } - if (!$this->db instanceof Connection) { - throw new InvalidConfigException('Mutex::db must be either a DB connection instance or the application component ID of a DB connection.'); - } - } - - /** - * This method should be extended by concrete mutex implementations. Returns whether current mutex - * implementation can be used in a distributed environment. - * @return boolean whether current mutex implementation can be used in a distributed environment. - */ - public function getIsDistributed() - { - return true; - } -} diff --git a/extensions/mutex/yii/mutex/db/mssql/Mutex.php b/extensions/mutex/yii/mutex/db/mssql/Mutex.php deleted file mode 100644 index 374100a..0000000 --- a/extensions/mutex/yii/mutex/db/mssql/Mutex.php +++ /dev/null @@ -1,56 +0,0 @@ - - * @since 2.0 - */ -class Mutex extends \yii\mutex\db\Mutex -{ - /** - * Initializes Microsoft SQL Server specific mutex component implementation. - * @throws InvalidConfigException if [[db]] is not Microsoft SQL Server connection. - */ - public function init() - { - parent::init(); - $driverName = $this->db->driverName; - if ($driverName !== 'sqlsrv' && $driverName !== 'dblib' && $driverName !== 'mssql') { - throw new InvalidConfigException(''); - } - } - - /** - * This method should be extended by concrete mutex implementations. Acquires lock by given name. - * @param string $name of the lock to be acquired. - * @param integer $timeout to wait for lock to become released. - * @return boolean acquiring result. - * @throws \BadMethodCallException - * @see http://msdn.microsoft.com/en-us/library/ms189823.aspx - */ - protected function acquire($name, $timeout = 0) - { - throw new \BadMethodCallException('Not implemented yet.'); - } - - /** - * This method should be extended by concrete mutex implementations. Releases lock by given name. - * @param string $name of the lock to be released. - * @return boolean release result. - * @throws \BadMethodCallException - * @see http://msdn.microsoft.com/en-us/library/ms178602.aspx - */ - protected function release($name) - { - throw new \BadMethodCallException('Not implemented yet.'); - } -} diff --git a/extensions/mutex/yii/mutex/db/mysql/Mutex.php b/extensions/mutex/yii/mutex/db/mysql/Mutex.php deleted file mode 100644 index 7153d38..0000000 --- a/extensions/mutex/yii/mutex/db/mysql/Mutex.php +++ /dev/null @@ -1,72 +0,0 @@ - - * @since 2.0 - */ -class Mutex extends \yii\mutex\db\Mutex -{ - /** - * Initializes MySQL specific mutex component implementation. - * @throws InvalidConfigException if [[db]] is not MySQL connection. - */ - public function init() - { - parent::init(); - if ($this->db->driverName !== 'mysql') { - throw new InvalidConfigException(''); - } - } - - /** - * This method should be extended by concrete mutex implementations. Acquires lock by given name. - * @param string $name of the lock to be acquired. - * @param integer $timeout to wait for lock to become released. - * @return boolean acquiring result. - * @see http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_get-lock - */ - protected function acquire($name, $timeout = 0) - { - return (boolean)$this->db - ->createCommand('SELECT GET_LOCK(:name, :timeout)', array(':name' => $name, ':timeout' => $timeout)) - ->queryScalar(); - } - - /** - * This method should be extended by concrete mutex implementations. Releases lock by given name. - * @param string $name of the lock to be released. - * @return boolean release result. - * @see http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_release-lock - */ - protected function release($name) - { - return (boolean)$this->db - ->createCommand('SELECT RELEASE_LOCK(:name)', array(':name' => $name)) - ->queryScalar(); - } - - /** - * This method may optionally be extended by concrete mutex implementations. Checks whether lock has been - * already acquired by given name. - * @param string $name of the lock to be released. - * @return null|boolean whether lock has been already acquired. Returns `null` in case this feature - * is not supported by concrete mutex implementation. - * @see http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_is-free-lock - */ - protected function getIsAcquired($name) - { - return (boolean)$this->db - ->createCommand('SELECT IS_FREE_LOCK(:name)', array(':name' => $name)) - ->queryScalar(); - } -} diff --git a/extensions/mutex/yii/mutex/unix/Mutex.php b/extensions/mutex/yii/mutex/unix/Mutex.php deleted file mode 100644 index ca18a49..0000000 --- a/extensions/mutex/yii/mutex/unix/Mutex.php +++ /dev/null @@ -1,99 +0,0 @@ - - * @since 2.0 - */ -class Mutex extends \yii\mutex\Mutex -{ - /** - * @var resource[] stores all opened lock files. Keys are lock names and values are file handles. - */ - private $_files = array(); - - - /** - * Initializes mutex component implementation dedicated for UNIX, GNU/Linux, Mac OS X, and other UNIX-like - * operating systems. - * @throws InvalidConfigException - */ - public function init() - { - if (stripos(php_uname('s'), 'win') === 0) { - throw new InvalidConfigException(''); - } - } - - /** - * This method should be extended by concrete mutex implementations. Acquires lock by given name. - * @param string $name of the lock to be acquired. - * @param integer $timeout to wait for lock to become released. - * @return boolean acquiring result. - */ - protected function acquire($name, $timeout = 0) - { - $file = fopen(Yii::$app->getRuntimePath() . '/mutex.' . md5($name) . '.lock', 'w+'); - if ($file === false) { - return false; - } - $waitTime = 0; - while (!flock($file, LOCK_EX | LOCK_NB)) { - $waitTime++; - if ($waitTime > $timeout) { - fclose($file); - return false; - } - sleep(1); - } - $this->_files[$name] = $file; - return true; - } - - /** - * This method should be extended by concrete mutex implementations. Releases lock by given name. - * @param string $name of the lock to be released. - * @return boolean release result. - */ - protected function release($name) - { - if (!isset($this->_files[$name]) || !flock($this->_files[$name], LOCK_UN)) { - return false; - } else { - fclose($this->_files[$name]); - unset($this->_files[$name]); - return true; - } - } - - /** - * This method may optionally be extended by concrete mutex implementations. Checks whether lock has been - * already acquired by given name. - * @param string $name of the lock to be released. - * @return null|boolean whether lock has been already acquired. Returns `null` in case this feature - * is not supported by concrete mutex implementation. - */ - protected function getIsAcquired($name) - { - return false; - } - - /** - * This method should be extended by concrete mutex implementations. Returns whether current mutex - * implementation can be used in a distributed environment. - * @return boolean whether current mutex implementation can be used in a distributed environment. - */ - public function getIsDistributed() - { - return false; - } -}