Issue #2044367 by InternetDevels, Berdir, wildflower_0002, ianthomas_uk, ParisLiakos, Xano | cosmicdreams: Replace calls to lock() with \Drupal::lock().

8.0.x
webchick 2014-02-26 11:30:20 -08:00
parent aa49131293
commit 08a9a7993d
10 changed files with 81 additions and 71 deletions

View File

@ -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)));
}
/**
* @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.
*
@ -2639,7 +2588,3 @@ function drupal_check_memory_limit($required, $memory_limit = NULL) {
function lock() {
return \Drupal::lock();
}
/**
* @} End of "defgroup lock".
*/

View File

@ -2140,11 +2140,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 (!\Drupal::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__);
\Drupal::lock()->wait(__FUNCTION__);
return FALSE;
}
@ -2164,7 +2164,7 @@ function menu_router_rebuild() {
watchdog_exception('menu', $e);
}
lock()->release(__FUNCTION__);
\Drupal::lock()->release(__FUNCTION__);
return TRUE;
}

View File

@ -245,6 +245,8 @@ class Drupal {
* Returns the locking layer instance.
*
* @return \Drupal\Core\Lock\LockBackendInterface
*
* @ingroup lock
*/
public static function lock() {
return static::$container->get('lock');

View File

@ -12,6 +12,8 @@ use Drupal\Core\Database\IntegrityConstraintViolationException;
/**
* Defines the database lock backend. This is the default backend in Drupal.
*
* @ingroup lock
*/
class DatabaseLockBackend extends LockBackendAbstract {

View File

@ -9,6 +9,8 @@ namespace Drupal\Core\Lock;
/**
* Non backend related common methods implementation for lock backends.
*
* @ingroup lock
*/
abstract class LockBackendAbstract implements LockBackendInterface {

View File

@ -7,8 +7,64 @@
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.
*
* @ingroup lock
*/
interface LockBackendInterface {

View File

@ -12,6 +12,8 @@ namespace Drupal\Core\Lock;
*
* This implementation won't actually lock anything and will always succeed on
* lock attempts.
*
* @ingroup lock
*/
class NullLockBackend implements LockBackendInterface {

View File

@ -202,13 +202,13 @@ abstract class CacheArray implements \ArrayAccess {
// 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 || \Drupal::lock()->acquire($lock_name)) {
if ($cached = \Drupal::cache($this->bin)->get($this->cid)) {
$data = $cached->data + $data;
}
\Drupal::cache($this->bin)->set($this->cid, $data, Cache::PERMANENT, $this->tags);
if ($lock) {
lock()->release($lock_name);
\Drupal::lock()->release($lock_name);
}
}
}

View File

@ -33,36 +33,37 @@ class LockFunctionalTest extends WebTestBase {
* Confirms that we can acquire and release locks in two parallel requests.
*/
public function testLockAcquire() {
$lock = $this->container->get('lock');
$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'), 'Lock acquired by this request.', 'Lock');
$this->assertTrue(lock()->acquire('system_test_lock_acquire'), 'Lock extended by this request.', 'Lock');
lock()->release('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 extended by this request.', 'Lock');
$lock->release('system_test_lock_acquire');
// Cause another request to acquire the lock.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_acquired, 'Lock acquired by the other request.', '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->drupalGet('system-test/lock-acquire');
$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.
$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);
// The other request should break our lock.
$this->drupalGet('system-test/lock-acquire');
$this->assertText($lock_acquired, 'Lock acquired by the other request, breaking our lock.', 'Lock');
// 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.
$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, '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');
}
}

View File

@ -77,8 +77,8 @@ function system_test_system_info_alter(&$info, $file, $type) {
* @deprecated \Drupal\system_test\Controller\SystemTestController::lockAcquire()
*/
function system_test_lock_acquire() {
if (lock()->acquire('system_test_lock_acquire')) {
lock()->release('system_test_lock_acquire');
if (\Drupal::lock()->acquire('system_test_lock_acquire')) {
\Drupal::lock()->release('system_test_lock_acquire');
return 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
}
else {
@ -92,7 +92,7 @@ function system_test_lock_acquire() {
* @deprecated \Drupal\system_test\Controller\SystemTestController::lockExit()
*/
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()';
// The shut-down function should release the lock.
exit();