]> git.mxchange.org Git - friendica.git/blob - src/Core/Lock/Type/CacheLock.php
Restructure Lock to follow new paradigm
[friendica.git] / src / Core / Lock / Type / CacheLock.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2021, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Core\Lock\Type;
23
24 use Friendica\Core\Cache\Enum\Duration;
25 use Friendica\Core\Cache\IMemoryCache;
26
27 class CacheLock extends BaseLock
28 {
29         /**
30          * @var string The static prefix of all locks inside the cache
31          */
32         const CACHE_PREFIX = 'lock:';
33
34         /**
35          * @var \Friendica\Core\Cache\ICache;
36          */
37         private $cache;
38
39         /**
40          * CacheLock constructor.
41          *
42          * @param IMemoryCache $cache The CacheDriver for this type of lock
43          */
44         public function __construct(IMemoryCache $cache)
45         {
46                 $this->cache = $cache;
47         }
48
49         /**
50          * (@inheritdoc)
51          */
52         public function acquire($key, $timeout = 120, $ttl = Duration::FIVE_MINUTES)
53         {
54                 $got_lock = false;
55                 $start    = time();
56
57                 $cachekey = self::getLockKey($key);
58
59                 do {
60                         $lock = $this->cache->get($cachekey);
61                         // When we do want to lock something that was already locked by us.
62                         if ((int)$lock == getmypid()) {
63                                 $got_lock = true;
64                         }
65
66                         // When we do want to lock something new
67                         if (is_null($lock)) {
68                                 // At first initialize it with "0"
69                                 $this->cache->add($cachekey, 0);
70                                 // Now the value has to be "0" because otherwise the key was used by another process meanwhile
71                                 if ($this->cache->compareSet($cachekey, 0, getmypid(), $ttl)) {
72                                         $got_lock = true;
73                                         $this->markAcquire($key);
74                                 }
75                         }
76
77                         if (!$got_lock && ($timeout > 0)) {
78                                 usleep(rand(10000, 200000));
79                         }
80                 } while (!$got_lock && ((time() - $start) < $timeout));
81
82                 return $got_lock;
83         }
84
85         /**
86          * (@inheritdoc)
87          */
88         public function release($key, $override = false)
89         {
90                 $cachekey = self::getLockKey($key);
91
92                 if ($override) {
93                         $return = $this->cache->delete($cachekey);
94                 } else {
95                         $return = $this->cache->compareDelete($cachekey, getmypid());
96                 }
97                 $this->markRelease($key);
98
99                 return $return;
100         }
101
102         /**
103          * (@inheritdoc)
104          */
105         public function isLocked($key)
106         {
107                 $cachekey = self::getLockKey($key);
108                 $lock     = $this->cache->get($cachekey);
109                 return isset($lock) && ($lock !== false);
110         }
111
112         /**
113          * {@inheritDoc}
114          */
115         public function getName()
116         {
117                 return $this->cache->getName();
118         }
119
120         /**
121          * {@inheritDoc}
122          */
123         public function getLocks(string $prefix = '')
124         {
125                 $locks = $this->cache->getAllKeys(self::CACHE_PREFIX . $prefix);
126
127                 array_walk($locks, function (&$lock, $key) {
128                         $lock = substr($lock, strlen(self::CACHE_PREFIX));
129                 });
130
131                 return $locks;
132         }
133
134         /**
135          * {@inheritDoc}
136          */
137         public function releaseAll($override = false)
138         {
139                 $success = parent::releaseAll($override);
140
141                 $locks = $this->getLocks();
142
143                 foreach ($locks as $lock) {
144                         if (!$this->release($lock, $override)) {
145                                 $success = false;
146                         }
147                 }
148
149                 return $success;
150         }
151
152         /**
153          * @param string $key The original key
154          *
155          * @return string        The cache key used for the cache
156          */
157         private static function getLockKey($key)
158         {
159                 return self::CACHE_PREFIX . $key;
160         }
161 }