https://www.mediawiki.org/wiki/Special:Code/MediaWiki/106554
Revision: 106554 Author: aaron Date: 2011-12-18 08:16:17 +0000 (Sun, 18 Dec 2011) Log Message: ----------- * Added LSLockManager and and corresponding LockServerDaemon.php file * Message tweaks Modified Paths: -------------- branches/FileBackend/phase3/includes/AutoLoader.php branches/FileBackend/phase3/languages/messages/MessagesEn.php branches/FileBackend/phase3/maintenance/language/messages.inc Added Paths: ----------- branches/FileBackend/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php branches/FileBackend/phase3/maintenance/locking/LockServerDaemon.php Modified: branches/FileBackend/phase3/includes/AutoLoader.php =================================================================== --- branches/FileBackend/phase3/includes/AutoLoader.php 2011-12-18 03:33:31 UTC (rev 106553) +++ branches/FileBackend/phase3/includes/AutoLoader.php 2011-12-18 08:16:17 UTC (rev 106554) @@ -496,6 +496,7 @@ 'ScopedLock' => 'includes/filerepo/backend/lockmanager/LockManager.php', 'FSLockManager' => 'includes/filerepo/backend/lockmanager/FSLockManager.php', 'DBLockManager' => 'includes/filerepo/backend/lockmanager/DBLockManager.php', + 'LSLockManager' => 'includes/filerepo/backend/lockmanager/LSLockManager.php', 'MySqlLockManager'=> 'includes/filerepo/backend/lockmanager/DBLockManager.php', 'NullLockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php', 'FileOp' => 'includes/filerepo/backend/FileOp.php', Added: branches/FileBackend/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php =================================================================== --- branches/FileBackend/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php (rev 0) +++ branches/FileBackend/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php 2011-12-18 08:16:17 UTC (rev 106554) @@ -0,0 +1,285 @@ +<?php + +/** + * Version of LockManager based on using lock daemon servers. + * This is meant for multi-wiki systems that may share files. + * All locks are non-blocking, which avoids deadlocks. + * + * All lock requests for a resource, identified by a hash string, will map + * to one bucket. Each bucket maps to one or several peer servers, each + * running LockServerDaemon.php, listening on a designated TCP port. + * A majority of peers must agree for a lock to be acquired. + */ +class LSLockManager extends LockManager { + /** @var Array Mapping of lock types to the type actually used */ + protected $lockTypeMap = array( + self::LOCK_SH => self::LOCK_SH, + self::LOCK_UW => self::LOCK_SH, + self::LOCK_EX => self::LOCK_EX + ); + + /** @var Array Map of server names to server config */ + protected $lockServers; // (server name => server config array) + /** @var Array Map of bucket indexes to peer server lists */ + protected $srvsByBucket; // (bucket index => (lsrv1, lsrv2, ...)) + + /** @var Array Map of (locked key => lock type => count) */ + protected $locksHeld = array(); + /** @var Array Map Server connections (server name => resource) */ + protected $conns = array(); + + protected $connTimeout; // float number of seconds + protected $session = ''; // random SHA-1 string + + /** + * Construct a new instance from configuration. + * $config paramaters include: + * 'lockServers' : Associative array of server names to configuration. + * Configuration is an associative array that includes: + * 'host' - IP address/hostname + * 'port' - TCP port + * 'authKey' - Secret string the lock server uses + * 'srvsByBucket' : Array of 1-16 consecutive integer keys, starting from 0, + * each having an odd-numbered list of server names (peers) as values. + * 'connTimeout' : Lock server connection attempt timeout. [optional] + * + * @param Array $config + */ + public function __construct( array $config ) { + $this->lockServers = $config['lockServers']; + // Sanitize srvsByBucket config to prevent PHP errors + $this->srvsByBucket = array_filter( $config['srvsByBucket'], 'is_array' ); + $this->srvsByBucket = array_values( $this->srvsByBucket ); // consecutive + + if ( isset( $config['connTimeout'] ) ) { + $this->connTimeout = $config['connTimeout']; + } else { + $this->connTimeout = 3; // use some sane amount + } + + $this->session = ''; + for ( $i = 0; $i < 5; $i++ ) { + $this->session .= mt_rand( 0, 2147483647 ); + } + $this->session = sha1( $this->session ); + } + + protected function doLock( array $keys, $type ) { + $status = Status::newGood(); + + $keysToLock = array(); + // Get locks that need to be acquired (buckets => locks)... + foreach ( $keys as $key ) { + if ( isset( $this->locksHeld[$key][$type] ) ) { + ++$this->locksHeld[$key][$type]; + } elseif ( isset( $this->locksHeld[$key][self::LOCK_EX] ) ) { + $this->locksHeld[$key][$type] = 1; + } else { + $bucket = $this->getBucketFromKey( $key ); + $keysToLock[$bucket][] = $key; + } + } + + $lockedKeys = array(); // files locked in this attempt + // Attempt to acquire these locks... + foreach ( $keysToLock as $bucket => $keys ) { + // Try to acquire the locks for this bucket + $res = $this->doLockingRequestAll( $bucket, $keys, $type ); + if ( $res === 'cantacquire' ) { + // Resources already locked by another process. + // Abort and unlock everything we just locked. + $status->fatal( 'lockmanager-fail-acquirelocks', implode( ', ', $keys ) ); + $status->merge( $this->doUnlock( $lockedKeys, $type ) ); + return $status; + } elseif ( $res !== true ) { + // Couldn't contact any servers for this bucket. + // Abort and unlock everything we just locked. + $status->fatal( 'lockmanager-fail-acquirelocks', implode( ', ', $keys ) ); + $status->merge( $this->doUnlock( $lockedKeys, $type ) ); + return $status; + } + // Record these locks as active + foreach ( $keys as $key ) { + $this->locksHeld[$key][$type] = 1; // locked + } + // Keep track of what locks were made in this attempt + $lockedKeys = array_merge( $lockedKeys, $keys ); + } + + return $status; + } + + protected function doUnlock( array $keys, $type ) { + $status = Status::newGood(); + + foreach ( $keys as $key ) { + if ( !isset( $this->locksHeld[$key] ) ) { + $status->warning( 'lockmanager-notlocked', $key ); + } elseif ( !isset( $this->locksHeld[$key][$type] ) ) { + $status->warning( 'lockmanager-notlocked', $key ); + } else { + --$this->locksHeld[$key][$type]; + if ( $this->locksHeld[$key][$type] <= 0 ) { + unset( $this->locksHeld[$key][$type] ); + } + if ( !count( $this->locksHeld[$key] ) ) { + unset( $this->locksHeld[$key] ); // no SH or EX locks left for key + } + } + } + + // Reference count the locks held and release locks when zero + if ( !count( $this->locksHeld ) ) { + $status->merge( $this->releaseLocks() ); + } + + return $status; + } + + /** + * Get a connection to a lock server and acquire locks on $keys. + * + * @param $lockSrv string + * @param $keys Array + * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH + * @return bool Resources able to be locked + */ + protected function doLockingRequest( $lockSrv, array $keys, $type ) { + if ( $type == self::LOCK_SH ) { // reader locks + $type = 'SH'; + } elseif ( $type == self::LOCK_EX ) { // writer locks + $type = 'EX'; + } else { + return true; // ok... + } + + // Send out the command and get the response... + $response = $this->sendCommand( $lockSrv, 'ACQUIRE', $type, $keys ); + + return ( $response === 'ACQUIRED' ); + } + + /** + * Send a command and get back the response + * + * @param $lockSrv string + * @param $action string + * @param $type string + * @param $values Array + * @return string|false + */ + protected function sendCommand( $lockSrv, $action, $type, $values ) { + $conn = $this->getConnection( $lockSrv ); + if ( !$conn ) { + return false; // no connection + } + $authKey = $this->lockServers[$lockSrv]['authKey']; + // Build of the command as a flat string... + $values = implode( '|', $values ); + $key = sha1( $this->session . $action . $type . $values . $authKey ); + // Send out the command... + if ( fwrite( $conn, "{$this->session}:$key:$action:$type:$values\n" ) === false ) { + return false; + } + // Get the response... + $response = fgets( $conn ); + if ( $response === false ) { + return false; + } + return trim( $response ); + } + + /** + * Attempt to acquire locks with the peers for a bucket. + * + * @param $bucket integer + * @param $keys Array List of resource keys to lock + * @param $type integer LockManager::LOCK_EX or LockManager::LOCK_SH + * @return bool|string One of (true, 'cantacquire', 'srverrors') + */ + protected function doLockingRequestAll( $bucket, array $keys, $type ) { + $yesVotes = 0; // locks made on trustable servers + $votesLeft = count( $this->srvsByBucket[$bucket] ); // remaining peers + $quorum = floor( $votesLeft/2 + 1 ); // simple majority + // Get votes for each peer, in order, until we have enough... + foreach ( $this->srvsByBucket[$bucket] as $index => $lockSrv ) { + // Attempt to acquire the lock on this peer + if ( !$this->doLockingRequest( $lockSrv, $keys, $type ) ) { + return 'cantacquire'; // vetoed; resource locked + } + ++$yesVotes; // success for this peer + if ( $yesVotes >= $quorum ) { + return true; // lock obtained + } + $votesLeft--; + $votesNeeded = $quorum - $yesVotes; + if ( $votesNeeded > $votesLeft ) { + // In "trust cache" mode we don't have to meet the quorum + break; // short-circuit + } + } + // At this point, we must not have meet the quorum + return 'srverrors'; // not enough votes to ensure correctness + } + + /** + * Get (or reuse) a connection to a lock server + * + * @param $lockSrv string + * @return resource + */ + protected function getConnection( $lockSrv ) { + if ( !isset( $this->conns[$lockSrv] ) ) { + $cfg = $this->lockServers[$lockSrv]; + wfSuppressWarnings(); + $errno = $errstr = ''; + $conn = fsockopen( $cfg['host'], $cfg['port'], $errno, $errstr, $this->connTimeout ); + wfRestoreWarnings(); + if ( $conn === false ) { + return null; + } + $sec = floor( $this->connTimeout ); + $usec = floor( ( $this->connTimeout - floor( $this->connTimeout ) ) * 1e6 ); + stream_set_timeout( $conn, $sec, $usec ); + $this->conns[$lockSrv] = $conn; + } + return $this->conns[$lockSrv]; + } + + /** + * Release all locks that this session is holding. + * + * @return Status + */ + protected function releaseLocks() { + $status = Status::newGood(); + foreach ( $this->conns as $lockSrv => $conn ) { + $response = $this->sendCommand( $lockSrv, 'RELEASE_ALL', '', array() ); + if ( $response !== 'RELEASED_ALL' ) { + $status->fatal( 'lockmanager-fail-svr-release', $lockSrv ); + } + } + return $status; + } + + /** + * Get the bucket for lock key. + * + * @param $key string (40 char hex key) + * @return integer + */ + protected function getBucketFromKey( $key ) { + $prefix = substr( $key, 0, 2 ); // first 2 hex chars (8 bits) + return intval( base_convert( $prefix, 16, 10 ) ) % count( $this->srvsByBucket ); + } + + /** + * Make sure remaining locks get cleared for sanity + */ + function __destruct() { + $this->releaseLocks(); + foreach ( $this->conns as $lockSrv => $conn ) { + fclose( $conn ); + } + } +} Property changes on: branches/FileBackend/phase3/includes/filerepo/backend/lockmanager/LSLockManager.php ___________________________________________________________________ Added: svn:eol-style + native Modified: branches/FileBackend/phase3/languages/messages/MessagesEn.php =================================================================== --- branches/FileBackend/phase3/languages/messages/MessagesEn.php 2011-12-18 03:33:31 UTC (rev 106553) +++ branches/FileBackend/phase3/languages/messages/MessagesEn.php 2011-12-18 08:16:17 UTC (rev 106554) @@ -2279,8 +2279,9 @@ 'lockmanager-fail-acquirelock' => 'Could not acquire lock for key "$1".', 'lockmanager-fail-releaselock' => 'Could not release lock for key "$1".', 'lockmanager-fail-acquirelocks' => 'Could not acquire locks for keys "$1".', -'lockmanager-fail-db-bucket' => 'Could not contact enough lock servers in bucket $1', -'lockmanager-fail-db-release' => 'Could not release locks on server $1', +'lockmanager-fail-db-bucket' => 'Could not contact enough lock databases in bucket $1', +'lockmanager-fail-db-release' => 'Could not release locks on database $1', +'lockmanager-fail-svr-release' => 'Could not release locks on server $1', # img_auth script messages 'img-auth-accessdenied' => 'Access denied', Modified: branches/FileBackend/phase3/maintenance/language/messages.inc =================================================================== --- branches/FileBackend/phase3/maintenance/language/messages.inc 2011-12-18 03:33:31 UTC (rev 106553) +++ branches/FileBackend/phase3/maintenance/language/messages.inc 2011-12-18 08:16:17 UTC (rev 106554) @@ -1374,7 +1374,8 @@ 'lockmanager-fail-releaselock', 'lockmanager-fail-acquirelocks', 'lockmanager-fail-db-bucket', - 'lockmanager-fail-db-release' + 'lockmanager-fail-db-release', + 'lockmanager-fail-svr-release' ), 'zip' => array( Added: branches/FileBackend/phase3/maintenance/locking/LockServerDaemon.php =================================================================== --- branches/FileBackend/phase3/maintenance/locking/LockServerDaemon.php (rev 0) +++ branches/FileBackend/phase3/maintenance/locking/LockServerDaemon.php 2011-12-18 08:16:17 UTC (rev 106554) @@ -0,0 +1,414 @@ +<?php + +if ( php_sapi_name() !== 'cli' ) { + die( "This is not a valid entry point.\n" ); +} +error_reporting( E_ALL ); + +// Run the server... +set_time_limit( 0 ); +LockServerDaemon::init( + getopt( '', array( + 'address:', 'port:', 'authKey:', + 'connTimeout::', 'lockTimeout::', 'maxClients::', 'maxBacklog::', 'maxLocks::', + ) ) +)->main(); + +/** + * Simple lock server daemon that accepts lock/unlock requests. + * This should not require MediaWiki setup or PHP files. + */ +class LockServerDaemon { + /** @var resource */ + protected $sock; // socket to listen/accept on + /** @var Array */ + protected $shLocks = array(); // (key => session => 1) + /** @var Array */ + protected $exLocks = array(); // (key => session) + /** @var Array */ + protected $sessions = array(); // (session => resource) + /** @var Array */ + protected $deadSessions = array(); // (session => UNIX timestamp) + + /** @var Array */ + protected $sessionIndexSh = array(); // (session => key => 1) + /** @var Array */ + protected $sessionIndexEx = array(); // (session => key => 1) + + protected $authKey; // string key + protected $connTimeout; // array ( 'sec' => integer, 'usec' => integer ) + protected $lockTimeout; // integer number of seconds + protected $maxLocks; // integer + protected $maxClients; // integer + + protected $startTime; // integer UNIX timestamp + protected $lockCount = 0; // integer + protected $ticks = 0; // integer counter + + protected static $instance = null; + + /** + * @params $config Array + * @return LockServerDaemon + */ + public function init( array $config ) { + if ( self::$instance ) { + throw new Exception( 'LockServer already initialized.' ); + } + self::$instance = new self( $config ); + return self::$instance; + } + + /** + * @params $config Array + */ + protected function __construct( array $config ) { + $required = array( 'address', 'port', 'authKey' ); + foreach ( $required as $par ) { + if ( !isset( $config[$par] ) ) { + throw new Exception( "Parameter '$par' must be specified." ); + } + } + + $this->authKey = $config['authKey']; + $connTimeout = isset( $config['connTimeout'] ) + ? $config['connTimeout'] + : 1.5; + $this->connTimeout = array( + 'sec' => floor( $connTimeout ), + 'usec' => floor( ( $connTimeout - floor( $connTimeout ) ) * 1e6 ) + ); + $this->lockTimeout = isset( $config['lockTimeout'] ) + ? $config['lockTimeout'] + : 60; + $this->maxLocks = isset( $config['maxLocks'] ) + ? $config['maxLocks'] + : 5000; + $this->maxClients = isset( $config['maxClients'] ) + ? $config['maxClients'] + : 100; + $backlog = isset( $config['maxBacklog'] ) + ? $config['maxBacklog'] + : 10; + + if ( !function_exists( 'socket_create' ) ) { + throw new Exception( "PHP sockets extension missing from PHP CLI mode." ); + } + $sock = socket_create( AF_INET, SOCK_STREAM, SOL_TCP ); + if ( $sock === false ) { + throw new Exception( "socket_create(): " . socket_strerror( socket_last_error() ) ); + } + socket_set_option( $sock, SOL_SOCKET, SO_REUSEADDR, 1 ); // bypass 2MLS + if ( socket_bind( $sock, $config['address'], $config['port'] ) === false ) { + throw new Exception( "socket_bind(): " . + socket_strerror( socket_last_error( $sock ) ) ); + } elseif ( socket_listen( $sock, $backlog ) === false ) { + throw new Exception( "socket_listen(): " . + socket_strerror( socket_last_error( $sock ) ) ); + } + $this->sock = $sock; + + $this->startTime = time(); + } + + /** + * @return void + */ + public function main() { + // Create a list of all the clients that will be connected to us. + $clients = array( $this->sock ); // start off with listening socket + do { + // Create a copy, so $clients doesn't get modified by socket_select() + $read = $clients; // clients-with-data + // Get a list of all the clients that have data to be read from + $changed = socket_select( $read, $write = NULL, $except = NULL, NULL ); + if ( $changed === false ) { + trigger_error( 'socket_listen(): ' . socket_strerror( socket_last_error() ) ); + continue; + } elseif ( $changed < 1 ) { + continue; // wait + } + // Check if there is a client trying to connect... + if ( in_array( $this->sock, $read ) && count( $clients ) < $this->maxClients ) { + // Accept the new client... + $newsock = socket_accept( $this->sock ); + socket_set_option( $newsock, SOL_SOCKET, SO_RCVTIMEO, $this->connTimeout ); + socket_set_option( $newsock, SOL_SOCKET, SO_SNDTIMEO, $this->connTimeout ); + $clients[] = $newsock; + // Remove the listening socket from the clients-with-data array... + $key = array_search( $this->sock, $read ); + unset( $read[$key] ); + } + // Loop through all the clients that have data to read... + foreach ( $read as $read_sock ) { + // Read until newline or 65535 bytes are recieved. + // socket_read show errors when the client is disconnected. + $data = @socket_read( $read_sock, 65535, PHP_NORMAL_READ ); + // Check if the client is disconnected + if ( $data === false ) { + // Remove client for $clients array + $key = array_search( $read_sock, $clients ); + unset( $clients[$key] ); + // Remove socket's session from tracking (if it exists) + $session = array_search( $read_sock, $this->sessions ); + if ( $session !== false ) { + unset( $this->sessions[$session] ); + // Record recently killed sessions that still have locks + if ( isset( $this->sessionIndexSh[$session] ) + || isset( $this->sessionIndexEx[$session] ) ) + { + $this->deadSessions[$session] = time(); + } + } + } else { + // Perform the requested command... + $response = $this->doCommand( trim( $data ), $read_sock ); + // Send the response to the client... + if ( socket_write( $read_sock, "$response\n" ) === false ) { + trigger_error( 'socket_write(): ' . + socket_strerror( socket_last_error( $read_sock ) ) ); + } + } + } + // Prune dead locks every 10 socket events... + if ( ++$this->ticks >= 9 ) { + $this->ticks = 0; + $this->purgeExpiredLocks(); + } + } while ( true ); + } + + /** + * @param $data string + * @param $sourceSock resource + * @return string + */ + protected function doCommand( $data, $sourceSock ) { + $cmdArr = $this->getCommand( $data ); + if ( is_string( $cmdArr ) ) { + return $cmdArr; // error + } + list( $function, $session, $type, $resources ) = $cmdArr; + // On first command, track the session => sock correspondence + if ( !isset( $this->sessions[$session] ) ) { + $this->sessions[$session] = $sourceSock; + } + if ( $function === 'ACQUIRE' ) { + return $this->lock( $session, $type, $resources ); + } elseif ( $function === 'RELEASE' ) { + return $this->unlock( $session, $type, $resources ); + } elseif ( $function === 'RELEASE_ALL' ) { + return $this->release( $session ); + } elseif ( $function === 'STAT' ) { + return $this->stat(); + } + return 'INTERNAL_ERROR'; + } + + /** + * @param $data string + * @return Array + */ + protected function getCommand( $data ) { + $m = explode( ':', $data ); // <session, key, command, type, values> + if ( count( $m ) == 5 ) { + list( $session, $key, $command, $type, $values ) = $m; + if ( sha1( $session . $command . $type . $values . $this->authKey ) !== $key ) { + return 'BAD_KEY'; + } elseif ( strlen( $session ) !== 40 ) { + return 'BAD_SESSION'; + } + $values = explode( '|', $values ); + if ( $command === 'ACQUIRE' ) { + $needsLockArgs = true; + } elseif ( $command === 'RELEASE' ) { + $needsLockArgs = true; + } elseif ( $command === 'RELEASE_ALL' ) { + $needsLockArgs = false; + } elseif ( $command === 'STAT' ) { + $needsLockArgs = false; + } else { + return 'BAD_COMMAND'; + } + if ( $needsLockArgs ) { + if ( $type !== 'SH' && $type !== 'EX' ) { + return 'BAD_TYPE'; + } + foreach ( $values as $value ) { + if ( strlen( $value ) !== 40 ) { + return 'BAD_FORMAT'; + } + } + } + return array( $command, $session, $type, $values ); + } + return 'BAD_FORMAT'; + } + + /** + * @param $session string + * @param $type string + * @param $keys Array + * @return string + */ + protected function lock( $session, $type, $keys ) { + if ( $this->lockCount >= $this->maxLocks ) { + return 'TOO_MANY_LOCKS'; + } + if ( $type === 'SH' ) { + // Check if any keys are already write-locked... + foreach ( $keys as $key ) { + if ( isset( $this->exLocks[$key] ) && $this->exLocks[$key] !== $session ) { + return 'CANT_ACQUIRE'; + } + } + // Acquire the read-locks... + foreach ( $keys as $key ) { + $this->set_sh_lock( $key, $session ); + } + return 'ACQUIRED'; + } elseif ( $type === 'EX' ) { + // Check if any keys are already read-locked or write-locked... + foreach ( $keys as $key ) { + if ( isset( $this->exLocks[$key] ) && $this->exLocks[$key] !== $session ) { + return 'CANT_ACQUIRE'; + } + if ( isset( $this->shLocks[$key] ) ) { + foreach ( $this->shLocks[$key] as $otherSession => $x ) { + if ( $otherSession !== $session ) { + return 'CANT_ACQUIRE'; + } + } + } + } + // Acquire the write-locks... + foreach ( $keys as $key ) { + $this->set_ex_lock( $key, $session ); + } + return 'ACQUIRED'; + } + return 'INTERNAL_ERROR'; + } + + /** + * @param $session string + * @param $type string + * @param $keys Array + * @return string + */ + protected function unlock( $session, $type, $keys ) { + if ( $type === 'SH' ) { + foreach ( $keys as $key ) { + $this->unset_sh_lock( $key, $session ); + } + return 'RELEASED'; + } elseif ( $type === 'EX' ) { + foreach ( $keys as $key ) { + $this->unset_ex_lock( $key, $session ); + } + return 'RELEASED'; + } + return 'INTERNAL_ERROR'; + } + + /** + * @param $session string + * @return string + */ + protected function release( $session ) { + if ( isset( $this->sessionIndexSh[$session] ) ) { + foreach ( $this->sessionIndexSh[$session] as $key => $x ) { + $this->unset_sh_lock( $key, $session ); + } + } + if ( isset( $this->sessionIndexEx[$session] ) ) { + foreach ( $this->sessionIndexEx[$session] as $key => $x ) { + $this->unset_ex_lock( $key, $session ); + } + } + return 'RELEASED_ALL'; + } + + /** + * @return string + */ + protected function stat() { + return ( time() - $this->startTime ) . ':' . memory_get_usage(); + } + + /** + * Clear locks for sessions that have been dead for a while + * + * @return void + */ + protected function purgeExpiredLocks() { + $now = time(); + foreach ( $this->deadSessions as $session => $timestamp ) { + if ( ( $now - $timestamp ) > $this->lockTimeout ) { + $this->release( $session ); + unset( $this->deadSessions[$session] ); + } + } + } + + /** + * @param $key string + * @param $session string + * @return void + */ + protected function set_sh_lock( $key, $session ) { + if ( !isset( $this->shLocks[$key][$session] ) ) { + $this->shLocks[$key][$session] = 1; + $this->sessionIndexSh[$session][$key] = 1; + ++$this->lockCount; // we are adding a lock + } + } + + /** + * @param $key string + * @param $session string + * @return void + */ + protected function set_ex_lock( $key, $session ) { + if ( !isset( $this->exLocks[$key][$session] ) ) { + $this->exLocks[$key] = $session; + $this->sessionIndexEx[$session][$key] = 1; + ++$this->lockCount; // we are adding a lock + } + } + + /** + * @param $key string + * @param $session string + * @return void + */ + protected function unset_sh_lock( $key, $session ) { + if ( isset( $this->shLocks[$key][$session] ) ) { + unset( $this->shLocks[$key][$session] ); + if ( !count( $this->shLocks[$key] ) ) { + unset( $this->shLocks[$key] ); + } + unset( $this->sessionIndexSh[$session][$key] ); + if ( !count( $this->sessionIndexSh[$session] ) ) { + unset( $this->sessionIndexSh[$session] ); + } + --$this->lockCount; + } + } + + /** + * @param $key string + * @param $session string + * @return void + */ + protected function unset_ex_lock( $key, $session ) { + if ( isset( $this->exLocks[$key] ) && $this->exLocks[$key] === $session ) { + unset( $this->exLocks[$key] ); + unset( $this->sessionIndexEx[$session][$key] ); + if ( !count( $this->sessionIndexEx[$session] ) ) { + unset( $this->sessionIndexEx[$session] ); + } + --$this->lockCount; + } + } +} Property changes on: branches/FileBackend/phase3/maintenance/locking/LockServerDaemon.php ___________________________________________________________________ Added: svn:eol-style + native _______________________________________________ MediaWiki-CVS mailing list MediaWiki-CVS@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs