*/ final class Lock implements SharedLockInterface, LoggerAwareInterface { use LoggerAwareTrait; private PersistingStoreInterface $store; private Key $key; private ?float $ttl; private bool $autoRelease; private bool $dirty = false; /** * @param float|null $ttl Maximum expected lock duration in seconds * @param bool $autoRelease Whether to automatically release the lock or not when the lock instance is destroyed */ public function __construct(Key $key, PersistingStoreInterface $store, float $ttl = null, bool $autoRelease = true) { $this->store = $store; $this->key = $key; $this->ttl = $ttl; $this->autoRelease = $autoRelease; $this->logger = new NullLogger(); } public function __sleep(): array { throw new \BadMethodCallException('Cannot serialize '.__CLASS__); } public function __wakeup() { throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); } /** * Automatically releases the underlying lock when the object is destructed. */ public function __destruct() { if (!$this->autoRelease || !$this->dirty || !$this->isAcquired()) { return; } $this->release(); } /** * {@inheritdoc} */ public function acquire(bool $blocking = false): bool { $this->key->resetLifetime(); try { if ($blocking) { if (!$this->store instanceof BlockingStoreInterface) { while (true) { try { $this->store->save($this->key); break; } catch (LockConflictedException) { usleep((100 + random_int(-10, 10)) * 1000); } } } else { $this->store->waitAndSave($this->key); } } else { $this->store->save($this->key); } $this->dirty = true; $this->logger->debug('Successfully acquired the "{resource}" lock.', ['resource' => $this->key]); if ($this->ttl) { $this->refresh(); } if ($this->key->isExpired()) { try { $this->release(); } catch (\Exception) { // swallow exception to not hide the original issue } throw new LockExpiredException(sprintf('Failed to store the "%s" lock.', $this->key)); } return true; } catch (LockConflictedException $e) { $this->dirty = false; $this->logger->info('Failed to acquire the "{resource}" lock. Someone else already acquired the lock.', ['resource' => $this->key]); if ($blocking) { throw $e; } return false; } catch (\Exception $e) { $this->logger->notice('Failed to acquire the "{resource}" lock.', ['resource' => $this->key, 'exception' => $e]); throw new LockAcquiringException(sprintf('Failed to acquire the "%s" lock.', $this->key), 0, $e); } } /** * {@inheritdoc} */ public function acquireRead(bool $blocking = false): bool { $this->key->resetLifetime(); try { if (!$this->store instanceof SharedLockStoreInterface) { $this->logger->debug('Store does not support ReadLocks, fallback to WriteLock.', ['resource' => $this->key]); return $this->acquire($blocking); } if ($blocking) { if (!$this->store instanceof BlockingSharedLockStoreInterface) { while (true) { try { $this->store->saveRead($this->key); break; } catch (LockConflictedException) { usleep((100 + random_int(-10, 10)) * 1000); } } } else { $this->store->waitAndSaveRead($this->key); } } else { $this->store->saveRead($this->key); } $this->dirty = true; $this->logger->debug('Successfully acquired the "{resource}" lock for reading.', ['resource' => $this->key]); if ($this->ttl) { $this->refresh(); } if ($this->key->isExpired()) { try { $this->release(); } catch (\Exception) { // swallow exception to not hide the original issue } throw new LockExpiredException(sprintf('Failed to store the "%s" lock.', $this->key)); } return true; } catch (LockConflictedException $e) { $this->dirty = false; $this->logger->info('Failed to acquire the "{resource}" lock. Someone else already acquired the lock.', ['resource' => $this->key]); if ($blocking) { throw $e; } return false; } catch (\Exception $e) { $this->logger->notice('Failed to acquire the "{resource}" lock.', ['resource' => $this->key, 'exception' => $e]); throw new LockAcquiringException(sprintf('Failed to acquire the "%s" lock.', $this->key), 0, $e); } } /** * {@inheritdoc} */ public function refresh(float $ttl = null) { if (null === $ttl) { $ttl = $this->ttl; } if (!$ttl) { throw new InvalidArgumentException('You have to define an expiration duration.'); } try { $this->key->resetLifetime(); $this->store->putOffExpiration($this->key, $ttl); $this->dirty = true; if ($this->key->isExpired()) { try { $this->release(); } catch (\Exception) { // swallow exception to not hide the original issue } throw new LockExpiredException(sprintf('Failed to put off the expiration of the "%s" lock within the specified time.', $this->key)); } $this->logger->debug('Expiration defined for "{resource}" lock for "{ttl}" seconds.', ['resource' => $this->key, 'ttl' => $ttl]); } catch (LockConflictedException $e) { $this->dirty = false; $this->logger->notice('Failed to define an expiration for the "{resource}" lock, someone else acquired the lock.', ['resource' => $this->key]); throw $e; } catch (\Exception $e) { $this->logger->notice('Failed to define an expiration for the "{resource}" lock.', ['resource' => $this->key, 'exception' => $e]); throw new LockAcquiringException(sprintf('Failed to define an expiration for the "%s" lock.', $this->key), 0, $e); } } /** * {@inheritdoc} */ public function isAcquired(): bool { return $this->dirty = $this->store->exists($this->key); } /** * {@inheritdoc} */ public function release() { try { try { $this->store->delete($this->key); $this->dirty = false; } catch (LockReleasingException $e) { throw $e; } catch (\Exception $e) { throw new LockReleasingException(sprintf('Failed to release the "%s" lock.', $this->key), 0, $e); } if ($this->store->exists($this->key)) { throw new LockReleasingException(sprintf('Failed to release the "%s" lock, the resource is still locked.', $this->key)); } } catch (LockReleasingException $e) { $this->logger->notice('Failed to release the "{resource}" lock.', ['resource' => $this->key]); throw $e; } } /** * {@inheritdoc} */ public function isExpired(): bool { return $this->key->isExpired(); } /** * {@inheritdoc} */ public function getRemainingLifetime(): ?float { return $this->key->getRemainingLifetime(); } } __halt_compiler();----SIGNATURE:----sWJDBoyaf5LN1+tAijQrt+SwoiKwBPVHdqAN90BlDATdghMgDlWc8b7Ij953MUt90LF5jbqHyDE2+HIUWu/cNLi/k3wCURbP0BBGdOz2uvODdGFi0Vm3mmlv0kGZYHf7ikYPwnwhBgAGU5UvZK75tmuaHY71XobZd482Nh6fnmEUXQ/niR33r1UQD7lLUWyz2YZxoALwt2bYBpwllRWJXXgJh2CTN74dLu9Lsos0CoApnjZc7RYRCQ2t1JpjX80T0gL7XRXYQI1Oji1DtyQGyJRV6VPm9gPNzprhs+rVaJAwARP0zm0Pq4n6rrXfImeqrlLSftQiGTKB9DqiWp0egPrdaT5/QH2hi5uvmyfpBi7wzdFv7v9NjLVC5csHqXF2XddZt0j+BK3F6hbDhvI2x61ihRlhNp1WEYZFFHq6kBWJsmJGMni2tMXaY+5KkJBDimapCt+4OhPtQIQFWVDKq6akGOmCiRmuDflEMYFLUKNcLZ067OhS0O8yED/iaBu3cNtTdpFuWVZcE0IPJtBLn4Boq42etb3J7WDztA/utEbPED/Q8V5deDuvfCJ5zgnMrSoP8Nw+SI5rw14xQdRUG3p84Cd+sSlq1nMVhvePwfXX+eUw2JcCM5LEsQdnbX/9kBkoVcx4u+OyX0pskPjE3p5zUkj6QmYYnbcHOU9xL34=----ATTACHMENT:----MzUzNDgwMTMxMjUzMjM1NSAzNjkwMTQ5MTA5ODg1MjczIDM5MzE0OTY1Nzc4ODcwMzY=