diff --git a/core/core.services.yml b/core/core.services.yml index 1d9b2d2..4134ad6 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -450,6 +450,9 @@ services: arguments: ['@database'] tags: - { name: backend_overridable } + lock.persistent: + class: Drupal\Core\Lock\PersistentDatabaseLockBackend + arguments: ['@database'] router.request_context: class: Drupal\Core\Routing\RequestContext tags: diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index 3efcd76..948330c 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -44,7 +44,7 @@ class ConfigImporter { /** * The name used to identify the lock. */ - const LOCK_ID = 'config_importer'; + const LOCK_NAME = 'config_importer'; /** * The storage comparer used to discover configuration changes. @@ -512,9 +512,9 @@ public function initialize() { // Ensure that the changes have been validated. $this->validate(); - if (!$this->lock->acquire(static::LOCK_ID)) { + if (!$this->lock->acquire(static::LOCK_NAME)) { // Another process is synchronizing configuration. - throw new ConfigImporterException(sprintf('%s is already importing', static::LOCK_ID)); + throw new ConfigImporterException(sprintf('%s is already importing', static::LOCK_NAME)); } $sync_steps = array(); @@ -611,7 +611,7 @@ protected function processConfigurations(array &$context) { protected function finish(array &$context) { $this->eventDispatcher->dispatch(ConfigEvents::IMPORT, new ConfigImporterEvent($this)); // The import is now complete. - $this->lock->release(static::LOCK_ID); + $this->lock->release(static::LOCK_NAME); $this->reset(); $context['message'] = t('Finalizing configuration synchronization.'); $context['finished'] = 1; @@ -996,7 +996,7 @@ protected function importInvokeRename($collection, $rename_name) { * TRUE if an import is already running, FALSE if not. */ public function alreadyImporting() { - return !$this->lock->lockMayBeAvailable(static::LOCK_ID); + return !$this->lock->lockMayBeAvailable(static::LOCK_NAME); } /** @@ -1007,13 +1007,13 @@ public function alreadyImporting() { * keep the services used by the importer in sync. */ protected function reInjectMe() { - $this->eventDispatcher = \Drupal::service('event_dispatcher'); - $this->configManager = \Drupal::service('config.manager'); - $this->lock = \Drupal::lock(); - $this->typedConfigManager = \Drupal::service('config.typed'); - $this->moduleHandler = \Drupal::moduleHandler(); - $this->themeHandler = \Drupal::service('theme_handler'); - $this->stringTranslation = \Drupal::service('string_translation'); + $this->_serviceIds = array(); + $vars = get_object_vars($this); + foreach ($vars as $key => $value) { + if (is_object($value) && isset($value->_serviceId)) { + $this->$key = \Drupal::service($value->_serviceId); + } + } } } diff --git a/core/lib/Drupal/Core/Lock/PersistentDatabaseLockBackend.php b/core/lib/Drupal/Core/Lock/PersistentDatabaseLockBackend.php new file mode 100644 index 0000000..f14f87a --- /dev/null +++ b/core/lib/Drupal/Core/Lock/PersistentDatabaseLockBackend.php @@ -0,0 +1,36 @@ +database = $database; + // Set the lockId to a fixed string to make the lock ID the same across + // multiple requests. The lock ID is use as a page token to relate all the + // locks set during a request to each other. + // @see \Drupal\Core\Lock\LockBackendInterface::getLockId() + $this->lockId = 'persistent'; + } +} diff --git a/core/modules/config/src/Form/ConfigSync.php b/core/modules/config/src/Form/ConfigSync.php index 21840a5..b99c290 100644 --- a/core/modules/config/src/Form/ConfigSync.php +++ b/core/modules/config/src/Form/ConfigSync.php @@ -134,7 +134,7 @@ public static function create(ContainerInterface $container) { $container->get('config.storage.staging'), $container->get('config.storage'), $container->get('config.storage.snapshot'), - $container->get('lock'), + $container->get('lock.persistent'), $container->get('event_dispatcher'), $container->get('config.manager'), $container->get('config.typed'), diff --git a/core/modules/config/src/Tests/ConfigImportRenameValidationTest.php b/core/modules/config/src/Tests/ConfigImportRenameValidationTest.php index 60aab7f..fc0d0ed 100644 --- a/core/modules/config/src/Tests/ConfigImportRenameValidationTest.php +++ b/core/modules/config/src/Tests/ConfigImportRenameValidationTest.php @@ -55,7 +55,7 @@ protected function setUp() { $storage_comparer->createChangelist(), $this->container->get('event_dispatcher'), $this->container->get('config.manager'), - $this->container->get('lock'), + $this->container->get('lock.persistent'), $this->container->get('config.typed'), $this->container->get('module_handler'), $this->container->get('theme_handler'), diff --git a/core/modules/config/src/Tests/ConfigImportUITest.php b/core/modules/config/src/Tests/ConfigImportUITest.php index d4c4c9d..2c93dd4 100644 --- a/core/modules/config/src/Tests/ConfigImportUITest.php +++ b/core/modules/config/src/Tests/ConfigImportUITest.php @@ -228,14 +228,14 @@ function testImportLock() { // Acquire a fake-lock on the import mechanism. $config_importer = $this->configImporter(); - $this->container->get('lock')->acquire($config_importer::LOCK_ID); + $this->container->get('lock.persistent')->acquire($config_importer::LOCK_NAME); // Attempt to import configuration and verify that an error message appears. $this->drupalPostForm(NULL, array(), t('Import all')); $this->assertText(t('Another request may be synchronizing configuration already.')); // Release the lock, just to keep testing sane. - $this->container->get('lock')->release($config_importer::LOCK_ID); + $this->container->get('lock.persistent')->release($config_importer::LOCK_NAME); // Verify site name has not changed. $this->assertNotEqual($new_site_name, \Drupal::config('system.site')->get('name')); diff --git a/core/modules/config/src/Tests/ConfigImporterTest.php b/core/modules/config/src/Tests/ConfigImporterTest.php index e5e760e..b98a772 100644 --- a/core/modules/config/src/Tests/ConfigImporterTest.php +++ b/core/modules/config/src/Tests/ConfigImporterTest.php @@ -527,4 +527,3 @@ function testUpdated() { $this->assertEqual(count($logs), 0); } } - diff --git a/core/modules/system/src/Tests/Lock/LockFunctionalTest.php b/core/modules/system/src/Tests/Lock/LockFunctionalTest.php index 6b1518c..846b44d 100644 --- a/core/modules/system/src/Tests/Lock/LockFunctionalTest.php +++ b/core/modules/system/src/Tests/Lock/LockFunctionalTest.php @@ -59,4 +59,29 @@ public function testLockAcquire() { $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'); } + + /** + * Tests that the persistent lock is persisted between requests. + */ + public function testPersistentLock() { + $persistent_lock = $this->container->get('lock.persistent'); + // Get a persistent lock. + $this->drupalGet('system-test/lock-persist/lock1'); + $this->assertText('TRUE: Lock successfully acquired in SystemTestController::lockPersist()'); + // Ensure that a shutdown function has not released the lock. + $this->assertFalse($persistent_lock->lockMayBeAvailable('lock1')); + $this->drupalGet('system-test/lock-persist/lock1'); + $this->assertText('FALSE: Lock not acquired in SystemTestController::lockPersist()'); + + // Get another persistent lock. + $this->drupalGet('system-test/lock-persist/lock2'); + $this->assertText('TRUE: Lock successfully acquired in SystemTestController::lockPersist()'); + $this->assertFalse($persistent_lock->lockMayBeAvailable('lock2')); + + // Release the first lock and try getting it again. + $persistent_lock->release('lock1'); + $this->drupalGet('system-test/lock-persist/lock1'); + $this->assertText('TRUE: Lock successfully acquired in SystemTestController::lockPersist()'); + } + } diff --git a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php index dd04e6c..c6b6903 100644 --- a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php +++ b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php @@ -10,6 +10,8 @@ use Drupal\Core\Controller\ControllerBase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Drupal\Core\Lock\LockBackendInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Controller routines for system_test routes. @@ -17,6 +19,30 @@ class SystemTestController extends ControllerBase { /** + * The persistent lock service. + * + * @var \Drupal\Core\Lock\LockBackendInterface + */ + protected $persistentLock; + + /** + * Constructs the SystemTestController. + * + * @param \Drupal\Core\Lock\LockBackendInterface $persistent_lock + * The persistent lock service. + */ + public function __construct(LockBackendInterface $persistent_lock) { + $this->persistentLock = $persistent_lock; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('lock.persistent')); + } + + /** * Tests main content fallback. * * @return string @@ -57,6 +83,24 @@ public function lockExit() { } /** + * Creates a lock that will persist across requests. + * + * @param string $lock_name + * The name of the persistent lock to acquire. + * + * @return string + * The text to display. + */ + public function lockPersist($lock_name) { + if ($this->persistentLock->acquire($lock_name)) { + return 'TRUE: Lock successfully acquired in SystemTestController::lockPersist()'; + } + else { + return 'FALSE: Lock not acquired in SystemTestController::lockPersist()'; + } + } + + /** * Set cache tag on on the returned render array. */ public function system_test_cache_tags_page() { diff --git a/core/modules/system/tests/modules/system_test/system_test.routing.yml b/core/modules/system/tests/modules/system_test/system_test.routing.yml index 32d235b..530a2ca 100644 --- a/core/modules/system/tests/modules/system_test/system_test.routing.yml +++ b/core/modules/system/tests/modules/system_test/system_test.routing.yml @@ -53,6 +53,14 @@ system_test.lock_exit: requirements: _access: 'TRUE' +system_test.lock_persist: + path: '/system-test/lock-persist/{lock_name}' + defaults: + _title: 'Persistent lock acquire' + _content: '\Drupal\system_test\Controller\SystemTestController::lockPersist' + requirements: + _access: 'TRUE' + system_test.cache_tags_page: path: '/system-test/cache_tags_page' defaults: