MediaWiki  REL1_22
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 
00074     public function __construct( array $config ) {
00075         $this->domain = isset( $config['domain'] ) ? $config['domain'] : wfWikiID();
00076         if ( isset( $config['lockTTL'] ) ) {
00077             $this->lockTTL = max( 1, $config['lockTTL'] );
00078         } elseif ( PHP_SAPI === 'cli' ) {
00079             $this->lockTTL = 2 * 3600;
00080         } else {
00081             $met = ini_get( 'max_execution_time' ); // this is 0 in CLI mode
00082             $this->lockTTL = max( 5 * 60, 2 * (int)$met );
00083         }
00084     }
00085 
00094     final public function lock( array $paths, $type = self::LOCK_EX, $timeout = 0 ) {
00095         return $this->lockByType( array( $type => $paths ), $timeout );
00096     }
00097 
00106     final public function lockByType( array $pathsByType, $timeout = 0 ) {
00107         wfProfileIn( __METHOD__ );
00108         $status = Status::newGood();
00109         $pathsByType = $this->normalizePathsByType( $pathsByType );
00110         $msleep = array( 0, 50, 100, 300, 500 ); // retry backoff times
00111         $start = microtime( true );
00112         do {
00113             $status = $this->doLockByType( $pathsByType );
00114             $elapsed = microtime( true ) - $start;
00115             if ( $status->isOK() || $elapsed >= $timeout || $elapsed < 0 ) {
00116                 break; // success, timeout, or clock set back
00117             }
00118             usleep( 1e3 * ( next( $msleep ) ?: 1000 ) ); // use 1 sec after enough times
00119             $elapsed = microtime( true ) - $start;
00120         } while ( $elapsed < $timeout && $elapsed >= 0 );
00121         wfProfileOut( __METHOD__ );
00122         return $status;
00123     }
00124 
00132     final public function unlock( array $paths, $type = self::LOCK_EX ) {
00133         return $this->unlockByType( array( $type => $paths ) );
00134     }
00135 
00143     final public function unlockByType( array $pathsByType ) {
00144         wfProfileIn( __METHOD__ );
00145         $pathsByType = $this->normalizePathsByType( $pathsByType );
00146         $status = $this->doUnlockByType( $pathsByType );
00147         wfProfileOut( __METHOD__ );
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         return $res;
00189     }
00190 
00197     protected function doLockByType( array $pathsByType ) {
00198         $status = Status::newGood();
00199         $lockedByType = array(); // map of (type => paths)
00200         foreach ( $pathsByType as $type => $paths ) {
00201             $status->merge( $this->doLock( $paths, $type ) );
00202             if ( $status->isOK() ) {
00203                 $lockedByType[$type] = $paths;
00204             } else {
00205                 // Release the subset of locks that were acquired
00206                 foreach ( $lockedByType as $type => $paths ) {
00207                     $status->merge( $this->doUnlock( $paths, $type ) );
00208                 }
00209                 break;
00210             }
00211         }
00212         return $status;
00213     }
00214 
00222     abstract protected function doLock( array $paths, $type );
00223 
00230     protected function doUnlockByType( array $pathsByType ) {
00231         $status = Status::newGood();
00232         foreach ( $pathsByType as $type => $paths ) {
00233             $status->merge( $this->doUnlock( $paths, $type ) );
00234         }
00235         return $status;
00236     }
00237 
00245     abstract protected function doUnlock( array $paths, $type );
00246 }
00247 
00252 class NullLockManager extends LockManager {
00253     protected function doLock( array $paths, $type ) {
00254         return Status::newGood();
00255     }
00256 
00257     protected function doUnlock( array $paths, $type ) {
00258         return Status::newGood();
00259     }
00260 }