4 * Simple optimistic locks for Drydock resources and leases.
6 * Most blueprints only need very simple locks: for example, a host blueprint
7 * might not want to create multiple resources representing the same physical
8 * machine. These optimistic "slot locks" provide a flexible way to do this
9 * sort of simple locking.
11 * @task info Getting Lock Information
12 * @task lock Acquiring and Releasing Locks
14 final class DrydockSlotLock
extends DrydockDAO
{
20 protected function getConfiguration() {
22 self
::CONFIG_TIMESTAMPS
=> false,
23 self
::CONFIG_COLUMN_SCHEMA
=> array(
24 'lockIndex' => 'bytes12',
27 self
::CONFIG_KEY_SCHEMA
=> array(
29 'columns' => array('lockIndex'),
33 'columns' => array('ownerPHID'),
36 ) + parent
::getConfiguration();
40 /* -( Getting Lock Information )------------------------------------------- */
44 * Load all locks held by a particular owner.
46 * @param phid Owner PHID.
47 * @return list<DrydockSlotLock> All held locks.
50 public static function loadLocks($owner_phid) {
51 return id(new DrydockSlotLock())->loadAllWhere(
58 * Test if a lock is currently free.
60 * @param string Lock key to test.
61 * @return bool True if the lock is currently free.
64 public static function isLockFree($lock) {
65 return self
::areLocksFree(array($lock));
70 * Test if a list of locks are all currently free.
72 * @param list<string> List of lock keys to test.
73 * @return bool True if all locks are currently free.
76 public static function areLocksFree(array $locks) {
77 $lock_map = self
::loadHeldLocks($locks);
85 * @param list<string> List of lock keys to load.
86 * @return list<DrydockSlotLock> List of held locks.
89 public static function loadHeldLocks(array $locks) {
94 $table = new DrydockSlotLock();
95 $conn_r = $table->establishConnection('r');
98 foreach ($locks as $lock) {
99 $indexes[] = PhabricatorHash
::digestForIndex($lock);
102 return id(new DrydockSlotLock())->loadAllWhere(
103 'lockIndex IN (%Ls)',
108 /* -( Acquiring and Releasing Locks )-------------------------------------- */
112 * Acquire a set of slot locks.
114 * This method either acquires all the locks or throws an exception (usually
115 * because one or more locks are held).
117 * @param phid Lock owner PHID.
118 * @param list<string> List of locks to acquire.
122 public static function acquireLocks($owner_phid, array $locks) {
127 $table = new DrydockSlotLock();
128 $conn_w = $table->establishConnection('w');
131 foreach ($locks as $lock) {
136 PhabricatorHash
::digestForIndex($lock),
143 'INSERT INTO %T (ownerPHID, lockIndex, lockKey) VALUES %LQ',
144 $table->getTableName(),
146 } catch (AphrontDuplicateKeyQueryException
$ex) {
147 // Try to improve the readability of the exception. We might miss on
148 // this query if the lock has already been released, but most of the
149 // time we should be able to figure out which locks are already held.
150 $held = self
::loadHeldLocks($locks);
151 $held = mpull($held, 'getOwnerPHID', 'getLockKey');
153 throw new DrydockSlotLockException($held);
159 * Release all locks held by an owner.
161 * @param phid Lock owner PHID.
165 public static function releaseLocks($owner_phid) {
166 $table = new DrydockSlotLock();
167 $conn_w = $table->establishConnection('w');
171 'DELETE FROM %T WHERE ownerPHID = %s',
172 $table->getTableName(),