[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/filebackend/lockmanager/ -> LockManager.php (source)

   1  <?php
   2  /**
   3   * @defgroup LockManager Lock management
   4   * @ingroup FileBackend
   5   */
   6  
   7  /**
   8   * Resource locking handling.
   9   *
  10   * This program is free software; you can redistribute it and/or modify
  11   * it under the terms of the GNU General Public License as published by
  12   * the Free Software Foundation; either version 2 of the License, or
  13   * (at your option) any later version.
  14   *
  15   * This program is distributed in the hope that it will be useful,
  16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18   * GNU General Public License for more details.
  19   *
  20   * You should have received a copy of the GNU General Public License along
  21   * with this program; if not, write to the Free Software Foundation, Inc.,
  22   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  23   * http://www.gnu.org/copyleft/gpl.html
  24   *
  25   * @file
  26   * @ingroup LockManager
  27   * @author Aaron Schulz
  28   */
  29  
  30  /**
  31   * @brief Class for handling resource locking.
  32   *
  33   * Locks on resource keys can either be shared or exclusive.
  34   *
  35   * Implementations must keep track of what is locked by this proccess
  36   * in-memory and support nested locking calls (using reference counting).
  37   * At least LOCK_UW and LOCK_EX must be implemented. LOCK_SH can be a no-op.
  38   * Locks should either be non-blocking or have low wait timeouts.
  39   *
  40   * Subclasses should avoid throwing exceptions at all costs.
  41   *
  42   * @ingroup LockManager
  43   * @since 1.19
  44   */
  45  abstract class LockManager {
  46      /** @var array Mapping of lock types to the type actually used */
  47      protected $lockTypeMap = array(
  48          self::LOCK_SH => self::LOCK_SH,
  49          self::LOCK_UW => self::LOCK_EX, // subclasses may use self::LOCK_SH
  50          self::LOCK_EX => self::LOCK_EX
  51      );
  52  
  53      /** @var array Map of (resource path => lock type => count) */
  54      protected $locksHeld = array();
  55  
  56      protected $domain; // string; domain (usually wiki ID)
  57      protected $lockTTL; // integer; maximum time locks can be held
  58  
  59      /** Lock types; stronger locks have higher values */
  60      const LOCK_SH = 1; // shared lock (for reads)
  61      const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
  62      const LOCK_EX = 3; // exclusive lock (for writes)
  63  
  64      /**
  65       * Construct a new instance from configuration
  66       *
  67       * @param array $config Paramaters include:
  68       *   - domain  : Domain (usually wiki ID) that all resources are relative to [optional]
  69       *   - lockTTL : Age (in seconds) at which resource locks should expire.
  70       *               This only applies if locks are not tied to a connection/process.
  71       */
  72  	public function __construct( array $config ) {
  73          $this->domain = isset( $config['domain'] ) ? $config['domain'] : wfWikiID();
  74          if ( isset( $config['lockTTL'] ) ) {
  75              $this->lockTTL = max( 1, $config['lockTTL'] );
  76          } elseif ( PHP_SAPI === 'cli' ) {
  77              $this->lockTTL = 2 * 3600;
  78          } else {
  79              $met = ini_get( 'max_execution_time' ); // this is 0 in CLI mode
  80              $this->lockTTL = max( 5 * 60, 2 * (int)$met );
  81          }
  82      }
  83  
  84      /**
  85       * Lock the resources at the given abstract paths
  86       *
  87       * @param array $paths List of resource names
  88       * @param int $type LockManager::LOCK_* constant
  89       * @param int $timeout Timeout in seconds (0 means non-blocking) (since 1.21)
  90       * @return Status
  91       */
  92  	final public function lock( array $paths, $type = self::LOCK_EX, $timeout = 0 ) {
  93          return $this->lockByType( array( $type => $paths ), $timeout );
  94      }
  95  
  96      /**
  97       * Lock the resources at the given abstract paths
  98       *
  99       * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
 100       * @param int $timeout Timeout in seconds (0 means non-blocking) (since 1.21)
 101       * @return Status
 102       * @since 1.22
 103       */
 104  	final public function lockByType( array $pathsByType, $timeout = 0 ) {
 105          wfProfileIn( __METHOD__ );
 106          $status = Status::newGood();
 107          $pathsByType = $this->normalizePathsByType( $pathsByType );
 108          $msleep = array( 0, 50, 100, 300, 500 ); // retry backoff times
 109          $start = microtime( true );
 110          do {
 111              $status = $this->doLockByType( $pathsByType );
 112              $elapsed = microtime( true ) - $start;
 113              if ( $status->isOK() || $elapsed >= $timeout || $elapsed < 0 ) {
 114                  break; // success, timeout, or clock set back
 115              }
 116              usleep( 1e3 * ( next( $msleep ) ?: 1000 ) ); // use 1 sec after enough times
 117              $elapsed = microtime( true ) - $start;
 118          } while ( $elapsed < $timeout && $elapsed >= 0 );
 119          wfProfileOut( __METHOD__ );
 120  
 121          return $status;
 122      }
 123  
 124      /**
 125       * Unlock the resources at the given abstract paths
 126       *
 127       * @param array $paths List of paths
 128       * @param int $type LockManager::LOCK_* constant
 129       * @return Status
 130       */
 131  	final public function unlock( array $paths, $type = self::LOCK_EX ) {
 132          return $this->unlockByType( array( $type => $paths ) );
 133      }
 134  
 135      /**
 136       * Unlock the resources at the given abstract paths
 137       *
 138       * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
 139       * @return Status
 140       * @since 1.22
 141       */
 142  	final public function unlockByType( array $pathsByType ) {
 143          wfProfileIn( __METHOD__ );
 144          $pathsByType = $this->normalizePathsByType( $pathsByType );
 145          $status = $this->doUnlockByType( $pathsByType );
 146          wfProfileOut( __METHOD__ );
 147  
 148          return $status;
 149      }
 150  
 151      /**
 152       * Get the base 36 SHA-1 of a string, padded to 31 digits.
 153       * Before hashing, the path will be prefixed with the domain ID.
 154       * This should be used interally for lock key or file names.
 155       *
 156       * @param string $path
 157       * @return string
 158       */
 159  	final protected function sha1Base36Absolute( $path ) {
 160          return wfBaseConvert( sha1( "{$this->domain}:{$path}" ), 16, 36, 31 );
 161      }
 162  
 163      /**
 164       * Get the base 16 SHA-1 of a string, padded to 31 digits.
 165       * Before hashing, the path will be prefixed with the domain ID.
 166       * This should be used interally for lock key or file names.
 167       *
 168       * @param string $path
 169       * @return string
 170       */
 171  	final protected function sha1Base16Absolute( $path ) {
 172          return sha1( "{$this->domain}:{$path}" );
 173      }
 174  
 175      /**
 176       * Normalize the $paths array by converting LOCK_UW locks into the
 177       * appropriate type and removing any duplicated paths for each lock type.
 178       *
 179       * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
 180       * @return array
 181       * @since 1.22
 182       */
 183  	final protected function normalizePathsByType( array $pathsByType ) {
 184          $res = array();
 185          foreach ( $pathsByType as $type => $paths ) {
 186              $res[$this->lockTypeMap[$type]] = array_unique( $paths );
 187          }
 188  
 189          return $res;
 190      }
 191  
 192      /**
 193       * @see LockManager::lockByType()
 194       * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
 195       * @return Status
 196       * @since 1.22
 197       */
 198  	protected function doLockByType( array $pathsByType ) {
 199          $status = Status::newGood();
 200          $lockedByType = array(); // map of (type => paths)
 201          foreach ( $pathsByType as $type => $paths ) {
 202              $status->merge( $this->doLock( $paths, $type ) );
 203              if ( $status->isOK() ) {
 204                  $lockedByType[$type] = $paths;
 205              } else {
 206                  // Release the subset of locks that were acquired
 207                  foreach ( $lockedByType as $lType => $lPaths ) {
 208                      $status->merge( $this->doUnlock( $lPaths, $lType ) );
 209                  }
 210                  break;
 211              }
 212          }
 213  
 214          return $status;
 215      }
 216  
 217      /**
 218       * Lock resources with the given keys and lock type
 219       *
 220       * @param array $paths List of paths
 221       * @param int $type LockManager::LOCK_* constant
 222       * @return Status
 223       */
 224      abstract protected function doLock( array $paths, $type );
 225  
 226      /**
 227       * @see LockManager::unlockByType()
 228       * @param array $pathsByType Map of LockManager::LOCK_* constants to lists of paths
 229       * @return Status
 230       * @since 1.22
 231       */
 232  	protected function doUnlockByType( array $pathsByType ) {
 233          $status = Status::newGood();
 234          foreach ( $pathsByType as $type => $paths ) {
 235              $status->merge( $this->doUnlock( $paths, $type ) );
 236          }
 237  
 238          return $status;
 239      }
 240  
 241      /**
 242       * Unlock resources with the given keys and lock type
 243       *
 244       * @param array $paths List of paths
 245       * @param int $type LockManager::LOCK_* constant
 246       * @return Status
 247       */
 248      abstract protected function doUnlock( array $paths, $type );
 249  }
 250  
 251  /**
 252   * Simple version of LockManager that does nothing
 253   * @since 1.19
 254   */
 255  class NullLockManager extends LockManager {
 256  	protected function doLock( array $paths, $type ) {
 257          return Status::newGood();
 258      }
 259  
 260  	protected function doUnlock( array $paths, $type ) {
 261          return Status::newGood();
 262      }
 263  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1