summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Appelman <icewind@owncloud.com>2015-05-29 14:34:21 +0200
committerRobin Appelman <icewind@owncloud.com>2015-06-01 13:24:02 +0200
commita1372b2fb5acc51eacf70012a6702a96c78e61ff (patch)
tree391f8392a034c276ec7838698cd4af3a8bd6fbd5
parent43772e2a9a4b1400e7e3a8874130ac817aa64058 (diff)
downloadnextcloud-server-a1372b2fb5acc51eacf70012a6702a96c78e61ff.tar.gz
nextcloud-server-a1372b2fb5acc51eacf70012a6702a96c78e61ff.zip
add method to atomically change between shared and exclusive lock
-rw-r--r--lib/private/lock/memcachelockingprovider.php20
-rw-r--r--lib/public/lock/ilockingprovider.php9
-rw-r--r--tests/lib/lock/lockingprovider.php53
3 files changed, 82 insertions, 0 deletions
diff --git a/lib/private/lock/memcachelockingprovider.php b/lib/private/lock/memcachelockingprovider.php
index 1334d007314..368ff450325 100644
--- a/lib/private/lock/memcachelockingprovider.php
+++ b/lib/private/lock/memcachelockingprovider.php
@@ -99,6 +99,26 @@ class MemcacheLockingProvider implements ILockingProvider {
}
/**
+ * Change the type of an existing lock
+ *
+ * @param string $path
+ * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
+ * @throws \OCP\Lock\LockedException
+ */
+ public function changeLock($path, $targetType) {
+ if ($targetType === self::LOCK_SHARED) {
+ if (!$this->memcache->cas($path, 'exclusive', 1)) {
+ throw new LockedException($path);
+ }
+ } else if ($targetType === self::LOCK_EXCLUSIVE) {
+ // we can only change a shared lock to an exclusive if there's only a single owner of the shared lock
+ if (!$this->memcache->cas($path, 1, 'exclusive')) {
+ throw new LockedException($path);
+ }
+ }
+ }
+
+ /**
* release all lock acquired by this instance
*/
public function releaseAll() {
diff --git a/lib/public/lock/ilockingprovider.php b/lib/public/lock/ilockingprovider.php
index 18fa47fa631..6a963b9d7aa 100644
--- a/lib/public/lock/ilockingprovider.php
+++ b/lib/public/lock/ilockingprovider.php
@@ -46,6 +46,15 @@ interface ILockingProvider {
public function releaseLock($path, $type);
/**
+ * Change the type of an existing lock
+ *
+ * @param string $path
+ * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
+ * @throws \OCP\Lock\LockedException
+ */
+ public function changeLock($path, $targetType);
+
+ /**
* release all lock acquired by this instance
*/
public function releaseAll();
diff --git a/tests/lib/lock/lockingprovider.php b/tests/lib/lock/lockingprovider.php
index 337aa4cea7e..efd6e1939f2 100644
--- a/tests/lib/lock/lockingprovider.php
+++ b/tests/lib/lock/lockingprovider.php
@@ -158,4 +158,57 @@ abstract class LockingProvider extends TestCase {
$this->assertEquals('foo', $e->getPath());
}
}
+
+ public function testChangeLockToExclusive() {
+ $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
+ $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
+ $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
+ $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
+ }
+
+ public function testChangeLockToShared() {
+ $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
+ $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
+ $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
+ $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
+ }
+
+ /**
+ * @expectedException \OCP\Lock\LockedException
+ */
+ public function testChangeLockToExclusiveDoubleShared() {
+ $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
+ $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
+ $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
+ }
+
+ /**
+ * @expectedException \OCP\Lock\LockedException
+ */
+ public function testChangeLockToExclusiveNoShared() {
+ $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
+ }
+
+ /**
+ * @expectedException \OCP\Lock\LockedException
+ */
+ public function testChangeLockToExclusiveFromExclusive() {
+ $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
+ $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
+ }
+
+ /**
+ * @expectedException \OCP\Lock\LockedException
+ */
+ public function testChangeLockToSharedNoExclusive() {
+ $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
+ }
+
+ /**
+ * @expectedException \OCP\Lock\LockedException
+ */
+ public function testChangeLockToSharedFromShared() {
+ $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
+ $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
+ }
}