summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobin Appelman <icewind@owncloud.com>2015-10-20 14:04:50 +0200
committerThomas Müller <thomas.mueller@tmit.eu>2015-10-21 09:43:30 +0200
commitcc7bd53d1719d17a9fe222c2f4ed9e207d10c805 (patch)
tree3b4dc61c0185443731d3c2a0bef417f1bbeabe41
parent74f41349b76e258e17fe4a39d7845fdcca3d554f (diff)
downloadnextcloud-server-cc7bd53d1719d17a9fe222c2f4ed9e207d10c805.tar.gz
nextcloud-server-cc7bd53d1719d17a9fe222c2f4ed9e207d10c805.zip
Keep shared locks until the end of the request so we can reuse them
-rw-r--r--lib/private/lock/abstractlockingprovider.php8
-rw-r--r--lib/private/lock/dblockingprovider.php104
2 files changed, 94 insertions, 18 deletions
diff --git a/lib/private/lock/abstractlockingprovider.php b/lib/private/lock/abstractlockingprovider.php
index eb86be68500..ae0face325e 100644
--- a/lib/private/lock/abstractlockingprovider.php
+++ b/lib/private/lock/abstractlockingprovider.php
@@ -33,6 +33,14 @@ abstract class AbstractLockingProvider implements ILockingProvider {
'exclusive' => []
];
+ protected function hasAcquiredLock($path, $type) {
+ 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;
+ }
+ }
+
/**
* Mark a locally acquired lock
*
diff --git a/lib/private/lock/dblockingprovider.php b/lib/private/lock/dblockingprovider.php
index 450a0a27ab0..c1f24a9c132 100644
--- a/lib/private/lock/dblockingprovider.php
+++ b/lib/private/lock/dblockingprovider.php
@@ -25,6 +25,7 @@ namespace OC\Lock;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IDBConnection;
use OCP\ILogger;
+use OCP\Lock\ILockingProvider;
use OCP\Lock\LockedException;
/**
@@ -46,8 +47,42 @@ class DBLockingProvider extends AbstractLockingProvider {
*/
private $timeFactory;
+ private $sharedLocks = [];
+
const TTL = 3600; // how long until we clear stray locks in seconds
+ protected function isLocallyLocked($path) {
+ return isset($this->sharedLocks[$path]) && $this->sharedLocks[$path];
+ }
+
+ /**
+ * Mark a locally acquired lock
+ *
+ * @param string $path
+ * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
+ */
+ protected function markAcquire($path, $type) {
+ parent::markAcquire($path, $type);
+ if ($type === self::LOCK_SHARED) {
+ $this->sharedLocks[$path] = true;
+ }
+ }
+
+ /**
+ * Change the type of an existing tracked lock
+ *
+ * @param string $path
+ * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
+ */
+ protected function markChange($path, $targetType) {
+ parent::markChange($path, $targetType);
+ if ($targetType === self::LOCK_SHARED) {
+ $this->sharedLocks[$path] = true;
+ } else if ($targetType === self::LOCK_EXCLUSIVE) {
+ $this->sharedLocks[$path] = false;
+ }
+ }
+
/**
* @param \OCP\IDBConnection $connection
* @param \OCP\ILogger $logger
@@ -85,11 +120,19 @@ class DBLockingProvider extends AbstractLockingProvider {
* @return bool
*/
public function isLocked($path, $type) {
+ if ($this->hasAcquiredLock($path, $type)) {
+ return true;
+ }
$query = $this->connection->prepare('SELECT `lock` from `*PREFIX*file_locks` WHERE `key` = ?');
$query->execute([$path]);
$lockValue = (int)$query->fetchColumn();
if ($type === self::LOCK_SHARED) {
- return $lockValue > 0;
+ if ($this->isLocallyLocked($path)) {
+ // if we have a shared lock we kept open locally but it's released we always have at least 1 shared lock in the db
+ return $lockValue > 1;
+ } else {
+ return $lockValue > 0;
+ }
} else if ($type === self::LOCK_EXCLUSIVE) {
return $lockValue === -1;
} else {
@@ -105,19 +148,27 @@ class DBLockingProvider extends AbstractLockingProvider {
public function acquireLock($path, $type) {
$expire = $this->getExpireTime();
if ($type === self::LOCK_SHARED) {
- $result = $this->initLockField($path,1);
- if ($result <= 0) {
- $result = $this->connection->executeUpdate (
- 'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0',
- [$expire, $path]
- );
+ if (!$this->isLocallyLocked($path)) {
+ $result = $this->initLockField($path, 1);
+ if ($result <= 0) {
+ $result = $this->connection->executeUpdate(
+ 'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0',
+ [$expire, $path]
+ );
+ }
+ } else {
+ $result = 1;
}
} else {
- $result = $this->initLockField($path,-1);
+ $existing = 0;
+ if ($this->hasAcquiredLock($path, ILockingProvider::LOCK_SHARED) === false && $this->isLocallyLocked($path)) {
+ $existing = 1;
+ }
+ $result = $this->initLockField($path, -1);
if ($result <= 0) {
$result = $this->connection->executeUpdate(
- 'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 0',
- [$expire, $path]
+ 'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = ?',
+ [$expire, $path, $existing]
);
}
}
@@ -132,19 +183,15 @@ class DBLockingProvider extends AbstractLockingProvider {
* @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
*/
public function releaseLock($path, $type) {
- if ($type === self::LOCK_SHARED) {
- $this->connection->executeUpdate(
- 'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` - 1 WHERE `key` = ? AND `lock` > 0',
- [$path]
- );
- } else {
+ $this->markRelease($path, $type);
+
+ // we keep shared locks till the end of the request so we can re-use them
+ if ($type === self::LOCK_EXCLUSIVE) {
$this->connection->executeUpdate(
'UPDATE `*PREFIX*file_locks` SET `lock` = 0 WHERE `key` = ? AND `lock` = -1',
[$path]
);
}
-
- $this->markRelease($path, $type);
}
/**
@@ -162,6 +209,10 @@ class DBLockingProvider extends AbstractLockingProvider {
[$expire, $path]
);
} else {
+ // since we only keep one shared lock in the db we need to check if we have more then one shared lock locally manually
+ if (isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 1) {
+ throw new LockedException($path);
+ }
$result = $this->connection->executeUpdate(
'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 1',
[$expire, $path]
@@ -184,6 +235,23 @@ class DBLockingProvider extends AbstractLockingProvider {
);
}
+ /**
+ * release all lock acquired by this instance which were marked using the mark* methods
+ */
+ public function releaseAll() {
+ parent::releaseAll();
+
+ // since we keep shared locks we need to manually clean those
+ foreach ($this->sharedLocks as $path => $lock) {
+ if ($lock) {
+ $this->connection->executeUpdate(
+ 'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` - 1 WHERE `key` = ? AND `lock` > 0',
+ [$path]
+ );
+ }
+ }
+ }
+
public function __destruct() {
try {
$this->cleanEmptyLocks();