MediaWiki REL1_34
PoolCounter.php
Go to the documentation of this file.
1<?php
45abstract class PoolCounter {
46 /* Return codes */
47 const LOCKED = 1; /* Lock acquired */
48 const RELEASED = 2; /* Lock released */
49 const DONE = 3; /* Another worker did the work for you */
50
51 const ERROR = -1; /* Indeterminate error */
52 const NOT_LOCKED = -2; /* Called release() with no lock held */
53 const QUEUE_FULL = -3; /* There are already maxqueue workers on this lock */
54 const TIMEOUT = -4; /* Timeout exceeded */
55 const LOCK_HELD = -5; /* Cannot acquire another lock while you have one lock held */
56
58 protected $key;
60 protected $workers;
67 protected $slots = 0;
69 protected $maxqueue;
71 protected $timeout;
72
80 private static $acquiredMightWaitKey = 0;
81
87 protected function __construct( $conf, $type, $key ) {
88 $this->workers = $conf['workers'];
89 $this->maxqueue = $conf['maxqueue'];
90 $this->timeout = $conf['timeout'];
91 if ( isset( $conf['slots'] ) ) {
92 $this->slots = $conf['slots'];
93 }
94
95 if ( $this->slots ) {
96 $key = $this->hashKeyIntoSlots( $type, $key, $this->slots );
97 }
98
99 $this->key = $key;
100 $this->isMightWaitKey = !preg_match( '/^nowait:/', $this->key );
101 }
102
111 public static function factory( $type, $key ) {
112 global $wgPoolCounterConf;
113 if ( !isset( $wgPoolCounterConf[$type] ) ) {
114 return new PoolCounterNull;
115 }
116 $conf = $wgPoolCounterConf[$type];
117 $class = $conf['class'];
118
119 return new $class( $conf, $type, $key );
120 }
121
125 public function getKey() {
126 return $this->key;
127 }
128
134 abstract public function acquireForMe();
135
142 abstract public function acquireForAnyone();
143
151 abstract public function release();
152
158 final protected function precheckAcquire() {
159 if ( $this->isMightWaitKey ) {
160 if ( self::$acquiredMightWaitKey ) {
161 /*
162 * The poolcounter itself is quite happy to allow you to wait
163 * on another lock while you have a lock you waited on already
164 * but we think that it is unlikely to be a good idea. So we
165 * made it an error. If you are _really_ _really_ sure it is a
166 * good idea then feel free to implement an unsafe flag or
167 * something.
168 */
169 return Status::newFatal( 'poolcounter-usage-error',
170 'You may only aquire a single non-nowait lock.' );
171 }
172 } elseif ( $this->timeout !== 0 ) {
173 return Status::newFatal( 'poolcounter-usage-error',
174 'Locks starting in nowait: must have 0 timeout.' );
175 }
176 return Status::newGood();
177 }
178
183 final protected function onAcquire() {
184 self::$acquiredMightWaitKey |= $this->isMightWaitKey;
185 }
186
191 final protected function onRelease() {
192 self::$acquiredMightWaitKey &= !$this->isMightWaitKey;
193 }
194
207 protected function hashKeyIntoSlots( $type, $key, $slots ) {
208 return $type . ':' . ( hexdec( substr( sha1( $key ), 0, 4 ) ) % $slots );
209 }
210}
$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.
static factory( $type, $key)
Create a Pool counter.
__construct( $conf, $type, $key)
bool $isMightWaitKey
Whether the key is a "might wait" key.
float $timeout
Maximum time in seconds to wait for the lock.
onAcquire()
Update any lock tracking information when the lock is acquired.
int $maxqueue
If this number of workers are already working/waiting, fail instead of wait.
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.
acquireForMe()
I want to do this task and I need to do it myself.
const NOT_LOCKED
acquireForAnyone()
I want to do this task, but if anyone else does it instead, it's also fine for me.
const LOCK_HELD
onRelease()
Update any lock tracking information when the lock is released.