PluginProbe
Gmail SMTP / trunk
Gmail SMTP vtrunk
1.2.3.21 1.2.3.20 trunk 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9 1.1.0 1.1.1 1.1.2 1.1.9 1.2.0 1.2.3.14 1.2.3.15 1.2.3.16 1.2.3.18 1.2.3.5
gmail-smtp / google-api-php-client / vendor / google / auth / src / Cache / SysVCacheItemPool.php

SysVCacheItemPool.php in Gmail SMTP trunk, at google-api-php-client/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 = 100000;
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