diff --git a/lib/private/Memcache/CADTrait.php b/lib/private/Memcache/CADTrait.php index d0f6611c4f3..d5c0d8174d0 100644 --- a/lib/private/Memcache/CADTrait.php +++ b/lib/private/Memcache/CADTrait.php @@ -7,6 +7,9 @@ */ namespace OC\Memcache; +/** + * CAD/NCAD implementations for cache backends that lack their own. + */ trait CADTrait { abstract public function get($key); @@ -15,42 +18,77 @@ trait CADTrait { abstract public function add($key, $value, $ttl = 0); /** - * Compare and delete + * Compare-and-delete. + * + * If $key's current value is equal to $expectedValue, delete $key. + * Note may return false for reasons other than not meeting condition. + * + * Implements CAD using simple locking (only requiring backend add/remove support). * * @param string $key - * @param mixed $old - * @return bool + * @param mixed $expectedValue + * @return bool True if key was deleted; False if no deletion occurred */ - public function cad($key, $old) { - //no native cas, emulate with locking - if ($this->add($key . '_lock', true)) { - if ($this->get($key) === $old) { - $this->remove($key); - $this->remove($key . '_lock'); - return true; - } else { - $this->remove($key . '_lock'); - return false; - } + public function cad($key, $expectedValue) { + if (!$this->acquireLock($key)) { + return false; + } + $currentValue = $this->get($key); + // Check condition + if ($currentValue === $expectedValue) { + // Matches condition + $this->remove($key); + $this->releaseLock($key); + return true; } else { + // Fails condition + $this->releaseLock($key); + // TODO: consider throwing if release fails return false; } } - public function ncad(string $key, mixed $old): bool { - //no native cad, emulate with locking - if ($this->add($key . '_lock', true)) { - $value = $this->get($key); - if ($value !== null && $value !== $old) { - $this->remove($key); - $this->remove($key . '_lock'); - return true; - } else { - $this->remove($key . '_lock'); - return false; - } + /** + * Nonequal-compare-and-delete. + * + * If $key's current value is not equal to $expectedValue, delete $key. + * Note may return false for reasons other than not meeting condition. + * + * Implements NCAD using simple locking (only requiring backend add/remove support). + * + * @param string $key + * @param mixed $expectedValue + * @return bool True if key was deleted; False if no deletion occurred + */ + public function ncad(string $key, mixed $expectedValue): bool { + if (!$this->acquireLock($key)) { + return false; + } + $currentValue = $this->get($key); + // Check condition + if ($currentValue !== null && $currentValue !== $expectedValue) { + // Matches condition + $this->remove($key); + $this->releaseLock($key); + return true; } else { + // Fails condition + $this->releaseLock($key); + // TODO: consider throwing if release fails return false; } } + + // + // Utilities + // + + private function acquireLock(string $key): bool { + // TODO: an actual TTL would be clearer + return $this->add($key . '_lock', true); + } + + private function releaseLock(string $key): bool { + return $this->remove($key . '_lock'); + } }