Commit 768dfcd9 authored by catch's avatar catch

Issue #1774134 by larowlan, amateescu: Remove BC layer for lock API.

parent ac801b61
......@@ -10,6 +10,8 @@
use Symfony\Component\DependencyInjection\Exception\RuntimeException as DependencyInjectionRuntimeException;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Language\Language;
use Drupal\Core\Lock\DatabaseLockBackend;
use Drupal\Core\Lock\LockBackendInterface;
/**
* @file
......@@ -953,17 +955,17 @@ function variable_initialize($conf = array()) {
else {
// Cache miss. Avoid a stampede.
$name = 'variable_init';
if (!lock_acquire($name, 1)) {
if (!lock()->acquire($name, 1)) {
// Another request is building the variable cache.
// Wait, then re-run this function.
lock_wait($name);
lock()->wait($name);
return variable_initialize($conf);
}
else {
// Proceed with variable rebuild.
$variables = array_map('unserialize', db_query('SELECT name, value FROM {variable}')->fetchAllKeyed());
cache('bootstrap')->set('variables', $variables);
lock_release($name);
lock()->release($name);
}
}
......@@ -2405,9 +2407,6 @@ function _drupal_bootstrap_database() {
function _drupal_bootstrap_variables() {
global $conf;
// Initialize the lock system.
require_once DRUPAL_ROOT . '/core/includes/lock.inc';
// Load variables from the database, but do not overwrite variables set in settings.php.
$conf = variable_initialize(isset($conf) ? $conf : array());
// Load bootstrap modules.
......@@ -3486,3 +3485,87 @@ function drupal_php_storage($name = 'default') {
}
return $storage_controllers[$name];
}
/**
* @defgroup lock Locking mechanisms
* @{
* Functions to coordinate long-running operations across requests.
*
* In most environments, multiple Drupal page requests (a.k.a. threads or
* processes) will execute in parallel. This leads to potential conflicts or
* race conditions when two requests execute the same code at the same time. A
* common example of this is a rebuild like menu_router_rebuild() where we
* invoke many hook implementations to get and process data from all active
* modules, and then delete the current data in the database to insert the new
* afterwards.
*
* This is a cooperative, advisory lock system. Any long-running operation
* that could potentially be attempted in parallel by multiple requests should
* try to acquire a lock before proceeding. By obtaining a lock, one request
* notifies any other requests that a specific operation is in progress which
* must not be executed in parallel.
*
* To use this API, pick a unique name for the lock. A sensible choice is the
* name of the function performing the operation. A very simple example use of
* this API:
* @code
* function mymodule_long_operation() {
* if (lock()->acquire('mymodule_long_operation')) {
* // Do the long operation here.
* // ...
* lock()->release('mymodule_long_operation');
* }
* }
* @endcode
*
* If a function acquires a lock it should always release it when the
* operation is complete by calling lock()->release(), as in the example.
*
* A function that has acquired a lock may attempt to renew a lock (extend the
* duration of the lock) by calling lock()->acquire() again during the operation.
* Failure to renew a lock is indicative that another request has acquired
* the lock, and that the current operation may need to be aborted.
*
* If a function fails to acquire a lock it may either immediately return, or
* it may call lock()->wait() if the rest of the current page request requires
* that the operation in question be complete. After lock()->wait() returns,
* the function may again attempt to acquire the lock, or may simply allow the
* page request to proceed on the assumption that a parallel request completed
* the operation.
*
* lock()->acquire() and lock()->wait() will automatically break (delete) a lock
* whose duration has exceeded the timeout specified when it was acquired.
*/
/**
* Get locking layer instance.
*
* @return Drupal\Core\Lock\LockBackendInterface
*/
function lock() {
$lock_backend = &drupal_static(__FUNCTION__);
if (!isset($lock_backend)) {
$class_name = variable_get('lock_backend', 'Drupal\Core\Lock\DatabaseLockBackend');
// Do not allow a WSOD here, if the class does not exists use the default
// one.
// @todo We should log failed class loading for debugging, but for that we
// need an early watchdog function that logs into a file if the database
// is not present.
if (class_exists($class_name)) {
$lock_backend = new $class_name();
}
else {
$lock_backend = new DatabaseLockBackend();
}
drupal_register_shutdown_function(array($lock_backend, 'releaseAll'));
}
return $lock_backend;
}
/**
* @} End of "defgroup lock".
*/
......@@ -5018,7 +5018,7 @@ function drupal_cron_run() {
drupal_alter('queue_info', $queues);
// Try to acquire cron lock.
if (!lock_acquire('cron', 240.0)) {
if (!lock()->acquire('cron', 240.0)) {
// Cron is still running normally.
watchdog('cron', 'Attempting to re-run cron while it is already running.', array(), WATCHDOG_WARNING);
}
......@@ -5049,7 +5049,7 @@ function drupal_cron_run() {
watchdog('cron', 'Cron run completed.', array(), WATCHDOG_NOTICE);
// Release cron lock.
lock_release('cron');
lock()->release('cron');
// Return TRUE so other functions can check if it did run successfully
$return = TRUE;
......
......@@ -144,7 +144,7 @@ function config_import() {
return;
}
if (!lock_acquire(__FUNCTION__)) {
if (!lock()->acquire(__FUNCTION__)) {
// Another request is synchronizing configuration.
// Return a negative result for UI purposes. We do not differentiate between
// an actual synchronization error and a failed lock, because concurrent
......@@ -164,7 +164,7 @@ function config_import() {
watchdog_exception('config_import', $e);
$success = FALSE;
}
lock_release(__FUNCTION__);
lock()->release(__FUNCTION__);
return $success;
}
......
......@@ -345,7 +345,6 @@ function install_begin_request(&$install_state) {
// requests but, except for a WSOD, there is no chance for a a lock to stall
// (as opposed to the cache backend) so we can afford having a null
// implementation here.
require_once DRUPAL_ROOT . '/core/includes/lock.inc';
$conf['lock_backend'] = 'Drupal\Core\Lock\NullLockBackend';
// Prepare for themed output. We need to run this at the beginning of the
......
<?php
/**
* @file
* Drupal lock framework procedural proxy implementation.
*/
/**
* @defgroup lock Locking mechanisms
* @{
* Functions to coordinate long-running operations across requests.
*
* In most environments, multiple Drupal page requests (a.k.a. threads or
* processes) will execute in parallel. This leads to potential conflicts or
* race conditions when two requests execute the same code at the same time. A
* common example of this is a rebuild like menu_router_rebuild() where we
* invoke many hook implementations to get and process data from all active
* modules, and then delete the current data in the database to insert the new
* afterwards.
*
* This is a cooperative, advisory lock system. Any long-running operation
* that could potentially be attempted in parallel by multiple requests should
* try to acquire a lock before proceeding. By obtaining a lock, one request
* notifies any other requests that a specific operation is in progress which
* must not be executed in parallel.
*
* To use this API, pick a unique name for the lock. A sensible choice is the
* name of the function performing the operation. A very simple example use of
* this API:
* @code
* function mymodule_long_operation() {
* if (lock_acquire('mymodule_long_operation')) {
* // Do the long operation here.
* // ...
* lock_release('mymodule_long_operation');
* }
* }
* @endcode
*
* If a function acquires a lock it should always release it when the
* operation is complete by calling lock_release(), as in the example.
*
* A function that has acquired a lock may attempt to renew a lock (extend the
* duration of the lock) by calling lock_acquire() again during the operation.
* Failure to renew a lock is indicative that another request has acquired
* the lock, and that the current operation may need to be aborted.
*
* If a function fails to acquire a lock it may either immediately return, or
* it may call lock_wait() if the rest of the current page request requires
* that the operation in question be complete. After lock_wait() returns,
* the function may again attempt to acquire the lock, or may simply allow the
* page request to proceed on the assumption that a parallel request completed
* the operation.
*
* lock_acquire() and lock_wait() will automatically break (delete) a lock
* whose duration has exceeded the timeout specified when it was acquired.
*/
use Drupal\Core\Lock\DatabaseLockBackend;
use Drupal\Core\Lock\LockBackendInterface;
/**
* Get locking layer instance.
*
* @return Drupal\Core\Lock\LockBackendInterface
*/
function lock() {
$lock_backend = &drupal_static(__FUNCTION__);
if (!isset($lock_backend)) {
$class_name = variable_get('lock_backend', 'Drupal\Core\Lock\DatabaseLockBackend');
// Do not allow a WSOD here, if the class does not exists use the default
// one.
// @todo We should log failed class loading for debugging, but for that we
// need an early watchdog function that logs into a file if the database
// is not present.
if (class_exists($class_name)) {
$lock_backend = new $class_name();
}
else {
$lock_backend = new DatabaseLockBackend();
}
drupal_register_shutdown_function(array($lock_backend, 'releaseAll'));
}
return $lock_backend;
}
/**
* Acquire (or renew) a lock, but do not block if it fails.
*
* @param $name
* The name of the lock.
* @param $timeout
* A number of seconds (float) before the lock expires (minimum of 0.001).
*
* @return
* TRUE if the lock was acquired, FALSE if it failed.
*
* @deprecated
*/
function lock_acquire($name, $timeout = 30.0) {
return lock()->acquire($name, $timeout);
}
/**
* Wait for a lock to be available.
*
* This function may be called in a request that fails to acquire a desired
* lock. This will block further execution until the lock is available or the
* specified delay in seconds is reached. This should not be used with locks
* that are acquired very frequently, since the lock is likely to be acquired
* again by a different request while waiting.
*
* @param $name
* The name of the lock.
* @param $delay
* The maximum number of seconds to wait, as an integer.
*
* @return
* TRUE if the lock holds, FALSE if it is available.
*
* @deprecated
*/
function lock_wait($name, $delay = 30) {
return lock()->wait($name, $delay);
}
/**
* Release a lock previously acquired by lock_acquire().
*
* This will release the named lock if it is still held by the current request.
*
* @param $name
* The name of the lock.
*
* @deprecated
*/
function lock_release($name) {
lock()->release($name);
}
/**
* Release all previously acquired locks.
*
* @deprecated
*/
function lock_release_all($lock_id = NULL) {
lock()->releaseAll($lock_id);
}
/**
* @} End of "defgroup lock".
*/
......@@ -2648,11 +2648,11 @@ function menu_reset_static_cache() {
* in parallel and the current thread just waited for completion.
*/
function menu_router_rebuild() {
if (!lock_acquire(__FUNCTION__)) {
if (!lock()->acquire(__FUNCTION__)) {
// Wait for another request that is already doing this work.
// We choose to block here since otherwise the router item may not
// be available during routing resulting in a 404.
lock_wait(__FUNCTION__);
lock()->wait(__FUNCTION__);
return FALSE;
}
......@@ -2673,7 +2673,7 @@ function menu_router_rebuild() {
watchdog_exception('menu', $e);
}
lock_release(__FUNCTION__);
lock()->release(__FUNCTION__);
return TRUE;
}
......
......@@ -181,13 +181,13 @@ protected function set($data, $lock = TRUE) {
// Lock cache writes to help avoid stampedes.
// To implement locking for cache misses, override __construct().
$lock_name = $this->cid . ':' . $this->bin;
if (!$lock || lock_acquire($lock_name)) {
if (!$lock || lock()->acquire($lock_name)) {
if ($cached = cache($this->bin)->get($this->cid)) {
$data = $cached->data + $data;
}
cache($this->bin)->set($this->cid, $data);
if ($lock) {
lock_release($lock_name);
lock()->release($lock_name);
}
}
}
......
......@@ -99,7 +99,7 @@ public function resolveCacheMiss($offset) {
public function set($data, $lock = TRUE) {
$lock_name = $this->cid . ':' . $this->bin;
if (!$lock || lock_acquire($lock_name)) {
if (!$lock || lock()->acquire($lock_name)) {
if ($cached = cache($this->bin)->get($this->cid)) {
// Use array merge instead of union so that filled in values in $data
// overwrite empty values in the current cache.
......@@ -111,7 +111,7 @@ public function set($data, $lock = TRUE) {
}
cache($this->bin)->set($this->cid, $data);
if ($lock) {
lock_release($lock_name);
lock()->release($lock_name);
}
}
}
......
......@@ -128,7 +128,7 @@ protected function preSave(EntityInterface $comment) {
// has the lock, just move to the next integer.
do {
$thread = $prefix . comment_int_to_alphadecimal(++$n) . '/';
} while (!lock_acquire("comment:$comment->nid:$thread"));
} while (!lock()->acquire("comment:$comment->nid:$thread"));
$this->threadLock = $thread;
}
if (empty($comment->created)) {
......@@ -242,7 +242,7 @@ protected function updateNodeStatistics($nid) {
*/
protected function releaseThreadLock() {
if ($this->threadLock) {
lock_release($this->threadLock);
lock()->release($this->threadLock);
$this->threadLock = '';
}
}
......
......@@ -763,7 +763,7 @@ function image_style_deliver($style, $scheme) {
// generation is in progress in another thread.
$lock_name = 'image_style_deliver:' . $style['name'] . ':' . drupal_hash_base64($image_uri);
if (!file_exists($derivative_uri)) {
$lock_acquired = lock_acquire($lock_name);
$lock_acquired = lock()->acquire($lock_name);
if (!$lock_acquired) {
// Tell client to retry again in 3 seconds. Currently no browsers are known
// to support Retry-After.
......@@ -779,7 +779,7 @@ function image_style_deliver($style, $scheme) {
$success = file_exists($derivative_uri) || image_style_create_derivative($style, $image_uri, $derivative_uri);
if (!empty($lock_acquired)) {
lock_release($lock_name);
lock()->release($lock_name);
}
if ($success) {
......
......@@ -83,34 +83,34 @@ public function testBackendLockReleaseAll() {
public function testLockAcquire() {
$lock_acquired = 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
$lock_not_acquired = 'FALSE: Lock not acquired in system_test_lock_acquire()';
$this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
$this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock extended by this request.'), t('Lock'));
lock_release('system_test_lock_acquire');
$this->assertTrue(lock()->acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
$this->assertTrue(lock()->acquire('system_test_lock_acquire'), t('Lock extended by this request.'), t('Lock'));
lock()->release('system_test_lock_acquire');
// Cause another request to acquire the lock.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_acquired, t('Lock acquired by the other request.'), t('Lock'));
// The other request has finished, thus it should have released its lock.
$this->assertTrue(lock_acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
$this->assertTrue(lock()->acquire('system_test_lock_acquire'), t('Lock acquired by this request.'), t('Lock'));
// This request holds the lock, so the other request cannot acquire it.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_not_acquired, t('Lock not acquired by the other request.'), t('Lock'));
lock_release('system_test_lock_acquire');
lock()->release('system_test_lock_acquire');
// Try a very short timeout and lock breaking.
$this->assertTrue(lock_acquire('system_test_lock_acquire', 0.5), t('Lock acquired by this request.'), t('Lock'));
$this->assertTrue(lock()->acquire('system_test_lock_acquire', 0.5), t('Lock acquired by this request.'), t('Lock'));
sleep(1);
// The other request should break our lock.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_acquired, t('Lock acquired by the other request, breaking our lock.'), t('Lock'));
// We cannot renew it, since the other thread took it.
$this->assertFalse(lock_acquire('system_test_lock_acquire'), t('Lock cannot be extended by this request.'), t('Lock'));
$this->assertFalse(lock()->acquire('system_test_lock_acquire'), t('Lock cannot be extended by this request.'), t('Lock'));
// Check the shut-down function.
$lock_acquired_exit = 'TRUE: Lock successfully acquired in system_test_lock_exit()';
$lock_not_acquired_exit = 'FALSE: Lock not acquired in system_test_lock_exit()';
$this->drupalGet('system-test/lock-exit');
$this->assertText($lock_acquired_exit, t('Lock acquired by the other request before exit.'), t('Lock'));
$this->assertTrue(lock_acquire('system_test_lock_exit'), t('Lock acquired by this request after the other request exits.'), t('Lock'));
$this->assertTrue(lock()->acquire('system_test_lock_exit'), t('Lock acquired by this request after the other request exits.'), t('Lock'));
}
}
......@@ -281,8 +281,8 @@ function system_test_system_info_alter(&$info, $file, $type) {
* Try to acquire a named lock and report the outcome.
*/
function system_test_lock_acquire() {
if (lock_acquire('system_test_lock_acquire')) {
lock_release('system_test_lock_acquire');
if (lock()->acquire('system_test_lock_acquire')) {
lock()->release('system_test_lock_acquire');
return 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
}
else {
......@@ -294,7 +294,7 @@ function system_test_lock_acquire() {
* Try to acquire a specific lock, and then exit.
*/
function system_test_lock_exit() {
if (lock_acquire('system_test_lock_exit', 900)) {
if (lock()->acquire('system_test_lock_exit', 900)) {
echo 'TRUE: Lock successfully acquired in system_test_lock_exit()';
// The shut-down function should release the lock.
exit();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment