From 9e892866ab8388abe3c629d40d6c1e486aa92a78 Mon Sep 17 00:00:00 2001 From: Marc Beinder Date: Sat, 17 Jan 2026 09:55:20 -0600 Subject: [PATCH 1/6] Add Lock class for managing named locks with timeout support --- src/Objects/Support/Lock.php | 60 ++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 src/Objects/Support/Lock.php diff --git a/src/Objects/Support/Lock.php b/src/Objects/Support/Lock.php new file mode 100644 index 0000000..8d7409d --- /dev/null +++ b/src/Objects/Support/Lock.php @@ -0,0 +1,60 @@ + */ + private static array $locks = []; + + /** + * Execute a callback while holding a named lock + * + * @template T + * @param string $name The name of the lock to acquire + * @param int $timeout Maximum number of seconds to wait for the lock + * @param Closure(): T $callback The callback to execute while holding the lock + * @return T + */ + public static function block(string $name, int $timeout, Closure $callback): mixed + { + self::acquire($name, $timeout); + + try { + return $callback(); + } finally { + self::release($name); + } + } + + private static function acquire(string $name, int $timeout): void + { + $attempts = 0; + + while (self::isLocked($name) && $attempts < $timeout) { + Sleep::for(1)->seconds(); + $attempts++; + } + + self::$locks[$name] = true; + } + + private static function release(string $name): void + { + unset(self::$locks[$name]); + } + + private static function isLocked(string $name): bool + { + return self::$locks[$name] ?? false; + } +} \ No newline at end of file From 96519fd77c62815e7d73f992d24d24bbe5b02a9d Mon Sep 17 00:00:00 2001 From: Marc Beinder Date: Sat, 17 Jan 2026 09:58:58 -0600 Subject: [PATCH 2/6] Add LockTimeoutException for handling lock acquisition timeouts --- src/Exceptions/LockTimeoutException.php | 23 +++++++++++++++++++++++ src/Objects/Support/Lock.php | 6 +++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/Exceptions/LockTimeoutException.php diff --git a/src/Exceptions/LockTimeoutException.php b/src/Exceptions/LockTimeoutException.php new file mode 100644 index 0000000..47f4280 --- /dev/null +++ b/src/Exceptions/LockTimeoutException.php @@ -0,0 +1,23 @@ + Date: Sat, 17 Jan 2026 16:00:49 +0000 Subject: [PATCH 3/6] Rectifying --- src/Exceptions/LockTimeoutException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Exceptions/LockTimeoutException.php b/src/Exceptions/LockTimeoutException.php index 47f4280..d0eb80e 100644 --- a/src/Exceptions/LockTimeoutException.php +++ b/src/Exceptions/LockTimeoutException.php @@ -15,7 +15,7 @@ class LockTimeoutException extends BaseException public function __construct(string $lockName, int $timeout, ?Throwable $previous = null) { parent::__construct( - "Failed to acquire lock '$lockName' within $timeout second(s)", + "Failed to acquire lock '{$lockName}' within {$timeout} second(s)", ExitCode::GENERAL_ERROR, $previous ); From 4d947b79df7890ef11b093121016df642743da43 Mon Sep 17 00:00:00 2001 From: EncoreBot Date: Sat, 17 Jan 2026 16:00:49 +0000 Subject: [PATCH 4/6] Ignore Rector Commit in Git Blame --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 060e962..41441c3 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -61,3 +61,4 @@ bb927a60f808d44d10b6ffbd796982a3e5022c44 33a2233c7f0bfab90033796d51cbb722e2b5340e d0097bd14bf964bf4c283d07432ca52bfefa6391 0dc3de71ae8b40a7050bd1a106d31c216b9fc7d3 +430e41cb40dcd692360456fdd390c898bba8bd97 From 529667d80354f37d222563c9b37cff8b2e9cc3e5 Mon Sep 17 00:00:00 2001 From: EncoreBot Date: Sat, 17 Jan 2026 16:01:07 +0000 Subject: [PATCH 5/6] Dusting --- src/Objects/Support/Lock.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Objects/Support/Lock.php b/src/Objects/Support/Lock.php index 447fbc1..e838f2c 100644 --- a/src/Objects/Support/Lock.php +++ b/src/Objects/Support/Lock.php @@ -20,9 +20,10 @@ class Lock * Execute a callback while holding a named lock * * @template T - * @param string $name The name of the lock to acquire - * @param int $timeout Maximum number of seconds to wait for the lock - * @param Closure(): T $callback The callback to execute while holding the lock + * + * @param string $name The name of the lock to acquire + * @param int $timeout Maximum number of seconds to wait for the lock + * @param Closure(): T $callback The callback to execute while holding the lock * @return T */ public static function block(string $name, int $timeout, Closure $callback): mixed From 42d75ad8dea8f0a47c21e1674d6c04cbc8d7692f Mon Sep 17 00:00:00 2001 From: EncoreBot Date: Sat, 17 Jan 2026 16:01:07 +0000 Subject: [PATCH 6/6] Ignore Duster Commit in Git Blame --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 41441c3..e07c587 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -62,3 +62,4 @@ bb927a60f808d44d10b6ffbd796982a3e5022c44 d0097bd14bf964bf4c283d07432ca52bfefa6391 0dc3de71ae8b40a7050bd1a106d31c216b9fc7d3 430e41cb40dcd692360456fdd390c898bba8bd97 +529667d80354f37d222563c9b37cff8b2e9cc3e5