public function lock($lockName, $method, $timeout = 5) { // http://www.postgresql.org/docs/8.2/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS $key = $this->addQuotes($this->bigintFromLockName($lockName)); $loop = new WaitConditionLoop(function () use($lockName, $key, $timeout, $method) { $res = $this->query("SELECT pg_try_advisory_lock({$key}) AS lockstatus", $method); $row = $this->fetchObject($res); if ($row->lockstatus === 't') { parent::lock($lockName, $method, $timeout); // record return true; } return WaitConditionLoop::CONDITION_CONTINUE; }, $timeout); return $loop->invoke() === $loop::CONDITION_REACHED; }
/** * Acquire an advisory lock on a key string * * Note that if reentry is enabled, duplicate calls ignore $expiry * * @param string $key * @param int $timeout Lock wait timeout; 0 for non-blocking [optional] * @param int $expiry Lock expiry [optional]; 1 day maximum * @param string $rclass Allow reentry if set and the current lock used this value * @return bool Success */ public function lock($key, $timeout = 6, $expiry = 6, $rclass = '') { // Avoid deadlocks and allow lock reentry if specified if (isset($this->locks[$key])) { if ($rclass != '' && $this->locks[$key]['class'] === $rclass) { ++$this->locks[$key]['depth']; return true; } else { return false; } } $expiry = min($expiry ?: INF, self::TTL_DAY); $loop = new WaitConditionLoop(function () use($key, $timeout, $expiry) { $this->clearLastError(); if ($this->add("{$key}:lock", 1, $expiry)) { return true; // locked! } elseif ($this->getLastError()) { return WaitConditionLoop::CONDITION_ABORTED; // network partition? } return WaitConditionLoop::CONDITION_CONTINUE; }, $timeout); $locked = $loop->invoke() === $loop::CONDITION_REACHED; if ($locked) { $this->locks[$key] = ['class' => $rclass, 'depth' => 1]; } return $locked; }
/** * @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; }
/** * Load in previous master positions for the client */ protected function initPositions() { if ($this->initialized) { return; } $this->initialized = true; if ($this->wait) { // If there is an expectation to see master positions with a certain min // timestamp, then block until they appear, or until a timeout is reached. if ($this->waitForPosTime > 0.0) { $data = null; $loop = new WaitConditionLoop(function () use(&$data) { $data = $this->store->get($this->key); return self::minPosTime($data) >= $this->waitForPosTime ? WaitConditionLoop::CONDITION_REACHED : WaitConditionLoop::CONDITION_CONTINUE; }, $this->waitForPosTimeout); $result = $loop->invoke(); $waitedMs = $loop->getLastWaitTime() * 1000.0; if ($result == $loop::CONDITION_REACHED) { $msg = "expected and found pos time {$this->waitForPosTime} ({$waitedMs}ms)"; $this->logger->debug($msg); } else { $msg = "expected but missed pos time {$this->waitForPosTime} ({$waitedMs}ms)"; $this->logger->info($msg); } } else { $data = $this->store->get($this->key); } $this->startupPositions = $data ? $data['positions'] : []; $this->logger->info(__METHOD__ . ": key is {$this->key} (read)\n"); } else { $this->startupPositions = []; $this->logger->info(__METHOD__ . ": key is {$this->key} (unread)\n"); } }
/** * Lock the resources at the given abstract paths * * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths * @param int $timeout Timeout in seconds (0 means non-blocking) (since 1.21) * @return StatusValue * @since 1.22 */ public final function lockByType(array $pathsByType, $timeout = 0) { $pathsByType = $this->normalizePathsByType($pathsByType); $status = null; $loop = new WaitConditionLoop(function () use(&$status, $pathsByType) { $status = $this->doLockByType($pathsByType); return $status->isOK() ?: WaitConditionLoop::CONDITION_CONTINUE; }, $timeout); $loop->invoke(); return $status; }