[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Global, MySQL-backed lock. This is a high-reliability, low-performance 5 * global lock. 6 * 7 * The lock is maintained by using GET_LOCK() in MySQL, and automatically 8 * released when the connection terminates. Thus, this lock can safely be used 9 * to control access to shared resources without implementing any sort of 10 * timeout or override logic: the lock can't normally be stuck in a locked state 11 * with no process actually holding the lock. 12 * 13 * However, acquiring the lock is moderately expensive (several network 14 * roundtrips). This makes it unsuitable for tasks where lock performance is 15 * important. 16 * 17 * $lock = PhabricatorGlobalLock::newLock('example'); 18 * $lock->lock(); 19 * do_contentious_things(); 20 * $lock->unlock(); 21 * 22 * NOTE: This lock is not completely global; it is namespaced to the active 23 * storage namespace so that unit tests running in separate table namespaces 24 * are isolated from one another. 25 * 26 * @task construct Constructing Locks 27 * @task impl Implementation 28 */ 29 final class PhabricatorGlobalLock extends PhutilLock { 30 31 private $lockname; 32 private $conn; 33 34 private static $pool = array(); 35 36 37 /* -( Constructing Locks )------------------------------------------------- */ 38 39 40 public static function newLock($name) { 41 $namespace = PhabricatorLiskDAO::getStorageNamespace(); 42 $full_name = 'global:'.$namespace.':'.$name; 43 44 $lock = self::getLock($full_name); 45 if (!$lock) { 46 $lock = new PhabricatorGlobalLock($full_name); 47 $lock->lockname = $name; 48 self::registerLock($lock); 49 } 50 51 return $lock; 52 } 53 54 55 /* -( Implementation )----------------------------------------------------- */ 56 57 protected function doLock($wait) { 58 $conn = $this->conn; 59 60 if (!$conn) { 61 // Try to reuse a connection from the connection pool. 62 $conn = array_pop(self::$pool); 63 } 64 65 if (!$conn) { 66 // NOTE: Using the 'repository' database somewhat arbitrarily, mostly 67 // because the first client of locks is the repository daemons. We must 68 // always use the same database for all locks, but don't access any 69 // tables so we could use any valid database. We could build a 70 // database-free connection instead, but that's kind of messy and we 71 // might forget about it in the future if we vertically partition the 72 // application. 73 $dao = new PhabricatorRepository(); 74 75 // NOTE: Using "force_new" to make sure each lock is on its own 76 // connection. 77 $conn = $dao->establishConnection('w', $force_new = true); 78 79 // NOTE: Since MySQL will disconnect us if we're idle for too long, we set 80 // the wait_timeout to an enormous value, to allow us to hold the 81 // connection open indefinitely (or, at least, for 24 days). 82 $max_allowed_timeout = 2147483; 83 queryfx($conn, 'SET wait_timeout = %d', $max_allowed_timeout); 84 } 85 86 $result = queryfx_one( 87 $conn, 88 'SELECT GET_LOCK(%s, %f)', 89 'phabricator:'.$this->lockname, 90 $wait); 91 92 $ok = head($result); 93 if (!$ok) { 94 throw new PhutilLockException($this->getName()); 95 } 96 97 $this->conn = $conn; 98 } 99 100 protected function doUnlock() { 101 queryfx( 102 $this->conn, 103 'SELECT RELEASE_LOCK(%s)', 104 'phabricator:'.$this->lockname); 105 106 $this->conn->close(); 107 self::$pool[] = $this->conn; 108 $this->conn = null; 109 } 110 111 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |