MediaWiki  master
PoolCounter.php
Go to the documentation of this file.
1 <?php
26 use Wikimedia\ObjectFactory\ObjectFactory;
27 
49 abstract class PoolCounter {
50  /* Return codes */
51  public const LOCKED = 1; /* Lock acquired */
52  public const RELEASED = 2; /* Lock released */
53  public const DONE = 3; /* Another worker did the work for you */
54 
55  public const ERROR = -1; /* Indeterminate error */
56  public const NOT_LOCKED = -2; /* Called release() with no lock held */
57  public const QUEUE_FULL = -3; /* There are already maxqueue workers on this lock */
58  public const TIMEOUT = -4; /* Timeout exceeded */
59  public const LOCK_HELD = -5; /* Cannot acquire another lock while you have one lock held */
60 
62  protected $key;
64  protected $workers;
71  protected $slots = 0;
73  protected $maxqueue;
75  protected $timeout;
76 
80  private $isMightWaitKey;
84  private static $acquiredMightWaitKey = 0;
85 
89  private $fastStale;
90 
96  protected function __construct( array $conf, string $type, string $key ) {
97  $this->workers = $conf['workers'];
98  $this->maxqueue = $conf['maxqueue'];
99  $this->timeout = $conf['timeout'];
100  if ( isset( $conf['slots'] ) ) {
101  $this->slots = $conf['slots'];
102  }
103  $this->fastStale = $conf['fastStale'] ?? false;
104 
105  if ( $this->slots ) {
106  $key = $this->hashKeyIntoSlots( $type, $key, $this->slots );
107  }
108 
109  $this->key = $key;
110  $this->isMightWaitKey = !preg_match( '/^nowait:/', $this->key );
111  }
112 
121  public static function factory( string $type, string $key ) {
122  $poolCounterConf = MediaWikiServices::getInstance()->getMainConfig()
123  ->get( MainConfigNames::PoolCounterConf );
124  if ( !isset( $poolCounterConf[$type] ) ) {
125  return new PoolCounterNull;
126  }
127  $conf = $poolCounterConf[$type];
128 
130  $poolCounter = ObjectFactory::getObjectFromSpec(
131  $conf,
132  [
133  'extraArgs' => [ $conf, $type, $key ],
134  'assertClass' => self::class
135  ]
136  );
137 
138  return $poolCounter;
139  }
140 
144  public function getKey() {
145  return $this->key;
146  }
147 
155  abstract public function acquireForMe( $timeout = null );
156 
165  abstract public function acquireForAnyone( $timeout = null );
166 
174  abstract public function release();
175 
181  final protected function precheckAcquire() {
182  if ( $this->isMightWaitKey ) {
183  if ( self::$acquiredMightWaitKey ) {
184  /*
185  * The poolcounter itself is quite happy to allow you to wait
186  * on another lock while you have a lock you waited on already
187  * but we think that it is unlikely to be a good idea. So we
188  * made it an error. If you are _really_ _really_ sure it is a
189  * good idea then feel free to implement an unsafe flag or
190  * something.
191  */
192  return Status::newFatal( 'poolcounter-usage-error',
193  'You may only aquire a single non-nowait lock.' );
194  }
195  } elseif ( $this->timeout !== 0 ) {
196  return Status::newFatal( 'poolcounter-usage-error',
197  'Locks starting in nowait: must have 0 timeout.' );
198  }
199  return Status::newGood();
200  }
201 
206  final protected function onAcquire() {
207  self::$acquiredMightWaitKey |= $this->isMightWaitKey;
208  }
209 
214  final protected function onRelease() {
215  self::$acquiredMightWaitKey &= !$this->isMightWaitKey;
216  }
217 
230  protected function hashKeyIntoSlots( $type, $key, $slots ) {
231  return $type . ':' . ( hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots );
232  }
233 
240  public function isFastStaleEnabled() {
241  return $this->fastStale;
242  }
243 }
A class containing constants representing the names of configuration variables.
MediaWikiServices is the service locator for the application scope of MediaWiki.
A default PoolCounter, which provides no locking.
When you have many workers (threads/servers) giving service, and a cached item expensive to produce e...
Definition: PoolCounter.php:49
const QUEUE_FULL
Definition: PoolCounter.php:57
int $workers
Maximum number of workers working on tasks with the same key simultaneously.
Definition: PoolCounter.php:64
acquireForAnyone( $timeout=null)
I want to do this task, but if anyone else does it instead, it's also fine for me.
bool $isMightWaitKey
Whether the key is a "might wait" key.
Definition: PoolCounter.php:80
const LOCKED
Definition: PoolCounter.php:51
onAcquire()
Update any lock tracking information when the lock is acquired.
const TIMEOUT
Definition: PoolCounter.php:58
__construct(array $conf, string $type, string $key)
Definition: PoolCounter.php:96
bool $fastStale
Enable fast stale mode (T250248).
Definition: PoolCounter.php:89
int $maxqueue
If this number of workers are already working/waiting, fail instead of wait.
Definition: PoolCounter.php:73
static int $acquiredMightWaitKey
Whether this process holds a "might wait" lock key.
Definition: PoolCounter.php:84
isFastStaleEnabled()
Is fast stale mode (T250248) enabled? This may be overridden by the PoolCounterWork subclass.
string $key
All workers with the same key share the lock.
Definition: PoolCounter.php:62
const RELEASED
Definition: PoolCounter.php:52
int $slots
Maximum number of workers working on this task type, regardless of key.
Definition: PoolCounter.php:71
hashKeyIntoSlots( $type, $key, $slots)
Given a key (any string) and the number of lots, returns a slot key (a prefix with a suffix integer f...
release()
I have successfully finished my task.
precheckAcquire()
Checks that the lock request is sensible.
static factory(string $type, string $key)
Create a Pool counter.
acquireForMe( $timeout=null)
I want to do this task and I need to do it myself.
const NOT_LOCKED
Definition: PoolCounter.php:56
int $timeout
Maximum time in seconds to wait for the lock.
Definition: PoolCounter.php:75
const LOCK_HELD
Definition: PoolCounter.php:59
onRelease()
Update any lock tracking information when the lock is released.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82