PluginProbe
Authorizer / 3.14.2
Authorizer v3.14.2
3.15.3 3.15.2 3.15.1 3.15.0 3.14.3 3.14.4 3.14.2 3.14.1 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.9.0 2.9.1 2.9.10 2.9.11 2.9.12 2.9.13 2.9.2 2.9.3 2.9.6 All 126 releases
authorizer / vendor / google / auth / src / Cache / SysVCacheItemPool.php

SysVCacheItemPool.php in Authorizer 3.14.2, at vendor/google/auth/src/Cache/SysVCacheItemPool.php

353 lines 9.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Copyright 2018 Google Inc. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 namespace Google\Auth\Cache;
18
19 use Psr\Cache\CacheItemInterface;
20 use Psr\Cache\CacheItemPoolInterface;
21 use SysvSemaphore;
22 use SysvSharedMemory;
23
24 /**
25 * SystemV shared memory based CacheItemPool implementation.
26 *
27 * This CacheItemPool implementation can be used among multiple processes, but
28 * it doesn't provide any locking mechanism. If multiple processes write to
29 * this ItemPool, you have to avoid race condition manually in your code.
30 */
31 class SysVCacheItemPool implements CacheItemPoolInterface
32 {
33 const VAR_KEY = 1;
34
35 const DEFAULT_PROJ = 'A';
36
37 const DEFAULT_SEM_PROJ = 'B';
38
39 const DEFAULT_MEMSIZE = 10000;
40
41 const DEFAULT_PERM = 0600;
42
43 /**
44 * @var int
45 */
46 private $sysvKey;
47
48 /**
49 * @var CacheItemInterface[]
50 */
51 private $items;
52
53 /**
54 * @var CacheItemInterface[]
55 */
56 private $deferredItems;
57
58 /**
59 * @var array<mixed>
60 */
61 private $options;
62
63 /**
64 * @var bool
65 */
66 private $hasLoadedItems = false;
67
68 /**
69 * @var SysvSemaphore|false
70 */
71 private SysvSemaphore|false $semId = false;
72
73 /**
74 * Maintain the process which is currently holding the semaphore to prevent deadlock.
75 *
76 * @var int|null
77 */
78 private ?int $lockOwnerPid = null;
79
80 /**
81 * Create a SystemV shared memory based CacheItemPool.
82 *
83 * @param array<mixed> $options {
84 * [optional] Configuration options.
85 *
86 * @type int $variableKey The variable key for getting the data from the shared memory. **Defaults to** 1.
87 * @type string $proj The project identifier for ftok. This needs to be a one character string.
88 * **Defaults to** 'A'.
89 * @type string $semProj The project identifier for ftok to provide to `sem_get`. This needs to be a one
90 * character string.
91 * **Defaults to** 'B'.
92 * @type int $memsize The memory size in bytes for shm_attach. **Defaults to** 10000.
93 * @type int $perm The permission for shm_attach. **Defaults to** 0600.
94 * }
95 */
96 public function __construct($options = [])
97 {
98 if (!extension_loaded('sysvshm')) {
99 throw new \RuntimeException(
100 'sysvshm extension is required to use this ItemPool'
101 );
102 }
103 $this->options = $options + [
104 'variableKey' => self::VAR_KEY,
105 'proj' => self::DEFAULT_PROJ,
106 'semProj' => self::DEFAULT_SEM_PROJ,
107 'memsize' => self::DEFAULT_MEMSIZE,
108 'perm' => self::DEFAULT_PERM
109 ];
110 $this->items = [];
111 $this->deferredItems = [];
112 $this->sysvKey = ftok(__FILE__, $this->options['proj']);
113
114 // gracefully handle when `sysvsem` isn't loaded
115 // @TODO(v2): throw an exception when the extension isn't loaded
116 if (extension_loaded('sysvsem')) {
117 $semKey = ftok(__FILE__, $this->options['semProj']);
118 $this->semId = sem_get($semKey, 1, $this->options['perm'], true);
119 }
120 }
121
122 /**
123 * @param mixed $key
124 * @return CacheItemInterface
125 */
126 public function getItem($key): CacheItemInterface
127 {
128 $this->loadItems();
129 return current($this->getItems([$key])); // @phpstan-ignore-line
130 }
131
132 /**
133 * @param array<mixed> $keys
134 * @return iterable<CacheItemInterface>
135 */
136 public function getItems(array $keys = []): iterable
137 {
138 $this->loadItems();
139 $items = [];
140 foreach ($keys as $key) {
141 $items[$key] = $this->hasItem($key) ?
142 clone $this->items[$key] :
143 new TypedItem($key);
144 }
145 return $items;
146 }
147
148 /**
149 * {@inheritdoc}
150 */
151 public function hasItem($key): bool
152 {
153 $this->loadItems();
154 return isset($this->items[$key]) && $this->items[$key]->isHit();
155 }
156
157 /**
158 * {@inheritdoc}
159 */
160 public function clear(): bool
161 {
162 if (!$this->acquireLock()) {
163 return false;
164 }
165
166 $this->items = [];
167 $this->deferredItems = [];
168 $ret = $this->saveCurrentItems();
169
170 $this->resetShm();
171 $this->releaseLock();
172 return $ret;
173 }
174
175 /**
176 * {@inheritdoc}
177 */
178 public function deleteItem($key): bool
179 {
180 return $this->deleteItems([$key]);
181 }
182
183 /**
184 * {@inheritdoc}
185 */
186 public function deleteItems(array $keys): bool
187 {
188 if (!$this->acquireLock()) {
189 return false;
190 }
191
192 if (!$this->hasLoadedItems) {
193 $this->loadItems();
194 }
195
196 foreach ($keys as $key) {
197 unset($this->items[$key]);
198 }
199 $ret = $this->saveCurrentItems();
200
201 $this->resetShm();
202 $this->releaseLock();
203 return $ret;
204 }
205
206 /**
207 * {@inheritdoc}
208 */
209 public function save(CacheItemInterface $item): bool
210 {
211 if (!$this->acquireLock()) {
212 return false;
213 }
214
215 if (!$this->hasLoadedItems) {
216 $this->loadItems();
217 }
218
219 $this->items[$item->getKey()] = $item;
220 $ret = $this->saveCurrentItems();
221 $this->releaseLock();
222 return $ret;
223 }
224
225 /**
226 * {@inheritdoc}
227 */
228 public function saveDeferred(CacheItemInterface $item): bool
229 {
230 $this->deferredItems[$item->getKey()] = $item;
231 return true;
232 }
233
234 /**
235 * {@inheritdoc}
236 */
237 public function commit(): bool
238 {
239 if (!$this->acquireLock()) {
240 return false;
241 }
242
243 foreach ($this->deferredItems as $item) {
244 if ($this->save($item) === false) {
245 $this->releaseLock();
246 return false;
247 }
248 }
249 $this->deferredItems = [];
250 $this->releaseLock();
251 return true;
252 }
253
254 /**
255 * Save the current items.
256 *
257 * @return bool true when success, false upon failure
258 */
259 private function saveCurrentItems()
260 {
261 if (!$this->acquireLock()) {
262 return false;
263 }
264
265 if (false !== $shmid = $this->attachShm()) {
266 $success = shm_put_var(
267 $shmid,
268 $this->options['variableKey'],
269 $this->items
270 );
271 shm_detach($shmid);
272 $this->releaseLock();
273 return $success;
274 }
275 $this->releaseLock();
276 return false;
277 }
278
279 /**
280 * Load the items from the shared memory.
281 *
282 * @return bool true when success, false upon failure
283 */
284 private function loadItems()
285 {
286 if (!$this->acquireLock()) {
287 return false;
288 }
289
290 if (false !== $shmid = $this->attachShm()) {
291 $data = @shm_get_var($shmid, $this->options['variableKey']);
292 $this->items = $data ?: [];
293 shm_detach($shmid);
294 $this->hasLoadedItems = true;
295 $this->releaseLock();
296 return true;
297 }
298 $this->releaseLock();
299 return false;
300 }
301
302 private function acquireLock(): bool
303 {
304 if ($this->semId === false) {
305 // if `sysvsem` isn't loaded, or if `sem_get` fails, return true
306 // this ensures BC with previous versions of the auth library.
307 // @TODO consider better handling when `sem_get` fails.
308 return true;
309 }
310
311 $currentPid = getmypid();
312 if ($this->lockOwnerPid === $currentPid) {
313 // We already have the lock
314 return true;
315 }
316
317 if (sem_acquire($this->semId)) {
318 $this->lockOwnerPid = (int) $currentPid;
319 return true;
320 }
321 return false;
322 }
323
324 private function releaseLock(): bool
325 {
326 if ($this->semId === false || $this->lockOwnerPid !== getmypid()) {
327 return true;
328 }
329
330 $this->lockOwnerPid = null;
331 return sem_release($this->semId);
332 }
333
334 private function resetShm(): void
335 {
336 // Remove the shared memory segment and semaphore when clearing the cache
337 $shmid = @shm_attach($this->sysvKey);
338 if ($shmid !== false) {
339 @shm_remove($shmid);
340 @shm_detach($shmid);
341 }
342 }
343
344 private function attachShm(): SysvSharedMemory|false
345 {
346 return shm_attach(
347 $this->sysvKey,
348 $this->options['memsize'],
349 $this->options['perm']
350 );
351 }
352 }
353