Issue #2044367 by InternetDevels, Berdir, wildflower_0002, ianthomas_uk, ParisLiakos, Xano | cosmicdreams: Replace calls to lock() with \Drupal::lock().
parent
aa49131293
commit
08a9a7993d
|
@ -2577,57 +2577,6 @@ function drupal_check_memory_limit($required, $memory_limit = NULL) {
|
||||||
return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required)));
|
return ((!$memory_limit) || ($memory_limit == -1) || (parse_size($memory_limit) >= parse_size($required)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @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.
|
* Get locking layer instance.
|
||||||
*
|
*
|
||||||
|
@ -2639,7 +2588,3 @@ function drupal_check_memory_limit($required, $memory_limit = NULL) {
|
||||||
function lock() {
|
function lock() {
|
||||||
return \Drupal::lock();
|
return \Drupal::lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @} End of "defgroup lock".
|
|
||||||
*/
|
|
||||||
|
|
|
@ -2140,11 +2140,11 @@ function menu_reset_static_cache() {
|
||||||
* in parallel and the current thread just waited for completion.
|
* in parallel and the current thread just waited for completion.
|
||||||
*/
|
*/
|
||||||
function menu_router_rebuild() {
|
function menu_router_rebuild() {
|
||||||
if (!lock()->acquire(__FUNCTION__)) {
|
if (!\Drupal::lock()->acquire(__FUNCTION__)) {
|
||||||
// Wait for another request that is already doing this work.
|
// Wait for another request that is already doing this work.
|
||||||
// We choose to block here since otherwise the router item may not
|
// We choose to block here since otherwise the router item may not
|
||||||
// be available during routing resulting in a 404.
|
// be available during routing resulting in a 404.
|
||||||
lock()->wait(__FUNCTION__);
|
\Drupal::lock()->wait(__FUNCTION__);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2164,7 +2164,7 @@ function menu_router_rebuild() {
|
||||||
watchdog_exception('menu', $e);
|
watchdog_exception('menu', $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
lock()->release(__FUNCTION__);
|
\Drupal::lock()->release(__FUNCTION__);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -245,6 +245,8 @@ class Drupal {
|
||||||
* Returns the locking layer instance.
|
* Returns the locking layer instance.
|
||||||
*
|
*
|
||||||
* @return \Drupal\Core\Lock\LockBackendInterface
|
* @return \Drupal\Core\Lock\LockBackendInterface
|
||||||
|
*
|
||||||
|
* @ingroup lock
|
||||||
*/
|
*/
|
||||||
public static function lock() {
|
public static function lock() {
|
||||||
return static::$container->get('lock');
|
return static::$container->get('lock');
|
||||||
|
|
|
@ -12,6 +12,8 @@ use Drupal\Core\Database\IntegrityConstraintViolationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the database lock backend. This is the default backend in Drupal.
|
* Defines the database lock backend. This is the default backend in Drupal.
|
||||||
|
*
|
||||||
|
* @ingroup lock
|
||||||
*/
|
*/
|
||||||
class DatabaseLockBackend extends LockBackendAbstract {
|
class DatabaseLockBackend extends LockBackendAbstract {
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ namespace Drupal\Core\Lock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Non backend related common methods implementation for lock backends.
|
* Non backend related common methods implementation for lock backends.
|
||||||
|
*
|
||||||
|
* @ingroup lock
|
||||||
*/
|
*/
|
||||||
abstract class LockBackendAbstract implements LockBackendInterface {
|
abstract class LockBackendAbstract implements LockBackendInterface {
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,64 @@
|
||||||
|
|
||||||
namespace Drupal\Core\Lock;
|
namespace Drupal\Core\Lock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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() {
|
||||||
|
* $lock = \Drupal::lock();
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @} End of "defgroup lock".
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lock backend interface.
|
* Lock backend interface.
|
||||||
|
*
|
||||||
|
* @ingroup lock
|
||||||
*/
|
*/
|
||||||
interface LockBackendInterface {
|
interface LockBackendInterface {
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ namespace Drupal\Core\Lock;
|
||||||
*
|
*
|
||||||
* This implementation won't actually lock anything and will always succeed on
|
* This implementation won't actually lock anything and will always succeed on
|
||||||
* lock attempts.
|
* lock attempts.
|
||||||
|
*
|
||||||
|
* @ingroup lock
|
||||||
*/
|
*/
|
||||||
class NullLockBackend implements LockBackendInterface {
|
class NullLockBackend implements LockBackendInterface {
|
||||||
|
|
||||||
|
|
|
@ -202,13 +202,13 @@ abstract class CacheArray implements \ArrayAccess {
|
||||||
// Lock cache writes to help avoid stampedes.
|
// Lock cache writes to help avoid stampedes.
|
||||||
// To implement locking for cache misses, override __construct().
|
// To implement locking for cache misses, override __construct().
|
||||||
$lock_name = $this->cid . ':' . $this->bin;
|
$lock_name = $this->cid . ':' . $this->bin;
|
||||||
if (!$lock || lock()->acquire($lock_name)) {
|
if (!$lock || \Drupal::lock()->acquire($lock_name)) {
|
||||||
if ($cached = \Drupal::cache($this->bin)->get($this->cid)) {
|
if ($cached = \Drupal::cache($this->bin)->get($this->cid)) {
|
||||||
$data = $cached->data + $data;
|
$data = $cached->data + $data;
|
||||||
}
|
}
|
||||||
\Drupal::cache($this->bin)->set($this->cid, $data, Cache::PERMANENT, $this->tags);
|
\Drupal::cache($this->bin)->set($this->cid, $data, Cache::PERMANENT, $this->tags);
|
||||||
if ($lock) {
|
if ($lock) {
|
||||||
lock()->release($lock_name);
|
\Drupal::lock()->release($lock_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,36 +33,37 @@ class LockFunctionalTest extends WebTestBase {
|
||||||
* Confirms that we can acquire and release locks in two parallel requests.
|
* Confirms that we can acquire and release locks in two parallel requests.
|
||||||
*/
|
*/
|
||||||
public function testLockAcquire() {
|
public function testLockAcquire() {
|
||||||
|
$lock = $this->container->get('lock');
|
||||||
$lock_acquired = 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
|
$lock_acquired = 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
|
||||||
$lock_not_acquired = 'FALSE: Lock not 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'), 'Lock acquired by this request.', 'Lock');
|
$this->assertTrue($lock->acquire('system_test_lock_acquire'), 'Lock acquired by this request.', 'Lock');
|
||||||
$this->assertTrue(lock()->acquire('system_test_lock_acquire'), 'Lock extended by this request.', 'Lock');
|
$this->assertTrue($lock->acquire('system_test_lock_acquire'), 'Lock extended by this request.', 'Lock');
|
||||||
lock()->release('system_test_lock_acquire');
|
$lock->release('system_test_lock_acquire');
|
||||||
|
|
||||||
// Cause another request to acquire the lock.
|
// Cause another request to acquire the lock.
|
||||||
$this->drupalGet('system-test/lock-acquire');
|
$this->drupalGet('system-test/lock-acquire');
|
||||||
$this->assertText($lock_acquired, 'Lock acquired by the other request.', 'Lock');
|
$this->assertText($lock_acquired, 'Lock acquired by the other request.', 'Lock');
|
||||||
// The other request has finished, thus it should have released its lock.
|
// The other request has finished, thus it should have released its lock.
|
||||||
$this->assertTrue(lock()->acquire('system_test_lock_acquire'), 'Lock acquired by this request.', 'Lock');
|
$this->assertTrue($lock->acquire('system_test_lock_acquire'), 'Lock acquired by this request.', 'Lock');
|
||||||
// This request holds the lock, so the other request cannot acquire it.
|
// This request holds the lock, so the other request cannot acquire it.
|
||||||
$this->drupalGet('system-test/lock-acquire');
|
$this->drupalGet('system-test/lock-acquire');
|
||||||
$this->assertText($lock_not_acquired, 'Lock not acquired by the other request.', 'Lock');
|
$this->assertText($lock_not_acquired, 'Lock not acquired by the other request.', 'Lock');
|
||||||
lock()->release('system_test_lock_acquire');
|
$lock->release('system_test_lock_acquire');
|
||||||
|
|
||||||
// Try a very short timeout and lock breaking.
|
// Try a very short timeout and lock breaking.
|
||||||
$this->assertTrue(lock()->acquire('system_test_lock_acquire', 0.5), 'Lock acquired by this request.', 'Lock');
|
$this->assertTrue($lock->acquire('system_test_lock_acquire', 0.5), 'Lock acquired by this request.', 'Lock');
|
||||||
sleep(1);
|
sleep(1);
|
||||||
// The other request should break our lock.
|
// The other request should break our lock.
|
||||||
$this->drupalGet('system-test/lock-acquire');
|
$this->drupalGet('system-test/lock-acquire');
|
||||||
$this->assertText($lock_acquired, 'Lock acquired by the other request, breaking our lock.', 'Lock');
|
$this->assertText($lock_acquired, 'Lock acquired by the other request, breaking our lock.', 'Lock');
|
||||||
// We cannot renew it, since the other thread took it.
|
// We cannot renew it, since the other thread took it.
|
||||||
$this->assertFalse(lock()->acquire('system_test_lock_acquire'), 'Lock cannot be extended by this request.', 'Lock');
|
$this->assertFalse($lock->acquire('system_test_lock_acquire'), 'Lock cannot be extended by this request.', 'Lock');
|
||||||
|
|
||||||
// Check the shut-down function.
|
// Check the shut-down function.
|
||||||
$lock_acquired_exit = 'TRUE: Lock successfully acquired in system_test_lock_exit()';
|
$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()';
|
$lock_not_acquired_exit = 'FALSE: Lock not acquired in system_test_lock_exit()';
|
||||||
$this->drupalGet('system-test/lock-exit');
|
$this->drupalGet('system-test/lock-exit');
|
||||||
$this->assertText($lock_acquired_exit, 'Lock acquired by the other request before exit.', 'Lock');
|
$this->assertText($lock_acquired_exit, 'Lock acquired by the other request before exit.', 'Lock');
|
||||||
$this->assertTrue(lock()->acquire('system_test_lock_exit'), 'Lock acquired by this request after the other request exits.', 'Lock');
|
$this->assertTrue($lock->acquire('system_test_lock_exit'), 'Lock acquired by this request after the other request exits.', 'Lock');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,8 +77,8 @@ function system_test_system_info_alter(&$info, $file, $type) {
|
||||||
* @deprecated \Drupal\system_test\Controller\SystemTestController::lockAcquire()
|
* @deprecated \Drupal\system_test\Controller\SystemTestController::lockAcquire()
|
||||||
*/
|
*/
|
||||||
function system_test_lock_acquire() {
|
function system_test_lock_acquire() {
|
||||||
if (lock()->acquire('system_test_lock_acquire')) {
|
if (\Drupal::lock()->acquire('system_test_lock_acquire')) {
|
||||||
lock()->release('system_test_lock_acquire');
|
\Drupal::lock()->release('system_test_lock_acquire');
|
||||||
return 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
|
return 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -92,7 +92,7 @@ function system_test_lock_acquire() {
|
||||||
* @deprecated \Drupal\system_test\Controller\SystemTestController::lockExit()
|
* @deprecated \Drupal\system_test\Controller\SystemTestController::lockExit()
|
||||||
*/
|
*/
|
||||||
function system_test_lock_exit() {
|
function system_test_lock_exit() {
|
||||||
if (lock()->acquire('system_test_lock_exit', 900)) {
|
if (\Drupal::lock()->acquire('system_test_lock_exit', 900)) {
|
||||||
echo 'TRUE: Lock successfully acquired in system_test_lock_exit()';
|
echo 'TRUE: Lock successfully acquired in system_test_lock_exit()';
|
||||||
// The shut-down function should release the lock.
|
// The shut-down function should release the lock.
|
||||||
exit();
|
exit();
|
||||||
|
|
Loading…
Reference in New Issue