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 ) {
++$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
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] ) ) {
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
// 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() );