class DatabaseLockBackend

Defines the database lock backend. This is the default backend in Drupal.

Hierarchy

Expanded class hierarchy of DatabaseLockBackend

2 files declare their use of DatabaseLockBackend
bootstrap.inc in drupal/core/includes/bootstrap.inc
Functions that need to be loaded on every Drupal request.
TempStoreDatabaseTest.php in drupal/core/modules/user/lib/Drupal/user/Tests/TempStoreDatabaseTest.php
Definition of Drupal\user\Tests\TempStoreDatabaseTest.

File

drupal/core/lib/Drupal/Core/Lock/DatabaseLockBackend.php, line 15
Definition of Drupal\Core\Lock\DatabaseLockBackend.

Namespace

Drupal\Core\Lock
View source
class DatabaseLockBackend extends LockBackendAbstract {

  /**
   * Implements Drupal\Core\Lock\LockBackedInterface::acquire().
   */
  public function acquire($name, $timeout = 30.0) {

    // Insure that the timeout is at least 1 ms.
    $timeout = max($timeout, 0.001);
    $expire = microtime(TRUE) + $timeout;
    if (isset($this->locks[$name])) {

      // Try to extend the expiration of a lock we already acquired.
      $success = (bool) db_update('semaphore')
        ->fields(array(
        'expire' => $expire,
      ))
        ->condition('name', $name)
        ->condition('value', $this
        ->getLockId())
        ->execute();
      if (!$success) {

        // The lock was broken.
        unset($this->locks[$name]);
      }
      return $success;
    }
    else {

      // Optimistically try to acquire the lock, then retry once if it fails.
      // The first time through the loop cannot be a retry.
      $retry = FALSE;

      // We always want to do this code at least once.
      do {
        try {
          db_insert('semaphore')
            ->fields(array(
            'name' => $name,
            'value' => $this
              ->getLockId(),
            'expire' => $expire,
          ))
            ->execute();

          // We track all acquired locks in the global variable.
          $this->locks[$name] = TRUE;

          // We never need to try again.
          $retry = FALSE;
        } catch (IntegrityConstraintViolationException $e) {

          // Suppress the error. If this is our first pass through the loop,
          // then $retry is FALSE. In this case, the insert failed because some
          // other request acquired the lock but did not release it. We decide
          // whether to retry by checking lockMayBeAvailable(). This will clear
          // the offending row from the database table in case it has expired.
          $retry = $retry ? FALSE : $this
            ->lockMayBeAvailable($name);
        }

        // We only retry in case the first attempt failed, but we then broke
        // an expired lock.
      } while ($retry);
    }
    return isset($this->locks[$name]);
  }

  /**
   * Implements Drupal\Core\Lock\LockBackedInterface::lockMayBeAvailable().
   */
  public function lockMayBeAvailable($name) {
    $lock = db_query('SELECT expire, value FROM {semaphore} WHERE name = :name', array(
      ':name' => $name,
    ))
      ->fetchAssoc();
    if (!$lock) {
      return TRUE;
    }
    $expire = (double) $lock['expire'];
    $now = microtime(TRUE);
    if ($now > $expire) {

      // We check two conditions to prevent a race condition where another
      // request acquired the lock and set a new expire time. We add a small
      // number to $expire to avoid errors with float to string conversion.
      return (bool) db_delete('semaphore')
        ->condition('name', $name)
        ->condition('value', $lock['value'])
        ->condition('expire', 0.0001 + $expire, '<=')
        ->execute();
    }
    return FALSE;
  }

  /**
   * Implements Drupal\Core\Lock\LockBackedInterface::release().
   */
  public function release($name) {
    unset($this->locks[$name]);
    db_delete('semaphore')
      ->condition('name', $name)
      ->condition('value', $this
      ->getLockId())
      ->execute();
  }

  /**
   * Implements Drupal\Core\Lock\LockBackedInterface::releaseAll().
   */
  public function releaseAll($lock_id = NULL) {
    $this->locks = array();
    if (empty($lock_id)) {
      $lock_id = $this
        ->getLockId();
    }
    db_delete('semaphore')
      ->condition('value', $lock_id)
      ->execute();
  }

}

Members

Namesort descending Modifiers Type Description Overrides
DatabaseLockBackend::acquire public function Implements Drupal\Core\Lock\LockBackedInterface::acquire(). Overrides LockBackendInterface::acquire
DatabaseLockBackend::lockMayBeAvailable public function Implements Drupal\Core\Lock\LockBackedInterface::lockMayBeAvailable(). Overrides LockBackendInterface::lockMayBeAvailable
DatabaseLockBackend::release public function Implements Drupal\Core\Lock\LockBackedInterface::release(). Overrides LockBackendInterface::release
DatabaseLockBackend::releaseAll public function Implements Drupal\Core\Lock\LockBackedInterface::releaseAll(). Overrides LockBackendInterface::releaseAll
LockBackendAbstract::$lockId protected property Current page lock token identifier.
LockBackendAbstract::$locks protected property Existing locks for this page.
LockBackendAbstract::getLockId public function Implements Drupal\Core\Lock\LockBackedInterface::getLockId(). Overrides LockBackendInterface::getLockId
LockBackendAbstract::wait public function Implements Drupal\Core\Lock\LockBackedInterface::wait(). Overrides LockBackendInterface::wait