From a50eaac982e94c45ec41cdbcab0aa14c2d7cf21f Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 21 Jul 2026 15:32:16 -0400 Subject: [PATCH 1/2] refactor(lock): simplify acquired lock tracking Document the tracked lock state, remove redundant inherited documentation, and simplify shared and exclusive lock bookkeeping. Signed-off-by: Josh --- lib/private/Lock/AbstractLockingProvider.php | 69 +++++++++++--------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/lib/private/Lock/AbstractLockingProvider.php b/lib/private/Lock/AbstractLockingProvider.php index 947a239fd578c..63897d1b5b44a 100644 --- a/lib/private/Lock/AbstractLockingProvider.php +++ b/lib/private/Lock/AbstractLockingProvider.php @@ -16,70 +16,79 @@ * to release any leftover locks at the end of the request */ abstract class AbstractLockingProvider implements ILockingProvider { + /** + * @var array{ + * shared: array, + * exclusive: array, + * } + */ protected array $acquiredLocks = [ 'shared' => [], - 'exclusive' => [] + 'exclusive' => [], ]; /** - * - * @param int $ttl how long until we clear stray locks in seconds + * @param int $ttl How long until stray locks are cleared, in seconds. */ public function __construct( protected int $ttl, ) { } - /** @inheritDoc */ protected function hasAcquiredLock(string $path, int $type): bool { if ($type === self::LOCK_SHARED) { - return isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 0; - } else { - return isset($this->acquiredLocks['exclusive'][$path]) && $this->acquiredLocks['exclusive'][$path] === true; + return isset($this->acquiredLocks['shared'][$path]) + && $this->acquiredLocks['shared'][$path] > 0; } + + return $type === self::LOCK_EXCLUSIVE + && isset($this->acquiredLocks['exclusive'][$path]); } - /** @inheritDoc */ protected function markAcquire(string $path, int $targetType): void { + if ($targetType === self::LOCK_EXCLUSIVE) { + $this->acquiredLocks['exclusive'][$path] = true; + return; + } + if ($targetType === self::LOCK_SHARED) { - if (!isset($this->acquiredLocks['shared'][$path])) { - $this->acquiredLocks['shared'][$path] = 0; - } + $this->acquiredLocks['shared'][$path] ??= 0; $this->acquiredLocks['shared'][$path]++; - } else { - $this->acquiredLocks['exclusive'][$path] = true; } } - /** @inheritDoc */ protected function markRelease(string $path, int $type): void { + if ($type === self::LOCK_EXCLUSIVE) { + unset($this->acquiredLocks['exclusive'][$path]); + return; + } + if ($type === self::LOCK_SHARED) { - if (isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 0) { - $this->acquiredLocks['shared'][$path]--; - if ($this->acquiredLocks['shared'][$path] === 0) { - unset($this->acquiredLocks['shared'][$path]); - } + if (!isset($this->acquiredLocks['shared'][$path])) { + return; + } + + $this->acquiredLocks['shared'][$path]--; + if ($this->acquiredLocks['shared'][$path] === 0) { + unset($this->acquiredLocks['shared'][$path]); } - } elseif ($type === self::LOCK_EXCLUSIVE) { - unset($this->acquiredLocks['exclusive'][$path]); } } - /** @inheritDoc */ protected function markChange(string $path, int $targetType): void { + if ($targetType === self::LOCK_EXCLUSIVE) { + $this->acquiredLocks['exclusive'][$path] = true; + unset($this->acquiredLocks['shared'][$path]); + return; + } + if ($targetType === self::LOCK_SHARED) { unset($this->acquiredLocks['exclusive'][$path]); - if (!isset($this->acquiredLocks['shared'][$path])) { - $this->acquiredLocks['shared'][$path] = 0; - } + $this->acquiredLocks['shared'][$path] ??= 0; $this->acquiredLocks['shared'][$path]++; - } elseif ($targetType === self::LOCK_EXCLUSIVE) { - $this->acquiredLocks['exclusive'][$path] = true; - $this->acquiredLocks['shared'][$path]--; } } - /** @inheritDoc */ #[\Override] public function releaseAll(): void { foreach ($this->acquiredLocks['shared'] as $path => $count) { @@ -88,7 +97,7 @@ public function releaseAll(): void { } } - foreach ($this->acquiredLocks['exclusive'] as $path => $hasLock) { + foreach ($this->acquiredLocks['exclusive'] as $path => $_) { $this->releaseLock($path, self::LOCK_EXCLUSIVE); } } From de89f0f67f59b650d5a35eff81ce31fbd9a9d3a1 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 21 Jul 2026 16:17:11 -0400 Subject: [PATCH 2/2] chore(lock): add a shared lock type validator helper For use by providers Signed-off-by: Josh --- lib/private/Lock/AbstractLockingProvider.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/private/Lock/AbstractLockingProvider.php b/lib/private/Lock/AbstractLockingProvider.php index 63897d1b5b44a..625244d69b94e 100644 --- a/lib/private/Lock/AbstractLockingProvider.php +++ b/lib/private/Lock/AbstractLockingProvider.php @@ -35,6 +35,12 @@ public function __construct( ) { } + protected function assertValidLockType(int $type): void { + if ($type !== self::LOCK_SHARED && $type !== self::LOCK_EXCLUSIVE) { + throw new \InvalidArgumentException('Invalid lock type: ' . $type); + } + } + protected function hasAcquiredLock(string $path, int $type): bool { if ($type === self::LOCK_SHARED) { return isset($this->acquiredLocks['shared'][$path])