Merge "Make DBAccessBase use DBConnRef, rename $wiki, and hide getLoadBalancer()"
[lhc/web/wiklou.git] / includes / libs / lockmanager / QuorumLockManager.php
index 950b283..6478a61 100644 (file)
@@ -38,7 +38,7 @@ abstract class QuorumLockManager extends LockManager {
        final protected function doLockByType( array $pathsByType ) {
                $status = StatusValue::newGood();
 
-               $pathsToLock = []; // (bucket => type => paths)
+               $pathsByTypeByBucket = []; // (bucket => type => paths)
                // Get locks that need to be acquired (buckets => locks)...
                foreach ( $pathsByType as $type => $paths ) {
                        foreach ( $paths as $path ) {
@@ -46,23 +46,27 @@ abstract class QuorumLockManager extends LockManager {
                                        ++$this->locksHeld[$path][$type];
                                } else {
                                        $bucket = $this->getBucketFromPath( $path );
-                                       $pathsToLock[$bucket][$type][] = $path;
+                                       $pathsByTypeByBucket[$bucket][$type][] = $path;
                                }
                        }
                }
 
+               // Acquire locks in each bucket in bucket order to reduce contention. Any blocking
+               // mutexes during the acquisition step will not involve circular waiting on buckets.
+               ksort( $pathsByTypeByBucket );
+
                $lockedPaths = []; // files locked in this attempt (type => paths)
                // Attempt to acquire these locks...
-               foreach ( $pathsToLock as $bucket => $pathsToLockByType ) {
+               foreach ( $pathsByTypeByBucket as $bucket => $bucketPathsByType ) {
                        // Try to acquire the locks for this bucket
-                       $status->merge( $this->doLockingRequestBucket( $bucket, $pathsToLockByType ) );
+                       $status->merge( $this->doLockingRequestBucket( $bucket, $bucketPathsByType ) );
                        if ( !$status->isOK() ) {
                                $status->merge( $this->doUnlockByType( $lockedPaths ) );
 
                                return $status;
                        }
                        // Record these locks as active
-                       foreach ( $pathsToLockByType as $type => $paths ) {
+                       foreach ( $bucketPathsByType as $type => $paths ) {
                                foreach ( $paths as $path ) {
                                        $this->locksHeld[$path][$type] = 1; // locked
                                        // Keep track of what locks were made in this attempt
@@ -77,7 +81,7 @@ abstract class QuorumLockManager extends LockManager {
        protected function doUnlockByType( array $pathsByType ) {
                $status = StatusValue::newGood();
 
-               $pathsToUnlock = []; // (bucket => type => paths)
+               $pathsByTypeByBucket = []; // (bucket => type => paths)
                foreach ( $pathsByType as $type => $paths ) {
                        foreach ( $paths as $path ) {
                                if ( !isset( $this->locksHeld[$path][$type] ) ) {
@@ -88,7 +92,7 @@ abstract class QuorumLockManager extends LockManager {
                                        if ( $this->locksHeld[$path][$type] <= 0 ) {
                                                unset( $this->locksHeld[$path][$type] );
                                                $bucket = $this->getBucketFromPath( $path );
-                                               $pathsToUnlock[$bucket][$type][] = $path;
+                                               $pathsByTypeByBucket[$bucket][$type][] = $path;
                                        }
                                        if ( $this->locksHeld[$path] === [] ) {
                                                unset( $this->locksHeld[$path] ); // no SH or EX locks left for key
@@ -99,8 +103,8 @@ abstract class QuorumLockManager extends LockManager {
 
                // Remove these specific locks if possible, or at least release
                // all locks once this process is currently not holding any locks.
-               foreach ( $pathsToUnlock as $bucket => $pathsToUnlockByType ) {
-                       $status->merge( $this->doUnlockingRequestBucket( $bucket, $pathsToUnlockByType ) );
+               foreach ( $pathsByTypeByBucket as $bucket => $bucketPathsByType ) {
+                       $status->merge( $this->doUnlockingRequestBucket( $bucket, $bucketPathsByType ) );
                }
                if ( $this->locksHeld === [] ) {
                        $status->merge( $this->releaseAllLocks() );