MediaWiki
REL1_22
|
00001 <?php 00043 abstract class BagOStuff { 00044 private $debugMode = false; 00045 00049 public function setDebug( $bool ) { 00050 $this->debugMode = $bool; 00051 } 00052 00053 /* *** THE GUTS OF THE OPERATION *** */ 00054 /* Override these with functional things in subclasses */ 00055 00062 abstract public function get( $key, &$casToken = null ); 00063 00071 abstract public function set( $key, $value, $exptime = 0 ); 00072 00081 abstract public function cas( $casToken, $key, $value, $exptime = 0 ); 00082 00089 abstract public function delete( $key, $time = 0 ); 00090 00102 public function merge( $key, closure $callback, $exptime = 0, $attempts = 10 ) { 00103 return $this->mergeViaCas( $key, $callback, $exptime, $attempts ); 00104 } 00105 00115 protected function mergeViaCas( $key, closure $callback, $exptime = 0, $attempts = 10 ) { 00116 do { 00117 $casToken = null; // passed by reference 00118 $currentValue = $this->get( $key, $casToken ); // get the old value 00119 $value = $callback( $this, $key, $currentValue ); // derive the new value 00120 00121 if ( $value === false ) { 00122 $success = true; // do nothing 00123 } elseif ( $currentValue === false ) { 00124 // Try to create the key, failing if it gets created in the meantime 00125 $success = $this->add( $key, $value, $exptime ); 00126 } else { 00127 // Try to update the key, failing if it gets changed in the meantime 00128 $success = $this->cas( $casToken, $key, $value, $exptime ); 00129 } 00130 } while ( !$success && --$attempts ); 00131 00132 return $success; 00133 } 00134 00144 protected function mergeViaLock( $key, closure $callback, $exptime = 0, $attempts = 10 ) { 00145 if ( !$this->lock( $key, 60 ) ) { 00146 return false; 00147 } 00148 00149 $currentValue = $this->get( $key ); // get the old value 00150 $value = $callback( $this, $key, $currentValue ); // derive the new value 00151 00152 if ( $value === false ) { 00153 $success = true; // do nothing 00154 } else { 00155 $success = $this->set( $key, $value, $exptime ); // set the new value 00156 } 00157 00158 if ( !$this->unlock( $key ) ) { 00159 // this should never happen 00160 trigger_error( "Could not release lock for key '$key'." ); 00161 } 00162 00163 return $success; 00164 } 00165 00171 public function lock( $key, $timeout = 60 ) { 00172 $timestamp = microtime( true ); // starting UNIX timestamp 00173 if ( $this->add( "{$key}:lock", 1, $timeout ) ) { 00174 return true; 00175 } 00176 00177 $uRTT = ceil( 1e6 * ( microtime( true ) - $timestamp ) ); // estimate RTT (us) 00178 $sleep = 2 * $uRTT; // rough time to do get()+set() 00179 00180 $locked = false; // lock acquired 00181 $attempts = 0; // failed attempts 00182 do { 00183 if ( ++$attempts >= 3 && $sleep <= 1e6 ) { 00184 // Exponentially back off after failed attempts to avoid network spam. 00185 // About 2*$uRTT*(2^n-1) us of "sleep" happen for the next n attempts. 00186 $sleep *= 2; 00187 } 00188 usleep( $sleep ); // back off 00189 $locked = $this->add( "{$key}:lock", 1, $timeout ); 00190 } while ( !$locked ); 00191 00192 return $locked; 00193 } 00194 00199 public function unlock( $key ) { 00200 return $this->delete( "{$key}:lock" ); 00201 } 00202 00212 public function deleteObjectsExpiringBefore( $date, $progressCallback = false ) { 00213 // stub 00214 return false; 00215 } 00216 00217 /* *** Emulated functions *** */ 00218 00224 public function getMulti( array $keys ) { 00225 $res = array(); 00226 foreach ( $keys as $key ) { 00227 $val = $this->get( $key ); 00228 if ( $val !== false ) { 00229 $res[$key] = $val; 00230 } 00231 } 00232 return $res; 00233 } 00234 00241 public function add( $key, $value, $exptime = 0 ) { 00242 if ( $this->get( $key ) === false ) { 00243 return $this->set( $key, $value, $exptime ); 00244 } 00245 return false; // key already set 00246 } 00247 00254 public function replace( $key, $value, $exptime = 0 ) { 00255 if ( $this->get( $key ) !== false ) { 00256 return $this->set( $key, $value, $exptime ); 00257 } 00258 return false; // key not already set 00259 } 00260 00267 public function incr( $key, $value = 1 ) { 00268 if ( !$this->lock( $key ) ) { 00269 return false; 00270 } 00271 $n = $this->get( $key ); 00272 if ( $this->isInteger( $n ) ) { // key exists? 00273 $n += intval( $value ); 00274 $this->set( $key, max( 0, $n ) ); // exptime? 00275 } else { 00276 $n = false; 00277 } 00278 $this->unlock( $key ); 00279 00280 return $n; 00281 } 00282 00289 public function decr( $key, $value = 1 ) { 00290 return $this->incr( $key, - $value ); 00291 } 00292 00296 public function debug( $text ) { 00297 if ( $this->debugMode ) { 00298 $class = get_class( $this ); 00299 wfDebug( "$class debug: $text\n" ); 00300 } 00301 } 00302 00308 protected function convertExpiry( $exptime ) { 00309 if ( ( $exptime != 0 ) && ( $exptime < 86400 * 3650 /* 10 years */ ) ) { 00310 return time() + $exptime; 00311 } else { 00312 return $exptime; 00313 } 00314 } 00315 00323 protected function convertToRelative( $exptime ) { 00324 if ( $exptime >= 86400 * 3650 /* 10 years */ ) { 00325 $exptime -= time(); 00326 if ( $exptime <= 0 ) { 00327 $exptime = 1; 00328 } 00329 return $exptime; 00330 } else { 00331 return $exptime; 00332 } 00333 } 00334 00341 protected function isInteger( $value ) { 00342 return ( is_int( $value ) || ctype_digit( $value ) ); 00343 } 00344 }