Commit 08a9a799 authored by webchick's avatar webchick

Issue #2044367 by InternetDevels, Berdir, wildflower_0002, ianthomas_uk,...

Issue #2044367 by InternetDevels, Berdir, wildflower_0002, ianthomas_uk, ParisLiakos, Xano | cosmicdreams: Replace calls to lock() with \Drupal::lock().
parent aa491312
...@@ -2577,57 +2577,6 @@ function drupal_check_memory_limit($required, $memory_limit = NULL) { ...@@ -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) { ...@@ -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() { ...@@ -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() { ...@@ -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 @@ public static function keyValueExpirable($collection) { ...@@ -245,6 +245,8 @@ public static function keyValueExpirable($collection) {
* 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 @@ ...@@ -12,6 +12,8 @@
/** /**
* 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 @@ ...@@ -9,6 +9,8 @@
/** /**
* 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 @@ ...@@ -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 @@ ...@@ -12,6 +12,8 @@
* *
* 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 @@ protected function set($data, $lock = TRUE) { ...@@ -202,13 +202,13 @@ protected function set($data, $lock = TRUE) {
// 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 @@ public static function getInfo() { ...@@ -33,36 +33,37 @@ public static function getInfo() {
* 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) { ...@@ -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() { ...@@ -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();
......
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