]> git.mxchange.org Git - friendica.git/blob - src/Core/Lock/DatabaseLock.php
Merge pull request #9039 from MrPetovan/task/frio-accent-scheme
[friendica.git] / src / Core / Lock / DatabaseLock.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2020, Friendica
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;
23
24 use Friendica\Core\BaseLock;
25 use Friendica\Core\Cache\Duration;
26 use Friendica\Database\Database;
27 use Friendica\Util\DateTimeFormat;
28
29 /**
30  * Locking driver that stores the locks in the database
31  */
32 class DatabaseLock extends BaseLock
33 {
34         /**
35          * The current ID of the process
36          *
37          * @var int
38          */
39         private $pid;
40
41         /**
42          * @var Database The database connection of Friendica
43          */
44         private $dba;
45
46         /**
47          * @param null|int $pid The Id of the current process (null means determine automatically)
48          */
49         public function __construct(Database $dba, $pid = null)
50         {
51                 $this->dba = $dba;
52                 $this->pid = isset($pid) ? $pid : getmypid();
53         }
54
55         /**
56          * (@inheritdoc)
57          */
58         public function acquire($key, $timeout = 120, $ttl = Duration::FIVE_MINUTES)
59         {
60                 $got_lock = false;
61                 $start    = time();
62
63                 do {
64                         $this->dba->lock('locks');
65                         $lock = $this->dba->selectFirst('locks', ['locked', 'pid'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
66
67                         if ($this->dba->isResult($lock)) {
68                                 if ($lock['locked']) {
69                                         // We want to lock something that was already locked by us? So we got the lock.
70                                         if ($lock['pid'] == $this->pid) {
71                                                 $got_lock = true;
72                                         }
73                                 }
74                                 if (!$lock['locked']) {
75                                         $this->dba->update('locks', ['locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')], ['name' => $key]);
76                                         $got_lock = true;
77                                 }
78                         } else {
79                                 $this->dba->insert('locks', ['name' => $key, 'locked' => true, 'pid' => $this->pid, 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]);
80                                 $got_lock = true;
81                                 $this->markAcquire($key);
82                         }
83
84                         $this->dba->unlock();
85
86                         if (!$got_lock && ($timeout > 0)) {
87                                 usleep(rand(100000, 2000000));
88                         }
89                 } while (!$got_lock && ((time() - $start) < $timeout));
90
91                 return $got_lock;
92         }
93
94         /**
95          * (@inheritdoc)
96          */
97         public function release($key, $override = false)
98         {
99                 if ($override) {
100                         $where = ['name' => $key];
101                 } else {
102                         $where = ['name' => $key, 'pid' => $this->pid];
103                 }
104
105                 if ($this->dba->exists('locks', $where)) {
106                         $return = $this->dba->delete('locks', $where);
107                 } else {
108                         $return = false;
109                 }
110
111                 $this->markRelease($key);
112
113                 return $return;
114         }
115
116         /**
117          * (@inheritdoc)
118          */
119         public function releaseAll($override = false)
120         {
121                 $success = parent::releaseAll($override);
122
123                 if ($override) {
124                         $where = ['1 = 1'];
125                 } else {
126                         $where = ['pid' => $this->pid];
127                 }
128                 $return = $this->dba->delete('locks', $where);
129
130                 $this->acquiredLocks = [];
131
132                 return $return && $success;
133         }
134
135         /**
136          * (@inheritdoc)
137          */
138         public function isLocked($key)
139         {
140                 $lock = $this->dba->selectFirst('locks', ['locked'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
141
142                 if ($this->dba->isResult($lock)) {
143                         return $lock['locked'] !== false;
144                 } else {
145                         return false;
146                 }
147         }
148
149         /**
150          * {@inheritDoc}
151          */
152         public function getName()
153         {
154                 return Type::DATABASE;
155         }
156
157         /**
158          * {@inheritDoc}
159          */
160         public function getLocks(string $prefix = '')
161         {
162                 if (empty($prefix)) {
163                         $where = ['`expires` >= ?', DateTimeFormat::utcNow()];
164                 } else {
165                         $where = ['`expires` >= ? AND `name` LIKE CONCAT(?, \'%\')', DateTimeFormat::utcNow(), $prefix];
166                 }
167
168                 $stmt = $this->dba->select('locks', ['name'], $where);
169
170                 $keys = [];
171                 while ($key = $this->dba->fetch($stmt)) {
172                         array_push($keys, $key['name']);
173                 }
174                 $this->dba->close($stmt);
175
176                 return $keys;
177         }
178 }