PluginProbe
Media Cloud Sync / 1.4.1
Media Cloud Sync v1.4.1
1.4.1 1.4.0 1.3.12 1.3.11 1.3.10 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.2.0 1.2.10 1.2.11 1.2.12 1.2.13 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.2.9 All 35 releases
← All changes | includes/sdk/google/google/auth/src/Cache/SysVCacheItemPool.php +122 -35 1.2.31.4.1 View file →
@@ -14,12 +14,14 @@
14 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 15 * See the License for the specific language governing permissions and
16 16 * limitations under the License.
17 17 */
18 -namespace Dudlewebs\WPMCS\Google\Auth\Cache;
18 +namespace Dudlewebs\WPMCS\GCP\Google\Auth\Cache;
19 19
20 -use Dudlewebs\WPMCS\Psr\Cache\CacheItemInterface;
21 -use Dudlewebs\WPMCS\Psr\Cache\CacheItemPoolInterface;
20 +use Dudlewebs\WPMCS\GCP\Psr\Cache\CacheItemInterface;
21 +use Dudlewebs\WPMCS\GCP\Psr\Cache\CacheItemPoolInterface;
22 +use SysvSemaphore;
23 +use SysvSharedMemory;
22 24 /**
23 25 * SystemV shared memory based CacheItemPool implementation.
24 26 *
25 27 * This CacheItemPool implementation can be used among multiple processes, but
@@ -29,8 +31,9 @@
29 31 class SysVCacheItemPool implements CacheItemPoolInterface
30 32 {
31 33 const VAR_KEY = 1;
32 34 const DEFAULT_PROJ = 'A';
35 + const DEFAULT_SEM_PROJ = 'B';
33 36 const DEFAULT_MEMSIZE = 10000;
34 37 const DEFAULT_PERM = 0600;
35 38 /**
36 39 * @var int
@@ -52,8 +55,18 @@
52 55 * @var bool
53 56 */
54 57 private $hasLoadedItems = \false;
55 58 /**
59 + * @var SysvSemaphore|false
60 + */
61 + private SysvSemaphore|false $semId = \false;
62 + /**
63 + * Maintain the process which is currently holding the semaphore to prevent deadlock.
64 + *
65 + * @var int|null
66 + */
67 + private ?int $lockOwnerPid = null;
68 + /**
56 69 * Create a SystemV shared memory based CacheItemPool.
57 70 *
58 71 * @param array<mixed> $options {
59 72 * [optional] Configuration options.
@@ -60,8 +73,11 @@
60 73 *
61 74 * @type int $variableKey The variable key for getting the data from the shared memory. **Defaults to** 1.
62 75 * @type string $proj The project identifier for ftok. This needs to be a one character string.
63 76 * **Defaults to** 'A'.
77 + * @type string $semProj The project identifier for ftok to provide to `sem_get`. This needs to be a one
78 + * character string.
79 + * **Defaults to** 'B'.
64 80 * @type int $memsize The memory size in bytes for shm_attach. **Defaults to** 10000.
65 81 * @type int $perm The permission for shm_attach. **Defaults to** 0600.
66 82 * }
67 83 */
@@ -66,24 +82,30 @@
66 82 * }
67 83 */
68 84 public function __construct($options = [])
69 85 {
70 - if (!extension_loaded('sysvshm')) {
86 + if (!\extension_loaded('sysvshm')) {
71 87 throw new \RuntimeException('sysvshm extension is required to use this ItemPool');
72 88 }
73 - $this->options = $options + ['variableKey' => self::VAR_KEY, 'proj' => self::DEFAULT_PROJ, 'memsize' => self::DEFAULT_MEMSIZE, 'perm' => self::DEFAULT_PERM];
89 + $this->options = $options + ['variableKey' => self::VAR_KEY, 'proj' => self::DEFAULT_PROJ, 'semProj' => self::DEFAULT_SEM_PROJ, 'memsize' => self::DEFAULT_MEMSIZE, 'perm' => self::DEFAULT_PERM];
74 90 $this->items = [];
75 91 $this->deferredItems = [];
76 - $this->sysvKey = ftok(__FILE__, $this->options['proj']);
92 + $this->sysvKey = \ftok(__FILE__, $this->options['proj']);
93 + // gracefully handle when `sysvsem` isn't loaded
94 + // @TODO(v2): throw an exception when the extension isn't loaded
95 + if (\extension_loaded('sysvsem')) {
96 + $semKey = \ftok(__FILE__, $this->options['semProj']);
97 + $this->semId = \sem_get($semKey, 1, $this->options['perm'], \true);
98 + }
77 99 }
78 100 /**
79 101 * @param mixed $key
80 102 * @return CacheItemInterface
81 103 */
82 - public function getItem($key): CacheItemInterface
104 + public function getItem($key) : CacheItemInterface
83 105 {
84 106 $this->loadItems();
85 - return current($this->getItems([$key]));
107 + return \current($this->getItems([$key]));
86 108 // @phpstan-ignore-line
87 109 }
88 110 /**
89 111 * @param array<mixed> $keys
@@ -88,15 +110,14 @@
88 110 /**
89 111 * @param array<mixed> $keys
90 112 * @return iterable<CacheItemInterface>
91 113 */
92 - public function getItems(array $keys = []): iterable
114 + public function getItems(array $keys = []) : iterable
93 115 {
94 116 $this->loadItems();
95 117 $items = [];
96 - $itemClass = \PHP_VERSION_ID >= 80000 ? TypedItem::class : Item::class;
97 118 foreach ($keys as $key) {
98 - $items[$key] = $this->hasItem($key) ? clone $this->items[$key] : new $itemClass($key);
119 + $items[$key] = $this->hasItem($key) ? clone $this->items[$key] : new TypedItem($key);
99 120 }
100 121 return $items;
101 122 }
102 123 /**
@@ -101,9 +122,9 @@
101 122 }
102 123 /**
103 124 * {@inheritdoc}
104 125 */
105 - public function hasItem($key): bool
126 + public function hasItem($key) : bool
106 127 {
107 128 $this->loadItems();
108 129 return isset($this->items[$key]) && $this->items[$key]->isHit();
109 130 }
@@ -109,18 +130,24 @@
109 130 }
110 131 /**
111 132 * {@inheritdoc}
112 133 */
113 - public function clear(): bool
134 + public function clear() : bool
114 135 {
136 + if (!$this->acquireLock()) {
137 + return \false;
138 + }
115 139 $this->items = [];
116 140 $this->deferredItems = [];
117 - return $this->saveCurrentItems();
141 + $ret = $this->saveCurrentItems();
142 + $this->resetShm();
143 + $this->releaseLock();
144 + return $ret;
118 145 }
119 146 /**
120 147 * {@inheritdoc}
121 148 */
122 - public function deleteItem($key): bool
149 + public function deleteItem($key) : bool
123 150 {
124 151 return $this->deleteItems([$key]);
125 152 }
126 153 /**
@@ -125,10 +152,13 @@
125 152 }
126 153 /**
127 154 * {@inheritdoc}
128 155 */
129 - public function deleteItems(array $keys): bool
156 + public function deleteItems(array $keys) : bool
130 157 {
158 + if (!$this->acquireLock()) {
159 + return \false;
160 + }
131 161 if (!$this->hasLoadedItems) {
132 162 $this->loadItems();
133 163 }
134 164 foreach ($keys as $key) {
@@ -133,25 +163,33 @@
133 163 }
134 164 foreach ($keys as $key) {
135 165 unset($this->items[$key]);
136 166 }
137 - return $this->saveCurrentItems();
167 + $ret = $this->saveCurrentItems();
168 + $this->resetShm();
169 + $this->releaseLock();
170 + return $ret;
138 171 }
139 172 /**
140 173 * {@inheritdoc}
141 174 */
142 - public function save(CacheItemInterface $item): bool
175 + public function save(CacheItemInterface $item) : bool
143 176 {
177 + if (!$this->acquireLock()) {
178 + return \false;
179 + }
144 180 if (!$this->hasLoadedItems) {
145 181 $this->loadItems();
146 182 }
147 183 $this->items[$item->getKey()] = $item;
148 - return $this->saveCurrentItems();
184 + $ret = $this->saveCurrentItems();
185 + $this->releaseLock();
186 + return $ret;
149 187 }
150 188 /**
151 189 * {@inheritdoc}
152 190 */
153 - public function saveDeferred(CacheItemInterface $item): bool
191 + public function saveDeferred(CacheItemInterface $item) : bool
154 192 {
155 193 $this->deferredItems[$item->getKey()] = $item;
156 194 return \true;
157 195 }
@@ -157,16 +195,21 @@
157 195 }
158 196 /**
159 197 * {@inheritdoc}
160 198 */
161 - public function commit(): bool
199 + public function commit() : bool
162 200 {
201 + if (!$this->acquireLock()) {
202 + return \false;
203 + }
163 204 foreach ($this->deferredItems as $item) {
164 205 if ($this->save($item) === \false) {
206 + $this->releaseLock();
165 207 return \false;
166 208 }
167 209 }
168 210 $this->deferredItems = [];
211 + $this->releaseLock();
169 212 return \true;
170 213 }
171 214 /**
172 215 * Save the current items.
@@ -174,14 +217,18 @@
174 217 * @return bool true when success, false upon failure
175 218 */
176 219 private function saveCurrentItems()
177 220 {
178 - $shmid = shm_attach($this->sysvKey, $this->options['memsize'], $this->options['perm']);
179 - if ($shmid !== \false) {
180 - $ret = shm_put_var($shmid, $this->options['variableKey'], $this->items);
181 - shm_detach($shmid);
182 - return $ret;
221 + if (!$this->acquireLock()) {
222 + return \false;
183 223 }
224 + if (\false !== ($shmid = $this->attachShm())) {
225 + $success = \shm_put_var($shmid, $this->options['variableKey'], $this->items);
226 + \shm_detach($shmid);
227 + $this->releaseLock();
228 + return $success;
229 + }
230 + $this->releaseLock();
184 231 return \false;
185 232 }
186 233 /**
187 234 * Load the items from the shared memory.
@@ -189,19 +236,59 @@
189 236 * @return bool true when success, false upon failure
190 237 */
191 238 private function loadItems()
192 239 {
193 - $shmid = shm_attach($this->sysvKey, $this->options['memsize'], $this->options['perm']);
194 - if ($shmid !== \false) {
195 - $data = @shm_get_var($shmid, $this->options['variableKey']);
196 - if (!empty($data)) {
197 - $this->items = $data;
198 - } else {
199 - $this->items = [];
200 - }
201 - shm_detach($shmid);
240 + if (!$this->acquireLock()) {
241 + return \false;
242 + }
243 + if (\false !== ($shmid = $this->attachShm())) {
244 + $data = @\shm_get_var($shmid, $this->options['variableKey']);
245 + $this->items = $data ?: [];
246 + \shm_detach($shmid);
202 247 $this->hasLoadedItems = \true;
248 + $this->releaseLock();
203 249 return \true;
204 250 }
251 + $this->releaseLock();
205 252 return \false;
253 + }
254 + private function acquireLock() : bool
255 + {
256 + if ($this->semId === \false) {
257 + // if `sysvsem` isn't loaded, or if `sem_get` fails, return true
258 + // this ensures BC with previous versions of the auth library.
259 + // @TODO consider better handling when `sem_get` fails.
260 + return \true;
261 + }
262 + $currentPid = \getmypid();
263 + if ($this->lockOwnerPid === $currentPid) {
264 + // We already have the lock
265 + return \true;
266 + }
267 + if (\sem_acquire($this->semId)) {
268 + $this->lockOwnerPid = (int) $currentPid;
269 + return \true;
270 + }
271 + return \false;
272 + }
273 + private function releaseLock() : bool
274 + {
275 + if ($this->semId === \false || $this->lockOwnerPid !== \getmypid()) {
276 + return \true;
277 + }
278 + $this->lockOwnerPid = null;
279 + return \sem_release($this->semId);
280 + }
281 + private function resetShm() : void
282 + {
283 + // Remove the shared memory segment and semaphore when clearing the cache
284 + $shmid = @\shm_attach($this->sysvKey);
285 + if ($shmid !== \false) {
286 + @\shm_remove($shmid);
287 + @\shm_detach($shmid);
288 + }
289 + }
290 + private function attachShm() : SysvSharedMemory|false
291 + {
292 + return \shm_attach($this->sysvKey, $this->options['memsize'], $this->options['perm']);
206 293 }
207 294 }