From 2dbee69914379572197803589b7f6715090fbd60 Mon Sep 17 00:00:00 2001 From: bstanescu Date: Sun, 6 Oct 2019 23:21:00 +0300 Subject: [PATCH 1/3] Add locked timeout --- classes/exception/LockedTimeoutException.php | 10 ++++++++++ classes/mutex/PredisMutex.php | 4 ++-- classes/mutex/RedisMutex.php | 4 ++-- classes/mutex/SpinlockMutex.php | 18 +++++++++++++++--- 4 files changed, 29 insertions(+), 7 deletions(-) create mode 100644 classes/exception/LockedTimeoutException.php diff --git a/classes/exception/LockedTimeoutException.php b/classes/exception/LockedTimeoutException.php new file mode 100644 index 00000000..8b2d8dc7 --- /dev/null +++ b/classes/exception/LockedTimeoutException.php @@ -0,0 +1,10 @@ +redisAPIs = $redisAPIs; $this->logger = new NullLogger(); diff --git a/classes/mutex/SpinlockMutex.php b/classes/mutex/SpinlockMutex.php index 4fc97be1..334d4355 100644 --- a/classes/mutex/SpinlockMutex.php +++ b/classes/mutex/SpinlockMutex.php @@ -3,6 +3,7 @@ namespace malkusch\lock\mutex; use malkusch\lock\exception\ExecutionOutsideLockException; +use malkusch\lock\exception\LockedTimeoutException; use malkusch\lock\exception\LockAcquireException; use malkusch\lock\exception\LockReleaseException; use malkusch\lock\util\Loop; @@ -22,7 +23,12 @@ abstract class SpinlockMutex extends LockMutex * @var int The timeout in seconds a lock may live. */ private $timeout; - + + /** + * @var int The timeout in seconds a process will wait while mutex is locked + */ + private $lockedTimeout; + /** * @var Loop The loop. */ @@ -50,16 +56,18 @@ abstract class SpinlockMutex extends LockMutex * * @throws \LengthException The timeout must be greater than 0. */ - public function __construct(string $name, int $timeout = 3) + public function __construct(string $name, int $timeout = 3, int $lockedTimeout = null) { $this->timeout = $timeout; + $this->lockedTimeout = $lockedTimeout; $this->loop = new Loop($this->timeout); $this->key = self::PREFIX.$name; } protected function lock(): void { - $this->loop->execute(function (): void { + $this->start = microtime(true); + $this->loop->execute(function (): void { $this->acquired = microtime(true); /* @@ -71,6 +79,10 @@ protected function lock(): void */ if ($this->acquire($this->key, $this->timeout + 1)) { $this->loop->end(); + } elseif ($this->lockedTimeout !== null) { + if (microtime(true) - $this->start > $this->lockedTimeout) { + throw new LockedTimeoutException(); + } } }); } From 93c81287c84df1396b9c6ab37a15d40fe9c556d1 Mon Sep 17 00:00:00 2001 From: bstanescu Date: Mon, 7 Oct 2019 12:07:26 +0300 Subject: [PATCH 2/3] Add LockedTimeout test --- classes/mutex/SpinlockMutex.php | 2 +- tests/mutex/SpinlockMutexTest.php | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/classes/mutex/SpinlockMutex.php b/classes/mutex/SpinlockMutex.php index 99d87417..a541dd64 100644 --- a/classes/mutex/SpinlockMutex.php +++ b/classes/mutex/SpinlockMutex.php @@ -88,7 +88,7 @@ protected function lock(): void $this->loop->end(); } elseif ($this->lockedTimeout !== null) { if (microtime(true) - $this->start > $this->lockedTimeout) { - throw new LockedTimeoutException(); + throw new LockedTimeoutException("Timeout while locked of $this->lockedTimeout seconds exceeded."); } } }); diff --git a/tests/mutex/SpinlockMutexTest.php b/tests/mutex/SpinlockMutexTest.php index bc2a06d2..7a94dc5f 100644 --- a/tests/mutex/SpinlockMutexTest.php +++ b/tests/mutex/SpinlockMutexTest.php @@ -3,6 +3,7 @@ namespace malkusch\lock\mutex; use malkusch\lock\exception\ExecutionOutsideLockException; +use malkusch\lock\exception\LockedTimeoutException; use malkusch\lock\exception\LockAcquireException; use phpmock\environment\SleepEnvironmentBuilder; use phpmock\phpunit\PHPMock; @@ -48,6 +49,27 @@ public function testFailAcquireLock() }); } + /** + * Tests failing to acquire the lock due to a timeout (while lock is already taken). + * + * @expectedException \malkusch\lock\exception\LockedTimeoutException + * @expectedExceptionMessage Timeout while locked of 3 seconds exceeded. + */ + public function testLockedTimeoutExeption() + { + $timeout = 5; + $lockedTimeout = 3; + $mutex = $this->getMockForAbstractClass(SpinlockMutex::class, ['test', $timeout, $lockedTimeout]); + + $mutex->expects($this->atLeastOnce()) + ->method('acquire') + ->willReturn(false); + + $mutex->synchronized(function () { + $this->fail('execution is not expected'); + }); + } + /** * Tests failing to acquire the lock due to a timeout. * From c95afe9133fc79dda18ebe9ee9bff10f65c65d64 Mon Sep 17 00:00:00 2001 From: bstanescu Date: Mon, 7 Oct 2019 12:21:13 +0300 Subject: [PATCH 3/3] Fix phpcs warnings --- classes/mutex/SpinlockMutex.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/mutex/SpinlockMutex.php b/classes/mutex/SpinlockMutex.php index a541dd64..4b717b6a 100644 --- a/classes/mutex/SpinlockMutex.php +++ b/classes/mutex/SpinlockMutex.php @@ -74,7 +74,7 @@ public function __construct(string $name, int $timeout = 3, int $lockedTimeout = protected function lock(): void { $this->start = microtime(true); - $this->loop->execute(function (): void { + $this->loop->execute(function (): void { $this->acquired = microtime(true); /*