MediaWiki REL1_37
PoolCounter.php
Go to the documentation of this file.
1<?php
24use Wikimedia\ObjectFactory;
25
47abstract class PoolCounter {
48 /* Return codes */
49 public const LOCKED = 1; /* Lock acquired */
50 public const RELEASED = 2; /* Lock released */
51 public const DONE = 3; /* Another worker did the work for you */
52
53 public const ERROR = -1; /* Indeterminate error */
54 public const NOT_LOCKED = -2; /* Called release() with no lock held */
55 public const QUEUE_FULL = -3; /* There are already maxqueue workers on this lock */
56 public const TIMEOUT = -4; /* Timeout exceeded */
57 public const LOCK_HELD = -5; /* Cannot acquire another lock while you have one lock held */
58
60 protected $key;
62 protected $workers;
69 protected $slots = 0;
71 protected $maxqueue;
73 protected $timeout;
74
82 private static $acquiredMightWaitKey = 0;
83
87 private $fastStale;
88
94 protected function __construct( array $conf, string $type, string $key ) {
95 $this->workers = $conf['workers'];
96 $this->maxqueue = $conf['maxqueue'];
97 $this->timeout = $conf['timeout'];
98 if ( isset( $conf['slots'] ) ) {
99 $this->slots = $conf['slots'];
100 }
101 $this->fastStale = $conf['fastStale'] ?? false;
102
103 if ( $this->slots ) {
104 $key = $this->hashKeyIntoSlots( $type, $key, $this->slots );
105 }
106
107 $this->key = $key;
108 $this->isMightWaitKey = !preg_match( '/^nowait:/', $this->key );
109 }
110
119 public static function factory( string $type, string $key ) {
120 global $wgPoolCounterConf;
121 if ( !isset( $wgPoolCounterConf[$type] ) ) {
122 return new PoolCounterNull;
123 }
124 $conf = $wgPoolCounterConf[$type];
125
127 $poolCounter = ObjectFactory::getObjectFromSpec(
128 $conf,
129 [
130 'extraArgs' => [ $conf, $type, $key ],
131 'assertClass' => self::class
132 ]
133 );
134
135 return $poolCounter;
136 }
137
141 public function getKey() {
142 return $this->key;
143 }
144
152 abstract public function acquireForMe( $timeout = null );
153
162 abstract public function acquireForAnyone( $timeout = null );
163
171 abstract public function release();
172
178 final protected function precheckAcquire() {
179 if ( $this->isMightWaitKey ) {
180 if ( self::$acquiredMightWaitKey ) {
181 /*
182 * The poolcounter itself is quite happy to allow you to wait
183 * on another lock while you have a lock you waited on already
184 * but we think that it is unlikely to be a good idea. So we
185 * made it an error. If you are _really_ _really_ sure it is a
186 * good idea then feel free to implement an unsafe flag or
187 * something.
188 */
189 return Status::newFatal( 'poolcounter-usage-error',
190 'You may only aquire a single non-nowait lock.' );
191 }
192 } elseif ( $this->timeout !== 0 ) {
193 return Status::newFatal( 'poolcounter-usage-error',
194 'Locks starting in nowait: must have 0 timeout.' );
195 }
196 return Status::newGood();
197 }
198
203 final protected function onAcquire() {
204 self::$acquiredMightWaitKey |= $this->isMightWaitKey;
205 }
206
211 final protected function onRelease() {
212 self::$acquiredMightWaitKey &= !$this->isMightWaitKey;
213 }
214
227 protected function hashKeyIntoSlots( $type, $key, $slots ) {
228 return $type . ':' . ( hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots );
229 }
230
237 public function isFastStaleEnabled() {
238 return $this->fastStale;
239 }
240}
$wgPoolCounterConf
Configuration for processing pool control, for use in high-traffic wikis.
A default PoolCounter, which provides no locking.
When you have many workers (threads/servers) giving service, and a cached item expensive to produce e...
const QUEUE_FULL
int $workers
Maximum number of workers working on tasks with the same key simultaneously.
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.
onAcquire()
Update any lock tracking information when the lock is acquired.
__construct(array $conf, string $type, string $key)
bool $fastStale
Enable fast stale mode (T250248).
int $maxqueue
If this number of workers are already working/waiting, fail instead of wait.
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.
int $slots
Maximum number of workers working on this task type, regardless of key.
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 sane.
static bool $acquiredMightWaitKey
Whether this process holds a "might wait" lock key.
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
int $timeout
Maximum time in seconds to wait for the lock.
const LOCK_HELD
onRelease()
Update any lock tracking information when the lock is released.