Aaron Schulz has uploaded a new change for review.
https://gerrit.wikimedia.org/r/60097
Change subject: [LockManager] Made it easier to get both SH and EX locks at
once.
......................................................................
[LockManager] Made it easier to get both SH and EX locks at once.
* This also makes it possible for subclasses to optimize this case.
* Added a timeout parameter to ScopedLock::factory().
* Cleaned up a few bits of documentation.
Change-Id: Id3e9cf01f25ab498ea00d87ffb6d00aa8b05052b
---
M includes/filebackend/lockmanager/LockManager.php
M includes/filebackend/lockmanager/ScopedLock.php
2 files changed, 106 insertions(+), 24 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core
refs/changes/97/60097/1
diff --git a/includes/filebackend/lockmanager/LockManager.php
b/includes/filebackend/lockmanager/LockManager.php
index 9c778c7..53d8a70 100644
--- a/includes/filebackend/lockmanager/LockManager.php
+++ b/includes/filebackend/lockmanager/LockManager.php
@@ -56,7 +56,7 @@
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)
@@ -92,11 +92,25 @@
* @return Status
*/
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 $paths Map of LockManager::LOCK_* constants to lists of
storage 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 = Status::newGood();
+ $pathsByType = $this->normalizePathsByType( $pathsByType );
$msleep = array( 0, 50, 100, 300, 500 ); // retry backoff times
$start = microtime( true );
do {
- $status = $this->doLock( array_unique( $paths ),
$this->lockTypeMap[$type] );
+ $status = $this->doLockByType( $pathsByType );
$elapsed = microtime( true ) - $start;
if ( $status->isOK() || $elapsed >= $timeout ||
$elapsed < 0 ) {
break; // success, timeout, or clock set back
@@ -116,8 +130,20 @@
* @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 $paths Map of LockManager::LOCK_* constants to lists of
storage 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;
}
@@ -147,6 +173,46 @@
}
/**
+ * Normalize the $paths array by converting LOCK_UW locks into the
+ * appropriate type and removing an duplicated paths for each lock type.
+ *
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of
storage 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
storage 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
@@ -156,6 +222,20 @@
abstract protected function doLock( array $paths, $type );
/**
+ * @see LockManager::unlockByType()
+ * @param array $paths Map of LockManager::LOCK_* constants to lists of
storage 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
diff --git a/includes/filebackend/lockmanager/ScopedLock.php
b/includes/filebackend/lockmanager/ScopedLock.php
index edcb1d6..5faad4a 100644
--- a/includes/filebackend/lockmanager/ScopedLock.php
+++ b/includes/filebackend/lockmanager/ScopedLock.php
@@ -36,24 +36,18 @@
protected $manager;
/** @var Status */
protected $status;
- /** @var Array List of resource paths*/
- protected $paths;
-
- protected $type; // integer lock type
+ /** @var Array Map of lock types to resource paths */
+ protected $pathsByType;
/**
- * @param $manager LockManager
- * @param array $paths List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @param $status Status
+ * @param LockManager $manager
+ * @param array $pathsByType Map of lock types to path lists
+ * @param Status $status
*/
- protected function __construct(
- LockManager $manager, array $paths, $type, Status $status
- ) {
+ protected function __construct( LockManager $manager, array
$pathsByType, Status $status ) {
$this->manager = $manager;
- $this->paths = $paths;
+ $this->pathsByType = $pathsByType;
$this->status = $status;
- $this->type = $type;
}
/**
@@ -61,19 +55,24 @@
* Any locks are released once this object goes out of scope.
* The status object is updated with any errors or warnings.
*
- * @param $manager LockManager
- * @param array $paths List of storage paths
- * @param $type integer LockManager::LOCK_* constant
- * @param $status Status
+ * $type can be "mixed" and $paths can be a map of types to paths
(since 1.22).
+ * Otherwise $type should be an integer and $paths should be a list of
paths.
+ *
+ * @param LockManager $manager
+ * @param array $paths List of storage paths or map of lock types to
path lists
+ * @param integer|string $type LockManager::LOCK_* constant or "mixed"
+ * @param Status $status
+ * @param integer $timeout Timeout in seconds (0 means non-blocking)
(since 1.22)
* @return ScopedLock|null Returns null on failure
*/
public static function factory(
- LockManager $manager, array $paths, $type, Status $status
+ LockManager $manager, array $paths, $type, Status $status,
$timeout = 0
) {
- $lockStatus = $manager->lock( $paths, $type );
+ $pathsByType = is_integer( $type ) ? array( $type => $paths ) :
$paths;
+ $lockStatus = $manager->lockByType( $pathsByType, $timeout );
$status->merge( $lockStatus );
if ( $lockStatus->isOK() ) {
- return new self( $manager, $paths, $type, $status );
+ return new self( $manager, $pathsByType, $status );
}
return null;
}
@@ -91,9 +90,12 @@
$lock = null;
}
+ /**
+ * Release the locks when this goes out of scope
+ */
function __destruct() {
$wasOk = $this->status->isOK();
- $this->status->merge( $this->manager->unlock( $this->paths,
$this->type ) );
+ $this->status->merge( $this->manager->unlockByType(
$this->pathsByType ) );
if ( $wasOk ) {
// Make sure status is OK, despite any unlockFiles()
fatals
$this->status->setResult( true, $this->status->value );
--
To view, visit https://gerrit.wikimedia.org/r/60097
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Id3e9cf01f25ab498ea00d87ffb6d00aa8b05052b
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Aaron Schulz <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits