From b659878386abfef5f230745b3557f4ffe6b88062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Fri, 6 Dec 2024 22:53:58 +0100 Subject: [PATCH 1/2] Remove CASMutex class --- README.md | 35 ++------------ composer.json | 1 - src/Mutex/CASMutex.php | 75 ----------------------------- tests/Mutex/CASMutexTest.php | 92 ------------------------------------ 4 files changed, 4 insertions(+), 199 deletions(-) delete mode 100644 src/Mutex/CASMutex.php delete mode 100644 tests/Mutex/CASMutexTest.php diff --git a/README.md b/README.md index b25e3641..b0ec6679 100644 --- a/README.md +++ b/README.md @@ -127,8 +127,8 @@ if ($newBalance === false) { ### Extracting code result after lock release exception -Mutex implementations based on [`Malkush\Lock\Mutex\LockMutex`][12] will throw -[`Malkusch\Lock\Exception\LockReleaseException`][13] in case of lock release +Mutex implementations based on [`Malkush\Lock\Mutex\LockMutex`][10] will throw +[`Malkusch\Lock\Exception\LockReleaseException`][11] in case of lock release problem, but the synchronized code block will be already executed at this point. In order to read the code result (or an exception thrown there), `LockReleaseException` provides methods to extract it. @@ -164,7 +164,6 @@ Because the [`Malkusch\Lock\Mutex\Mutex`](#mutex) class is an abstract class, you can choose from one of the provided implementations or create/extend your own implementation. -- [`CASMutex`](#casmutex) - [`FlockMutex`](#flockmutex) - [`MemcachedMutex`](#memcachedmutex) - [`PHPRedisMutex`](#phpredismutex) @@ -174,30 +173,6 @@ own implementation. - [`MySQLMutex`](#mysqlmutex) - [`PgAdvisoryLockMutex`](#pgadvisorylockmutex) -#### CASMutex - -The **CASMutex** has to be used with a [Compare-and-swap][10] operation. This -mutex is lock free. It will repeat executing the code until the CAS operation -was successful. The code should therefore notify the mutex by calling -[`Malkusch\Lock\Mutex\CASMutex::notify()`][11]. - -As the mutex keeps executing the critical code, it must not have any side -effects as long as the CAS operation was not successful. - -Example: - -```php -$mutex = new CASMutex(); -$mutex->synchronized(function () use ($memcached, $mutex, $amount): void { - $balance = $memcached->get('balance', null, $casToken); - $balance -= $amount; - if (!$memcached->cas($casToken, 'balance', $balance)) { - return; - } - $mutex->notify(); -}); -``` - #### FlockMutex The **FlockMutex** is a lock implementation based on @@ -406,7 +381,5 @@ This project is free and is licensed under the MIT. [7]: https://github.com/php-lock/lock/blob/35526aee28/src/mutex/Mutex.php#L60 [8]: https://github.com/php-lock/lock/blob/35526aee28/src/util/DoubleCheckedLocking.php#L63 [9]: https://en.wikipedia.org/wiki/Double-checked_locking -[10]: https://en.wikipedia.org/wiki/Compare-and-swap -[11]: https://github.com/php-lock/lock/blob/35526aee28/src/mutex/CASMutex.php#L42 -[12]: https://github.com/php-lock/lock/blob/35526aee28/src/mutex/LockMutex.php -[13]: https://github.com/php-lock/lock/blob/35526aee28/src/exception/LockReleaseException.php +[10]: https://github.com/php-lock/lock/blob/35526aee28/src/mutex/LockMutex.php +[11]: https://github.com/php-lock/lock/blob/35526aee28/src/exception/LockReleaseException.php diff --git a/composer.json b/composer.json index 92754356..6a240a07 100644 --- a/composer.json +++ b/composer.json @@ -12,7 +12,6 @@ "redlock", "memcache", "redis", - "cas", "advisory-locks", "mysql", "postgresql" diff --git a/src/Mutex/CASMutex.php b/src/Mutex/CASMutex.php deleted file mode 100644 index 7d235135..00000000 --- a/src/Mutex/CASMutex.php +++ /dev/null @@ -1,75 +0,0 @@ -loop = new Loop($timeout); - } - - /** - * Notifies the Mutex about a successful CAS operation. - */ - public function notify(): void - { - $this->loop->end(); - } - - /** - * Repeats executing a code until a compare-and-swap operation was successful. - * - * The code has to be designed in a way that it can be repeated without any - * side effects. When the CAS operation was successful it should notify - * this mutex by calling {@link CASMutex::notify()}. I.e. the only side effects - * of the code may happen after a successful CAS operation. The CAS - * operation itself is a valid side effect as well. - * - * If the code throws an exception it will stop repeating the execution. - * - * Example: - * - * $mutex = new CASMutex(); - * $mutex->synchronized(static function () use ($memcached, $mutex, $amount) { - * $balance = $memcached->get('balance', null, $casToken); - * $balance -= $amount; - * if (!$memcached->cas($casToken, 'balance', $balance)) { - * return; - * } - * $mutex->notify(); - * }); - * - * - * @throws \Exception The execution block threw an exception - * @throws TimeoutException The timeout was reached - */ - #[\Override] - public function synchronized(callable $code) - { - return $this->loop->execute($code); - } -} diff --git a/tests/Mutex/CASMutexTest.php b/tests/Mutex/CASMutexTest.php deleted file mode 100644 index a9988dd0..00000000 --- a/tests/Mutex/CASMutexTest.php +++ /dev/null @@ -1,92 +0,0 @@ -addNamespace(__NAMESPACE__); - $sleepBuilder->addNamespace('Malkusch\Lock\Mutex'); - $sleepBuilder->addNamespace('Malkusch\Lock\Util'); - $sleep = $sleepBuilder->build(); - try { - $sleep->enable(); - $this->registerForTearDown($sleep); - } catch (MockEnabledException $e) { - // workaround for burn testing - \assert($e->getMessage() === 'microtime is already enabled.Call disable() on the existing mock.'); - } - } - - /** - * Tests exceeding the execution timeout. - */ - public function testExceedTimeout(): void - { - $this->expectException(LockAcquireException::class); - - $mutex = new CASMutex(1); - $mutex->synchronized(static function (): void { - sleep(2); - }); - } - - /** - * Tests that an exception would stop any further iteration. - */ - public function testExceptionStopsIteration(): void - { - $this->expectException(\DomainException::class); - - $mutex = new CASMutex(); - $mutex->synchronized(static function () { - throw new \DomainException(); - }); - } - - /** - * Tests notify() will stop the iteration and return the result. - */ - public function testNotify(): void - { - $i = 0; - $mutex = new CASMutex(); - $mutex->synchronized(static function () use ($mutex, &$i) { - ++$i; - $mutex->notify(); - }); - self::assertSame(1, $i); - } - - /** - * Tests that the code is executed more times. - */ - public function testIteration(): void - { - $i = 0; - $mutex = new CASMutex(); - $mutex->synchronized(static function () use ($mutex, &$i): void { - ++$i; - if ($i > 1) { - $mutex->notify(); - } - }); - self::assertSame(2, $i); - } -} From 8eb0694fca866a340414b8ae8b9dea79b446a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Fri, 6 Dec 2024 23:50:36 +0100 Subject: [PATCH 2/2] fix CI --- tests/Mutex/FixCiTest.php | 44 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 tests/Mutex/FixCiTest.php diff --git a/tests/Mutex/FixCiTest.php b/tests/Mutex/FixCiTest.php new file mode 100644 index 00000000..2848fe2a --- /dev/null +++ b/tests/Mutex/FixCiTest.php @@ -0,0 +1,44 @@ +addNamespace(__NAMESPACE__); + $sleepBuilder->addNamespace('Malkusch\Lock\Mutex'); + $sleepBuilder->addNamespace('Malkusch\Lock\Util'); + $sleep = $sleepBuilder->build(); + try { + $sleep->enable(); + $this->registerForTearDown($sleep); + } catch (MockEnabledException $e) { + // workaround for burn testing + \assert($e->getMessage() === 'microtime is already enabled.Call disable() on the existing mock.'); + } + } + + public function testDummy(): void + { + self::assertTrue(microtime(true) > 1.0); + } +}