summaryrefslogtreecommitdiff
path: root/includes/filebackend/lockmanager/LockManager.php
diff options
context:
space:
mode:
Diffstat (limited to 'includes/filebackend/lockmanager/LockManager.php')
-rw-r--r--includes/filebackend/lockmanager/LockManager.php125
1 files changed, 102 insertions, 23 deletions
diff --git a/includes/filebackend/lockmanager/LockManager.php b/includes/filebackend/lockmanager/LockManager.php
index 0512a01b..dad8a624 100644
--- a/includes/filebackend/lockmanager/LockManager.php
+++ b/includes/filebackend/lockmanager/LockManager.php
@@ -56,7 +56,7 @@ abstract class LockManager {
protected $domain; // string; domain (usually wiki ID)
protected $lockTTL; // integer; maximum time locks can be held
- /* Lock types; stronger locks have higher values */
+ /** Lock types; stronger locks have higher values */
const LOCK_SH = 1; // shared lock (for reads)
const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
const LOCK_EX = 3; // exclusive lock (for writes)
@@ -76,10 +76,10 @@ abstract class LockManager {
if ( isset( $config['lockTTL'] ) ) {
$this->lockTTL = max( 1, $config['lockTTL'] );
} elseif ( PHP_SAPI === 'cli' ) {
- $this->lockTTL = 2*3600;
+ $this->lockTTL = 2 * 3600;
} else {
$met = ini_get( 'max_execution_time' ); // this is 0 in CLI mode
- $this->lockTTL = max( 5*60, 2*(int)$met );
+ $this->lockTTL = max( 5 * 60, 2 * (int)$met );
}
}
@@ -88,11 +88,36 @@ abstract class LockManager {
*
* @param array $paths List of resource names
* @param $type integer LockManager::LOCK_* constant
+ * @param integer $timeout Timeout in seconds (0 means non-blocking) (since 1.21)
* @return Status
*/
- final public function lock( array $paths, $type = self::LOCK_EX ) {
+ final public function lock( array $paths, $type = self::LOCK_EX, $timeout = 0 ) {
+ return $this->lockByType( array( $type => $paths ), $timeout );
+ }
+
+ /**
+ * Lock the resources at the given abstract paths
+ *
+ * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
+ * @param integer $timeout Timeout in seconds (0 means non-blocking) (since 1.21)
+ * @return Status
+ * @since 1.22
+ */
+ final public function lockByType( array $pathsByType, $timeout = 0 ) {
wfProfileIn( __METHOD__ );
- $status = $this->doLock( array_unique( $paths ), $this->lockTypeMap[$type] );
+ $status = Status::newGood();
+ $pathsByType = $this->normalizePathsByType( $pathsByType );
+ $msleep = array( 0, 50, 100, 300, 500 ); // retry backoff times
+ $start = microtime( true );
+ do {
+ $status = $this->doLockByType( $pathsByType );
+ $elapsed = microtime( true ) - $start;
+ if ( $status->isOK() || $elapsed >= $timeout || $elapsed < 0 ) {
+ break; // success, timeout, or clock set back
+ }
+ usleep( 1e3 * ( next( $msleep ) ?: 1000 ) ); // use 1 sec after enough times
+ $elapsed = microtime( true ) - $start;
+ } while ( $elapsed < $timeout && $elapsed >= 0 );
wfProfileOut( __METHOD__ );
return $status;
}
@@ -100,13 +125,25 @@ abstract class LockManager {
/**
* Unlock the resources at the given abstract paths
*
- * @param array $paths List of storage paths
+ * @param array $paths List of paths
* @param $type integer LockManager::LOCK_* constant
* @return Status
*/
final public function unlock( array $paths, $type = self::LOCK_EX ) {
+ return $this->unlockByType( array( $type => $paths ) );
+ }
+
+ /**
+ * Unlock the resources at the given abstract paths
+ *
+ * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
+ * @return Status
+ * @since 1.22
+ */
+ final public function unlockByType( array $pathsByType ) {
wfProfileIn( __METHOD__ );
- $status = $this->doUnlock( array_unique( $paths ), $this->lockTypeMap[$type] );
+ $pathsByType = $this->normalizePathsByType( $pathsByType );
+ $status = $this->doUnlockByType( $pathsByType );
wfProfileOut( __METHOD__ );
return $status;
}
@@ -136,20 +173,74 @@ abstract class LockManager {
}
/**
+ * Normalize the $paths array by converting LOCK_UW locks into the
+ * appropriate type and removing any duplicated paths for each lock type.
+ *
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of paths
+ * @return Array
+ * @since 1.22
+ */
+ final protected function normalizePathsByType( array $pathsByType ) {
+ $res = array();
+ foreach ( $pathsByType as $type => $paths ) {
+ $res[$this->lockTypeMap[$type]] = array_unique( $paths );
+ }
+ return $res;
+ }
+
+ /**
+ * @see LockManager::lockByType()
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of paths
+ * @return Status
+ * @since 1.22
+ */
+ protected function doLockByType( array $pathsByType ) {
+ $status = Status::newGood();
+ $lockedByType = array(); // map of (type => paths)
+ foreach ( $pathsByType as $type => $paths ) {
+ $status->merge( $this->doLock( $paths, $type ) );
+ if ( $status->isOK() ) {
+ $lockedByType[$type] = $paths;
+ } else {
+ // Release the subset of locks that were acquired
+ foreach ( $lockedByType as $type => $paths ) {
+ $status->merge( $this->doUnlock( $paths, $type ) );
+ }
+ break;
+ }
+ }
+ return $status;
+ }
+
+ /**
* Lock resources with the given keys and lock type
*
- * @param array $paths List of storage paths
+ * @param array $paths List of paths
* @param $type integer LockManager::LOCK_* constant
- * @return string
+ * @return Status
*/
abstract protected function doLock( array $paths, $type );
/**
+ * @see LockManager::unlockByType()
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of paths
+ * @return Status
+ * @since 1.22
+ */
+ protected function doUnlockByType( array $pathsByType ) {
+ $status = Status::newGood();
+ foreach ( $pathsByType as $type => $paths ) {
+ $status->merge( $this->doUnlock( $paths, $type ) );
+ }
+ return $status;
+ }
+
+ /**
* Unlock resources with the given keys and lock type
*
- * @param array $paths List of storage paths
+ * @param array $paths List of paths
* @param $type integer LockManager::LOCK_* constant
- * @return string
+ * @return Status
*/
abstract protected function doUnlock( array $paths, $type );
}
@@ -159,22 +250,10 @@ abstract class LockManager {
* @since 1.19
*/
class NullLockManager extends LockManager {
- /**
- * @see LockManager::doLock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
protected function doLock( array $paths, $type ) {
return Status::newGood();
}
- /**
- * @see LockManager::doUnlock()
- * @param $paths array
- * @param $type int
- * @return Status
- */
protected function doUnlock( array $paths, $type ) {
return Status::newGood();
}