* $Id$
*
*/
-class CRM_Core_Lock {
+class CRM_Core_Lock implements \Civi\Core\Lock\LockInterface {
static $jobLog = FALSE;
protected $_name;
+ /**
+ * Use MySQL's GET_LOCK(). Locks are shared across all Civi instances
+ * on the same MySQL server.
+ *
+ * @param string $name
+ * Symbolic name for the lock. Names generally look like
+ * "worker.mailing.EmailProcessor" ("{category}.{component}.{AdhocName}").
+ *
+ * Categories: worker|data|cache|...
+ * Component: core|mailing|member|contribute|...
+ * @return \Civi\Core\Lock\LockInterface
+ */
+ public static function createGlobalLock($name) {
+ return new static($name, NULL, TRUE);
+ }
+
+ /**
+ * Use MySQL's GET_LOCK(), but apply prefixes to the lock names.
+ * Locks are unique to each instance of Civi.
+ *
+ * @param string $name
+ * Symbolic name for the lock. Names generally look like
+ * "worker.mailing.EmailProcessor" ("{category}.{component}.{AdhocName}").
+ *
+ * Categories: worker|data|cache|...
+ * Component: core|mailing|member|contribute|...
+ * @return \Civi\Core\Lock\LockInterface
+ */
+ public static function createScopedLock($name) {
+ return new static($name);
+ }
+
+ /**
+ * Use MySQL's GET_LOCK(), but conditionally apply prefixes to the lock names
+ * (if civimail_server_wide_lock is disabled).
+ *
+ * @param string $name
+ * Symbolic name for the lock. Names generally look like
+ * "worker.mailing.EmailProcessor" ("{category}.{component}.{AdhocName}").
+ *
+ * Categories: worker|data|cache|...
+ * Component: core|mailing|member|contribute|...
+ * @return \Civi\Core\Lock\LockInterface
+ * @deprecated
+ */
+ public static function createCivimailLock($name) {
+ $serverWideLock = \CRM_Core_BAO_Setting::getItem(
+ \CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
+ 'civimail_server_wide_lock'
+ );
+ return new static($name, NULL, $serverWideLock);
+ }
+
/**
* Initialize the constants used during lock acquire / release
*
* @param string $name
- * Name of the lock. Please prefix with component / functionality.
- * e.g. civimail.cronjob.JOB_ID
+ * Symbolic name for the lock. Names generally look like
+ * "worker.mailing.EmailProcessor" ("{category}.{component}.{AdhocName}").
+ *
+ * Categories: worker|data|cache|...
+ * Component: core|mailing|member|contribute|...
* @param int $timeout
* The number of seconds to wait to get the lock. 1 if not set.
* @param bool $serverWideLock
* Should this lock be applicable across your entire mysql server.
- * this is useful if you have multiple sites running on the same
- * mysql server and you want to limit the number of parallel cron
- * jobs - CRM-91XX
- *
- * @return \CRM_Core_Lock the lock object
+ * this is useful if you have multiple sites running on the same
+ * mysql server and you want to limit the number of parallel cron
+ * jobs - CRM-91XX
*/
public function __construct($name, $timeout = NULL, $serverWideLock = FALSE) {
$config = CRM_Core_Config::singleton();
self::$jobLog = $this->_name;
}
$this->_timeout = $timeout !== NULL ? $timeout : self::TIMEOUT;
-
- $this->acquire();
}
public function __destruct() {
/**
* @return bool
*/
- public function acquire() {
+ public function acquire($timeout = NULL) {
if (!$this->_hasLock) {
$query = "SELECT GET_LOCK( %1, %2 )";
$params = array(
1 => array($this->_name, 'String'),
- 2 => array($this->_timeout, 'Integer'),
+ 2 => array($timeout ? $timeout : $this->_timeout, 'Integer'),
);
$res = CRM_Core_DAO::singleValueQuery($query, $params);
if ($res) {
* @return null|string
*/
public function release() {
- if (defined('CIVICRM_LOCK_DEBUG')) {
- CRM_Core_Error::debug_log_message('release lock for ' . $this->_name . ' (' . ($this->_hasLock ? 'hasLock' : '!hasLock') . ')');
- }
if ($this->_hasLock) {
+ if (defined('CIVICRM_LOCK_DEBUG')) {
+ CRM_Core_Error::debug_log_message('release lock for ' . $this->_name);
+ }
$this->_hasLock = FALSE;
if (self::$jobLog == $this->_name) {
<?php
namespace Civi\Core;
+use Civi\Core\Lock\LockManager;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\FileCacheReader;
// }
// }
+ $container->setDefinition('lockManager', new Definition(
+ '\Civi\Core\Lock\LockManager',
+ array()
+ ))
+ ->setFactoryService(self::SELF)->setFactoryMethod('createLockManager');
+
$container->setDefinition('angular', new Definition(
'\Civi\Angular\Manager',
array()
return $dispatcher;
}
+ /**
+ * @return LockManager
+ */
+ public function createLockManager() {
+ // Ideally, downstream implementers could override any definitions in
+ // the container. For now, we'll make-do with some define()s.
+ $lm = new LockManager();
+ $lm
+ ->register('/^cache\./', defined('CIVICRM_CACHE_LOCK') ? CIVICRM_CACHE_LOCK : array('CRM_Core_Lock', 'createScopedLock'))
+ ->register('/^data\./', defined('CIVICRM_DATA_LOCK') ? CIVICRM_DATA_LOCK : array('CRM_Core_Lock', 'createScopedLock'))
+ ->register('/^worker\.mailing\.send\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : array('CRM_Core_Lock', 'createCivimailLock'))
+ ->register('/^worker\./', defined('CIVICRM_WORK_LOCK') ? CIVICRM_WORK_LOCK : array('CRM_Core_Lock', 'createScopedLock'));
+
+ // Registrations may use complex resolver expressions, but (as a micro-optimization)
+ // the default factory is specified as an array.
+
+ return $lm;
+ }
+
/**
* @param \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
* @param $magicFunctionProvider
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+namespace Civi\Core\Lock;
+
+interface LockInterface {
+
+ /**
+ * @param int|NULL $timeout
+ * The number of seconds to wait to get the lock.
+ * For a default value, use NULL.
+ * @return bool
+ */
+ public function acquire($timeout = NULL);
+
+ /**
+ * @return bool|null|string
+ * Trueish/falsish.
+ */
+ public function release();
+
+ /**
+ * @return bool|null|string
+ * Trueish/falsish.
+ * @deprecated
+ * Not supported by some locking strategies. If you need to poll, better
+ * to use acquire(0).
+ */
+ public function isFree();
+
+ /**
+ * @return bool
+ */
+ public function isAcquired();
+}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+namespace Civi\Core\Lock;
+
+use Civi\Core\Resolver;
+
+/**
+ * Class LockManager
+ * @package Civi\Core\Lock
+ *
+ * The lock-manager allows one to define the lock policy -- i.e. given a
+ * specific lock, how does one acquire the lock?
+ */
+class LockManager {
+
+ private $rules = array();
+
+ /**
+ * @param string $name
+ * Symbolic name for the lock. Names generally look like
+ * "worker.mailing.EmailProcessor" ("{category}.{component}.{AdhocName}").
+ *
+ * Categories: worker|data|cache|...
+ * Component: core|mailing|member|contribute|...
+ * @return LockInterface
+ * @throws \CRM_Core_Exception
+ */
+ public function create($name) {
+ $factory = $this->getFactory($name);
+ if ($factory) {
+ /** @var LockInterface $lock */
+ $lock = call_user_func_array($factory, array($name));
+ return $lock;
+ }
+ else {
+ throw new \CRM_Core_Exception("Lock \"$name\" does not match any rules. Use register() to add more rules.");
+ }
+ }
+
+ /**
+ * Create and attempt to acquire a lock.
+ *
+ * Note: Be sure to check $lock->isAcquired() to determine whether
+ * acquisition was successful.
+ *
+ * @param string $name
+ * Symbolic name for the lock. Names generally look like
+ * "worker.mailing.EmailProcessor" ("{category}.{component}.{AdhocName}").
+ *
+ * Categories: worker|data|cache|...
+ * Component: core|mailing|member|contribute|...
+ * @param int|NULL $timeout
+ * The number of seconds to wait to get the lock.
+ * For a default value, use NULL.
+ * @return LockInterface
+ * @throws \CRM_Core_Exception
+ */
+ public function acquire($name, $timeout = NULL) {
+ $lock = $this->create($name);
+ $lock->acquire($timeout);
+ return $lock;
+ }
+
+ /**
+ * @param string $name
+ * Symbolic name for the lock.
+ * @return callable|NULL
+ */
+ public function getFactory($name) {
+ foreach ($this->rules as $rule) {
+ if (preg_match($rule['pattern'], $name)) {
+ return Resolver::singleton()->get($rule['factory']);
+ }
+ }
+ return NULL;
+ }
+
+ /**
+ * Register the lock-factory to use for specific lock-names.
+ *
+ * @param string $pattern
+ * A regex to match against the lock name.
+ * @param string|array $factory
+ * A callback. The callback should accept a $name parameter.
+ * Callbacks will be located using the resolver.
+ * @return $this
+ * @see Resolver
+ */
+ public function register($pattern, $factory) {
+ $this->rules[] = array(
+ 'pattern' => $pattern,
+ 'factory' => $factory,
+ );
+ return $this;
+ }
+
+}
--- /dev/null
+<?php
+/*
+ +--------------------------------------------------------------------+
+ | CiviCRM version 4.6 |
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC (c) 2004-2015 |
+ +--------------------------------------------------------------------+
+ | This file is a part of CiviCRM. |
+ | |
+ | CiviCRM is free software; you can copy, modify, and distribute it |
+ | under the terms of the GNU Affero General Public License |
+ | Version 3, 19 November 2007 and the CiviCRM Licensing Exception. |
+ | |
+ | CiviCRM is distributed in the hope that it will be useful, but |
+ | WITHOUT ANY WARRANTY; without even the implied warranty of |
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
+ | See the GNU Affero General Public License for more details. |
+ | |
+ | You should have received a copy of the GNU Affero General Public |
+ | License and the CiviCRM Licensing Exception along |
+ | with this program; if not, contact CiviCRM LLC |
+ | at info[AT]civicrm[DOT]org. If you have questions about the |
+ | GNU Affero General Public License or the licensing of CiviCRM, |
+ | see the CiviCRM license FAQ at http://civicrm.org/licensing |
+ +--------------------------------------------------------------------+
+ */
+namespace Civi\Core\Lock;
+
+/**
+ *
+ * @package CRM
+ * @copyright CiviCRM LLC (c) 2004-2015
+ * $Id$
+ *
+ */
+class NullLock implements LockInterface {
+
+ private $hasLock = FALSE;
+
+ /**
+ * @param string $name
+ * @return static
+ */
+ public static function create($name) {
+ return new static();
+ }
+
+ /**
+ * @param int|NULL $timeout
+ * The number of seconds to wait to get the lock.
+ * For a default value, use NULL.
+ * @return bool
+ */
+ public function acquire($timeout = NULL) {
+ $this->hasLock = TRUE;
+ return TRUE;
+ }
+
+ /**
+ * @return bool|null|string
+ * Trueish/falsish.
+ */
+ public function release() {
+ $this->hasLock = FALSE;
+ return TRUE;
+ }
+
+ /**
+ * @return bool|null|string
+ * Trueish/falsish.
+ * @deprecated
+ * Not supported by some locking strategies. If you need to poll, better
+ * to use acquire(0).
+ */
+ public function isFree() {
+ return !$this->hasLock;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isAcquired() {
+ return $this->hasLock;
+ }
+
+}