Completed
Push — stable13 ( ef1a69...922c2c )
by Morris
18:35
created

MemcacheLockingProvider   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 117
Duplicated Lines 23.93 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 28
loc 117
rs 10
c 0
b 0
f 0
wmc 24
lcom 1
cbo 4

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A setTTL() 0 5 2
A isLocked() 0 10 3
A acquireLock() 14 14 4
B releaseLock() 0 22 6
A changeLock() 14 14 5
A getExistingLockForException() 0 10 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Robin Appelman <[email protected]>
6
 *
7
 * @license AGPL-3.0
8
 *
9
 * This code is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Affero General Public License, version 3,
11
 * as published by the Free Software Foundation.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License, version 3,
19
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
20
 *
21
 */
22
23
namespace OC\Lock;
24
25
use OCP\IMemcacheTTL;
26
use OCP\Lock\LockedException;
27
use OCP\IMemcache;
28
29
class MemcacheLockingProvider extends AbstractLockingProvider {
30
	/**
31
	 * @var \OCP\IMemcache
32
	 */
33
	private $memcache;
34
35
	/**
36
	 * @param \OCP\IMemcache $memcache
37
	 * @param int $ttl
38
	 */
39
	public function __construct(IMemcache $memcache, $ttl = 3600) {
40
		$this->memcache = $memcache;
41
		$this->ttl = $ttl;
42
	}
43
44
	private function setTTL($path) {
45
		if ($this->memcache instanceof IMemcacheTTL) {
46
			$this->memcache->setTTL($path, $this->ttl);
47
		}
48
	}
49
50
	/**
51
	 * @param string $path
52
	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
53
	 * @return bool
54
	 */
55
	public function isLocked($path, $type) {
56
		$lockValue = $this->memcache->get($path);
57
		if ($type === self::LOCK_SHARED) {
58
			return $lockValue > 0;
59
		} else if ($type === self::LOCK_EXCLUSIVE) {
60
			return $lockValue === 'exclusive';
61
		} else {
62
			return false;
63
		}
64
	}
65
66
	/**
67
	 * @param string $path
68
	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
69
	 * @throws \OCP\Lock\LockedException
70
	 */
71 View Code Duplication
	public function acquireLock($path, $type) {
72
		if ($type === self::LOCK_SHARED) {
73
			if (!$this->memcache->inc($path)) {
74
				throw new LockedException($path, null, $this->getExistingLockForException($path));
75
			}
76
		} else {
77
			$this->memcache->add($path, 0);
78
			if (!$this->memcache->cas($path, 0, 'exclusive')) {
79
				throw new LockedException($path, null, $this->getExistingLockForException($path));
80
			}
81
		}
82
		$this->setTTL($path);
83
		$this->markAcquire($path, $type);
84
	}
85
86
	/**
87
	 * @param string $path
88
	 * @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
89
	 */
90
	public function releaseLock($path, $type) {
91
		if ($type === self::LOCK_SHARED) {
92
			$newValue = 0;
93
			if ($this->getOwnSharedLockCount($path) === 1) {
94
				$removed = $this->memcache->cad($path, 1); // if we're the only one having a shared lock we can remove it in one go
95
				if (!$removed) { //someone else also has a shared lock, decrease only
96
					$newValue = $this->memcache->dec($path);
97
				}
98
			} else {
99
				// if we own more than one lock ourselves just decrease
100
				$newValue = $this->memcache->dec($path);
101
			}
102
103
			// if we somehow release more locks then exists, reset the lock
104
			if ($newValue < 0) {
105
				$this->memcache->cad($path, $newValue);
106
			}
107
		} else if ($type === self::LOCK_EXCLUSIVE) {
108
			$this->memcache->cad($path, 'exclusive');
109
		}
110
		$this->markRelease($path, $type);
111
	}
112
113
	/**
114
	 * Change the type of an existing lock
115
	 *
116
	 * @param string $path
117
	 * @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
118
	 * @throws \OCP\Lock\LockedException
119
	 */
120 View Code Duplication
	public function changeLock($path, $targetType) {
121
		if ($targetType === self::LOCK_SHARED) {
122
			if (!$this->memcache->cas($path, 'exclusive', 1)) {
123
				throw new LockedException($path, null, $this->getExistingLockForException($path));
124
			}
125
		} else if ($targetType === self::LOCK_EXCLUSIVE) {
126
			// we can only change a shared lock to an exclusive if there's only a single owner of the shared lock
127
			if (!$this->memcache->cas($path, 1, 'exclusive')) {
128
				throw new LockedException($path, null, $this->getExistingLockForException($path));
129
			}
130
		}
131
		$this->setTTL($path);
132
		$this->markChange($path, $targetType);
133
	}
134
135
	private function getExistingLockForException($path) {
136
		$existing = $this->memcache->get($path);
137
		if (!$existing) {
138
			return 'none';
139
		} else if ($existing === 'exclusive') {
140
			return $existing;
141
		} else {
142
			return $existing . ' shared locks';
143
		}
144
	}
145
}
146