MediaWiki  REL1_24
LockManager.php
Go to the documentation of this file.
00001 <?php
00045 abstract class LockManager {
00047     protected $lockTypeMap = array(
00048         self::LOCK_SH => self::LOCK_SH,
00049         self::LOCK_UW => self::LOCK_EX, // subclasses may use self::LOCK_SH
00050         self::LOCK_EX => self::LOCK_EX
00051     );
00052 
00054     protected $locksHeld = array();
00055 
00056     protected $domain; // string; domain (usually wiki ID)
00057     protected $lockTTL; // integer; maximum time locks can be held
00058 
00060     const LOCK_SH = 1; // shared lock (for reads)
00061     const LOCK_UW = 2; // shared lock (for reads used to write elsewhere)
00062     const LOCK_EX = 3; // exclusive lock (for writes)
00063 
00072     public function __construct( array $config ) {
00073         $this->domain = isset( $config['domain'] ) ? $config['domain'] : wfWikiID();
00074         if ( isset( $config['lockTTL'] ) ) {
00075             $this->lockTTL = max( 1, $config['lockTTL'] );
00076         } elseif ( PHP_SAPI === 'cli' ) {
00077             $this->lockTTL = 2 * 3600;
00078         } else {
00079             $met = ini_get( 'max_execution_time' ); // this is 0 in CLI mode
00080             $this->lockTTL = max( 5 * 60, 2 * (int)$met );
00081         }
00082     }
00083 
00092     final public function lock( array $paths, $type = self::LOCK_EX, $timeout = 0 ) {
00093         return $this->lockByType( array( $type => $paths ), $timeout );
00094     }
00095 
00104     final public function lockByType( array $pathsByType, $timeout = 0 ) {
00105         wfProfileIn( __METHOD__ );
00106         $status = Status::newGood();
00107         $pathsByType = $this->normalizePathsByType( $pathsByType );
00108         $msleep = array( 0, 50, 100, 300, 500 ); // retry backoff times
00109         $start = microtime( true );
00110         do {
00111             $status = $this->doLockByType( $pathsByType );
00112             $elapsed = microtime( true ) - $start;
00113             if ( $status->isOK() || $elapsed >= $timeout || $elapsed < 0 ) {
00114                 break; // success, timeout, or clock set back
00115             }
00116             usleep( 1e3 * ( next( $msleep ) ?: 1000 ) ); // use 1 sec after enough times
00117             $elapsed = microtime( true ) - $start;
00118         } while ( $elapsed < $timeout && $elapsed >= 0 );
00119         wfProfileOut( __METHOD__ );
00120 
00121         return $status;
00122     }
00123 
00131     final public function unlock( array $paths, $type = self::LOCK_EX ) {
00132         return $this->unlockByType( array( $type => $paths ) );
00133     }
00134 
00142     final public function unlockByType( array $pathsByType ) {
00143         wfProfileIn( __METHOD__ );
00144         $pathsByType = $this->normalizePathsByType( $pathsByType );
00145         $status = $this->doUnlockByType( $pathsByType );
00146         wfProfileOut( __METHOD__ );
00147 
00148         return $status;
00149     }
00150 
00159     final protected function sha1Base36Absolute( $path ) {
00160         return wfBaseConvert( sha1( "{$this->domain}:{$path}" ), 16, 36, 31 );
00161     }
00162 
00171     final protected function sha1Base16Absolute( $path ) {
00172         return sha1( "{$this->domain}:{$path}" );
00173     }
00174 
00183     final protected function normalizePathsByType( array $pathsByType ) {
00184         $res = array();
00185         foreach ( $pathsByType as $type => $paths ) {
00186             $res[$this->lockTypeMap[$type]] = array_unique( $paths );
00187         }
00188 
00189         return $res;
00190     }
00191 
00198     protected function doLockByType( array $pathsByType ) {
00199         $status = Status::newGood();
00200         $lockedByType = array(); // map of (type => paths)
00201         foreach ( $pathsByType as $type => $paths ) {
00202             $status->merge( $this->doLock( $paths, $type ) );
00203             if ( $status->isOK() ) {
00204                 $lockedByType[$type] = $paths;
00205             } else {
00206                 // Release the subset of locks that were acquired
00207                 foreach ( $lockedByType as $lType => $lPaths ) {
00208                     $status->merge( $this->doUnlock( $lPaths, $lType ) );
00209                 }
00210                 break;
00211             }
00212         }
00213 
00214         return $status;
00215     }
00216 
00224     abstract protected function doLock( array $paths, $type );
00225 
00232     protected function doUnlockByType( array $pathsByType ) {
00233         $status = Status::newGood();
00234         foreach ( $pathsByType as $type => $paths ) {
00235             $status->merge( $this->doUnlock( $paths, $type ) );
00236         }
00237 
00238         return $status;
00239     }
00240 
00248     abstract protected function doUnlock( array $paths, $type );
00249 }
00250 
00255 class NullLockManager extends LockManager {
00256     protected function doLock( array $paths, $type ) {
00257         return Status::newGood();
00258     }
00259 
00260     protected function doUnlock( array $paths, $type ) {
00261         return Status::newGood();
00262     }
00263 }