24use Wikimedia\AtEase\AtEase;
40 protected $lockTypeMap = [
41 self::LOCK_SH => self::LOCK_SH,
42 self::LOCK_UW => self::LOCK_SH,
43 self::LOCK_EX => self::LOCK_EX
50 protected $handles = [];
62 parent::__construct( $config );
64 $this->lockDir = $config[
'lockDirectory'];
65 $this->isWindows = ( PHP_OS_FAMILY ===
'Windows' );
75 $status = StatusValue::newGood();
78 foreach ( $paths as
$path ) {
80 if ( $status->isOK() ) {
81 $lockedPaths[] =
$path;
100 $status = StatusValue::newGood();
102 foreach ( $paths as
$path ) {
117 $status = StatusValue::newGood();
119 if ( isset( $this->locksHeld[
$path][
$type] ) ) {
121 } elseif ( isset( $this->locksHeld[
$path][self::LOCK_EX] ) ) {
124 if ( isset( $this->handles[
$path] ) ) {
125 $handle = $this->handles[
$path];
127 AtEase::suppressWarnings();
129 if ( !$handle && !is_dir( $this->lockDir ) ) {
131 if ( mkdir( $this->lockDir, 0777,
true ) ) {
134 $this->logger->error(
"Cannot create directory '{$this->lockDir}'." );
137 AtEase::restoreWarnings();
142 if ( flock( $handle, $lock | LOCK_NB ) ) {
145 $this->handles[
$path] = $handle;
148 $status->fatal(
'lockmanager-fail-conflict' );
151 $status->fatal(
'lockmanager-fail-openlock',
$path );
166 $status = StatusValue::newGood();
168 if ( !isset( $this->locksHeld[
$path] ) ) {
169 $status->warning(
'lockmanager-notlocked',
$path );
170 } elseif ( !isset( $this->locksHeld[
$path][
$type] ) ) {
171 $status->warning(
'lockmanager-notlocked',
$path );
173 $handlesToClose = [];
178 if ( $this->locksHeld[
$path] === [] ) {
179 unset( $this->locksHeld[
$path] );
180 if ( isset( $this->handles[
$path] ) ) {
181 $handlesToClose[] = $this->handles[
$path];
182 unset( $this->handles[
$path] );
187 if ( $this->isWindows ) {
190 $status->merge( $this->closeLockHandles(
$path, $handlesToClose ) );
191 $status->merge( $this->pruneKeyLockFiles(
$path ) );
195 $status->merge( $this->pruneKeyLockFiles(
$path ) );
196 $status->merge( $this->closeLockHandles(
$path, $handlesToClose ) );
208 private function closeLockHandles(
$path, array $handlesToClose ) {
209 $status = StatusValue::newGood();
210 foreach ( $handlesToClose as $handle ) {
211 if ( !flock( $handle, LOCK_UN ) ) {
212 $status->fatal(
'lockmanager-fail-releaselock',
$path );
214 if ( !fclose( $handle ) ) {
215 $status->warning(
'lockmanager-fail-closelock',
$path );
226 private function pruneKeyLockFiles(
$path ) {
228 if ( !isset( $this->locksHeld[
$path] ) ) {
229 # No locks are held for the lock file anymore
231 $status->warning(
'lockmanager-fail-deletelock',
$path );
233 unset( $this->handles[
$path] );
245 return "{$this->lockDir}/{$this->sha1Base36Absolute( $path )}.lock";
252 while ( count( $this->locksHeld ) ) {
253 foreach ( $this->locksHeld as
$path => $locks ) {
Simple version of LockManager based on using FS lock files.
doSingleLock( $path, $type)
Lock a single resource key.
string $lockDir
Global dir for all servers.
__destruct()
Make sure remaining locks get cleared.
doLock(array $paths, $type)
array $handles
Map of (locked key => lock file handle)
doSingleUnlock( $path, $type)
Unlock a single resource key.
__construct(array $config)
Construct a new instance from configuration.
getLockPath( $path)
Get the path to the lock file for a key.
array $lockTypeMap
Mapping of lock types to the type actually used.
doUnlock(array $paths, $type)
Class for handling resource locking.
const LOCK_SH
Lock types; stronger locks have higher values.
static newGood( $value=null)
Factory function for good results.