public function add($key, $value, $exptime = 0) { $this->debugLog("add({$key})"); return $this->checkResult($key, parent::add($key, $value, $exptime)); }
/** * @param MemcachedBagOStuff $memc * @param array $keys List of keys to acquire * @return bool */ protected function acquireMutexes(MemcachedBagOStuff $memc, array $keys) { $lockedKeys = []; // Acquire the keys in lexicographical order, to avoid deadlock problems. // If P1 is waiting to acquire a key P2 has, P2 can't also be waiting for a key P1 has. sort($keys); // Try to quickly loop to acquire the keys, but back off after a few rounds. // This reduces memcached spam, especially in the rare case where a server acquires // some lock keys and dies without releasing them. Lock keys expire after a few minutes. $rounds = 0; $start = microtime(true); do { if (++$rounds % 4 == 0) { usleep(1000 * 50); // 50 ms } foreach (array_diff($keys, $lockedKeys) as $key) { if ($memc->add("{$key}:mutex", 1, 180)) { // lock record $lockedKeys[] = $key; } else { continue; // acquire in order } } } while (count($lockedKeys) < count($keys) && microtime(true) - $start <= 3); if (count($lockedKeys) != count($keys)) { $this->releaseMutexes($memc, $lockedKeys); // failed; release what was locked return false; } return true; }
/** * @param MemcachedBagOStuff $memc * @param array $keys List of keys to acquire * @return bool */ protected function acquireMutexes(MemcachedBagOStuff $memc, array $keys) { $lockedKeys = []; // Acquire the keys in lexicographical order, to avoid deadlock problems. // If P1 is waiting to acquire a key P2 has, P2 can't also be waiting for a key P1 has. sort($keys); // Try to quickly loop to acquire the keys, but back off after a few rounds. // This reduces memcached spam, especially in the rare case where a server acquires // some lock keys and dies without releasing them. Lock keys expire after a few minutes. $loop = new WaitConditionLoop(function () use($memc, $keys, &$lockedKeys) { foreach (array_diff($keys, $lockedKeys) as $key) { if ($memc->add("{$key}:mutex", 1, 180)) { // lock record $lockedKeys[] = $key; } } return array_diff($keys, $lockedKeys) ? WaitConditionLoop::CONDITION_CONTINUE : true; }, 3.0); $loop->invoke(); if (count($lockedKeys) != count($keys)) { $this->releaseMutexes($memc, $lockedKeys); // failed; release what was locked return false; } return true; }