* - 'name' : A unique name for the lock manager
* - 'class' : The lock manger class to use
* Additional parameters are specific to the class used.
+ * These settings should be global to all wikis.
*/
$wgLockManagers = array();
// Don't share DB, storage, or memcached connections
wfGetLBFactory()->destroyInstance();
FileBackendGroup::destroySingleton();
- LockManagerGroup::destroySingleton();
+ LockManagerGroup::destroySingletons();
ObjectCache::clear();
$wgMemc = null;
}
if ( $type == self::LOCK_EX ) { // writer locks
try {
- $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
+ $keys = array_unique( array_map( array( $this, 'sha1Base36Absolute' ), $paths ) );
# Build up values for INSERT clause
$data = array();
foreach ( $keys as $key ) {
$status = Status::newGood();
$db = $this->getConnection( $lockSrv ); // checked in isServerUp()
- $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
+ $keys = array_unique( array_map( array( $this, 'sha1Base36Absolute' ), $paths ) );
# Build up values for INSERT clause
$data = array();
foreach ( $keys as $key ) {
* @return string
*/
protected function getLockPath( $path ) {
- $hash = self::sha1Base36( $path );
- return "{$this->lockDir}/{$hash}.lock";
+ return "{$this->lockDir}/{$this->sha1Base36Absolute( $path )}.lock";
}
/**
// Send out the command and get the response...
$type = ( $type == self::LOCK_SH ) ? 'SH' : 'EX';
- $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
+ $keys = array_unique( array_map( array( $this, 'sha1Base36Absolute' ), $paths ) );
$response = $this->sendCommand( $lockSrv, 'ACQUIRE', $type, $keys );
if ( $response !== 'ACQUIRED' ) {
// Send out the command and get the response...
$type = ( $type == self::LOCK_SH ) ? 'SH' : 'EX';
- $keys = array_unique( array_map( 'LockManager::sha1Base36', $paths ) );
+ $keys = array_unique( array_map( array( $this, 'sha1Base36Absolute' ), $paths ) );
$response = $this->sendCommand( $lockSrv, 'RELEASE', $type, $keys );
if ( $response !== 'RELEASED' ) {
/** @var Array Map of (resource path => lock type => count) */
protected $locksHeld = array();
+ protected $wiki; // string; wiki ID
+
/* 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)
/**
* Construct a new instance from configuration
*
+ * $config paramaters include:
+ * - wiki : Wiki ID string that all resources are relative to [optional]
+ *
* @param $config Array
*/
- public function __construct( array $config ) {}
+ public function __construct( array $config ) {
+ $this->wiki = isset( $config['wiki'] ) ? $config['wiki'] : wfWikiID();
+ }
/**
* Lock the resources at the given abstract paths
}
/**
- * Get the base 36 SHA-1 of a string, padded to 31 digits
+ * Get the base 36 SHA-1 of a string, padded to 31 digits.
+ * Before hashing, the path will be prefixed with the wiki ID.
+ * This should be used interally for lock key or file names.
*
* @param $path string
* @return string
*/
- final protected static function sha1Base36( $path ) {
- return wfBaseConvert( sha1( $path ), 16, 36, 31 );
+ final protected function sha1Base36Absolute( $path ) {
+ return wfBaseConvert( sha1( "{$this->wiki}:{$path}" ), 16, 36, 31 );
}
/**
* @since 1.19
*/
class LockManagerGroup {
- /**
- * @var LockManagerGroup
- */
- protected static $instance = null;
+ /** @var Array (wiki => LockManager) */
+ protected static $instances = array();
+
+ protected $wiki; // string; wiki ID
/** @var Array of (name => ('class' =>, 'config' =>, 'instance' =>)) */
protected $managers = array();
- protected function __construct() {}
+ /**
+ * @param $wiki string Wiki ID
+ */
+ protected function __construct( $wiki ) {
+ $this->wiki = $wiki;
+ }
/**
+ * @param $wiki string Wiki ID
* @return LockManagerGroup
*/
- public static function singleton() {
- if ( self::$instance == null ) {
- self::$instance = new self();
- self::$instance->initFromGlobals();
+ public static function singleton( $wiki = false ) {
+ $wiki = ( $wiki === false ) ? wfWikiID() : $wiki;
+ if ( !isset( self::$instances[$wiki] ) ) {
+ self::$instances[$wiki] = new self( $wiki );
+ self::$instances[$wiki]->initFromGlobals();
}
- return self::$instance;
+ return self::$instances[$wiki];
}
/**
- * Destroy the singleton instance, so that a new one will be created next
- * time singleton() is called.
+ * Destroy the singleton instances
+ *
+ * @return void
*/
- public static function destroySingleton() {
- self::$instance = null;
+ public static function destroySingletons() {
+ self::$instances = array();
}
/**
*/
protected function register( array $configs ) {
foreach ( $configs as $config ) {
+ $config['wiki'] = $this->wiki;
if ( !isset( $config['name'] ) ) {
throw new MWException( "Cannot register a lock manager with no name." );
}
protected $lockExpiry; // integer; maximum time locks can be held
protected $session = ''; // string; random SHA-1 UUID
- protected $wikiId = ''; // string
/**
* Construct a new instance from configuration.
* each having an odd-numbered list of server names (peers) as values.
* - memcConfig : Configuration array for ObjectCache::newFromParams. [optional]
* If set, this must use one of the memcached classes.
- * - wikiId : Wiki ID string that all resources are relative to. [optional]
*
* @param Array $config
* @throws MWException
}
}
- $this->wikiId = isset( $config['wikiId'] ) ? $config['wikiId'] : wfWikiID();
-
$met = ini_get( 'max_execution_time' ); // this is 0 in CLI mode
$this->lockExpiry = $met ? 2*(int)$met : 2*3600;
* @return string
*/
protected function recordKeyForPath( $path ) {
- $hash = LockManager::sha1Base36( $path );
- list( $db, $prefix ) = wfSplitWikiID( $this->wikiId );
- return wfForeignMemcKey( $db, $prefix, __CLASS__, 'locks', $hash );
+ return implode( ':', array( __CLASS__, 'locks', $this->sha1Base36Absolute( $path ) ) );
}
/**
private function teardownGlobals() {
RepoGroup::destroySingleton();
FileBackendGroup::destroySingleton();
- LockManagerGroup::destroySingleton();
+ LockManagerGroup::destroySingletons();
LinkCache::singleton()->clear();
foreach ( $this->savedGlobals as $var => $val ) {