Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 68 |
| DatabaseLockBackend | |
0.00% |
0 / 1 |
|
0.00% |
0 / 5 |
210 | |
0.00% |
0 / 68 |
| __construct | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
| acquire | |
0.00% |
0 / 1 |
42 | |
0.00% |
0 / 34 |
|||
| lockMayBeAvailable | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 15 |
|||
| release | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 6 |
|||
| releaseAll | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 10 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\Core\Lock\DatabaseLockBackend. | |
| */ | |
| namespace Drupal\Core\Lock; | |
| use Drupal\Core\Database\Connection; | |
| use Drupal\Core\Database\IntegrityConstraintViolationException; | |
| /** | |
| * Defines the database lock backend. This is the default backend in Drupal. | |
| * | |
| * @ingroup lock | |
| */ | |
| class DatabaseLockBackend extends LockBackendAbstract { | |
| /** | |
| * The database connection. | |
| * | |
| * @var \Drupal\Core\Database\Connection | |
| */ | |
| protected $database; | |
| /** | |
| * Constructs a new DatabaseLockBackend. | |
| * | |
| * @param \Drupal\Core\Database\Connection $database | |
| * The database connection. | |
| */ | |
| public function __construct(Connection $database) { | |
| // __destruct() is causing problems with garbage collections, register a | |
| // shutdown function instead. | |
| drupal_register_shutdown_function(array($this, 'releaseAll')); | |
| $this->database = $database; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| 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) $this->database->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 { | |
| $this->database->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]); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function lockMayBeAvailable($name) { | |
| $lock = $this->database->query('SELECT expire, value FROM {semaphore} WHERE name = :name', array(':name' => $name))->fetchAssoc(); | |
| if (!$lock) { | |
| return TRUE; | |
| } | |
| $expire = (float) $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) $this->database->delete('semaphore') | |
| ->condition('name', $name) | |
| ->condition('value', $lock['value']) | |
| ->condition('expire', 0.0001 + $expire, '<=') | |
| ->execute(); | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function release($name) { | |
| unset($this->locks[$name]); | |
| $this->database->delete('semaphore') | |
| ->condition('name', $name) | |
| ->condition('value', $this->getLockId()) | |
| ->execute(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function releaseAll($lock_id = NULL) { | |
| // Only attempt to release locks if any were acquired. | |
| if (!empty($this->locks)) { | |
| $this->locks = array(); | |
| if (empty($lock_id)) { | |
| $lock_id = $this->getLockId(); | |
| } | |
| $this->database->delete('semaphore') | |
| ->condition('value', $lock_id) | |
| ->execute(); | |
| } | |
| } | |
| } |