lock(); * do_contentious_things(); * $lock->unlock(); * * @task construct Constructing Locks * @task impl Implementation */ final class PhabricatorGlobalLock extends PhutilLock { private $lockname; private $conn; /* -( Constructing Locks )------------------------------------------------- */ public static function newLock($name) { $full_name = 'global:'.$name; $lock = self::getLock($full_name); if (!$lock) { $lock = new PhabricatorGlobalLock($full_name); $lock->lockname = $name; self::registerLock($lock); } return $lock; } /* -( Implementation )----------------------------------------------------- */ protected function doLock() { $conn = $this->conn; if (!$conn) { // NOTE: Using the 'repository' database somewhat arbitrarily, mostly // because the first client of locks is the repository daemons. We must // always use the same database for all locks, but don't access any // tables so we could use any valid database. We could build a // database-free connection instead, but that's kind of messy and we // might forget about it in the future if we vertically partition the // application. $dao = new PhabricatorRepository(); // NOTE: Using "force_new" to make sure each lock is on its own // connection. $conn = $dao->establishConnection('w', $force_new = true); // NOTE: Since MySQL will disconnect us if we're idle for too long, we set // the wait_timeout to an enormous value, to allow us to hold the // connection open indefinitely (or, at least, for a year). queryfx($conn, 'SET wait_timeout = %d', 365 * 24 * 60 * 60); } $result = queryfx_one( $conn, 'SELECT GET_LOCK(%s, %d)', 'phabricator:'.$this->lockname, 0); $ok = head($result); if (!$ok) { throw new PhutilLockException($this->getName()); } $this->conn = $conn; } protected function doUnlock() { queryfx( $this->conn, 'SELECT RELEASE_LOCK(%s)', 'phabricator:'.$this->lockname); // TODO: There's no explicit close() method on connections right now. Once // we have one, we could close the connection here. Since we don't have // such a method, we need to keep the connection around in case lock() is // called again, so that long-running daemons don't gradually open // an unbounded number of connections. } }