false, self::CONFIG_COLUMN_SCHEMA => array( 'lockIndex' => 'bytes12', 'lockKey' => 'text', ), self::CONFIG_KEY_SCHEMA => array( 'key_lock' => array( 'columns' => array('lockIndex'), 'unique' => true, ), 'key_owner' => array( 'columns' => array('ownerPHID'), ), ), ) + parent::getConfiguration(); } /* -( Getting Lock Information )------------------------------------------- */ /** * Load all locks held by a particular owner. * * @param phid Owner PHID. * @return list All held locks. * @task info */ public static function loadLocks($owner_phid) { return id(new DrydockSlotLock())->loadAllWhere( 'ownerPHID = %s', $owner_phid); } /** * Test if a lock is currently free. * * @param string Lock key to test. * @return bool True if the lock is currently free. * @task info */ public static function isLockFree($lock) { return self::areLocksFree(array($lock)); } /** * Test if a list of locks are all currently free. * * @param list List of lock keys to test. * @return bool True if all locks are currently free. * @task info */ public static function areLocksFree(array $locks) { $lock_map = self::loadHeldLocks($locks); return !$lock_map; } /** * Load named locks. * * @param list List of lock keys to load. * @return list List of held locks. * @task info */ public static function loadHeldLocks(array $locks) { if (!$locks) { return array(); } $table = new DrydockSlotLock(); $conn_r = $table->establishConnection('r'); $indexes = array(); foreach ($locks as $lock) { $indexes[] = PhabricatorHash::digestForIndex($lock); } return id(new DrydockSlotLock())->loadAllWhere( 'lockIndex IN (%Ls)', $indexes); } /* -( Acquiring and Releasing Locks )-------------------------------------- */ /** * Acquire a set of slot locks. * * This method either acquires all the locks or throws an exception (usually * because one or more locks are held). * * @param phid Lock owner PHID. * @param list List of locks to acquire. * @return void * @task locks */ public static function acquireLocks($owner_phid, array $locks) { if (!$locks) { return; } $table = new DrydockSlotLock(); $conn_w = $table->establishConnection('w'); $sql = array(); foreach ($locks as $lock) { $sql[] = qsprintf( $conn_w, '(%s, %s, %s)', $owner_phid, PhabricatorHash::digestForIndex($lock), $lock); } try { queryfx( $conn_w, 'INSERT INTO %T (ownerPHID, lockIndex, lockKey) VALUES %Q', $table->getTableName(), implode(', ', $sql)); } catch (AphrontDuplicateKeyQueryException $ex) { // Try to improve the readability of the exception. We might miss on // this query if the lock has already been released, but most of the // time we should be able to figure out which locks are already held. $held = self::loadHeldLocks($locks); $held = mpull($held, 'getOwnerPHID', 'getLockKey'); throw new DrydockSlotLockException($held); } } /** * Release all locks held by an owner. * * @param phid Lock owner PHID. * @return void * @task locks */ public static function releaseLocks($owner_phid) { $table = new DrydockSlotLock(); $conn_w = $table->establishConnection('w'); queryfx( $conn_w, 'DELETE FROM %T WHERE ownerPHID = %s', $table->getTableName(), $owner_phid); } }