47 parent::__construct( $config );
49 $this->lockDir = $config[
'lockDirectory'];
50 $this->isWindows = ( PHP_OS_FAMILY ===
'Windows' );
59 protected function doLock( array $paths, $type ) {
60 $status = StatusValue::newGood();
63 foreach ( $paths as
$path ) {
65 if ( $status->isOK() ) {
66 $lockedPaths[] =
$path;
69 $status->merge( $this->
doUnlock( $lockedPaths, $type ) );
84 protected function doUnlock( array $paths, $type ) {
85 $status = StatusValue::newGood();
87 foreach ( $paths as
$path ) {
102 $status = StatusValue::newGood();
104 if ( isset( $this->locksHeld[
$path][$type] ) ) {
105 ++$this->locksHeld[
$path][$type];
106 } elseif ( isset( $this->locksHeld[
$path][self::LOCK_EX] ) ) {
107 $this->locksHeld[
$path][$type] = 1;
109 if ( isset( $this->handles[
$path] ) ) {
110 $handle = $this->handles[
$path];
113 $handle = @fopen( $this->
getLockPath( $path ),
'a+' );
114 if ( !$handle && !is_dir( $this->lockDir ) ) {
117 if ( @mkdir( $this->lockDir, 0o777,
true ) ) {
119 $handle = @fopen( $this->
getLockPath( $path ),
'a+' );
121 $this->logger->error(
"Cannot create directory '{$this->lockDir}'." );
128 if ( flock( $handle, $lock | LOCK_NB ) ) {
130 $this->locksHeld[
$path][$type] = 1;
131 $this->handles[
$path] = $handle;
134 $status->fatal(
'lockmanager-fail-conflict' );
137 $status->fatal(
'lockmanager-fail-openlock',
$path );
152 $status = StatusValue::newGood();
154 if ( !isset( $this->locksHeld[
$path] ) ) {
155 $status->warning(
'lockmanager-notlocked',
$path );
156 } elseif ( !isset( $this->locksHeld[
$path][$type] ) ) {
157 $status->warning(
'lockmanager-notlocked',
$path );
159 $handlesToClose = [];
160 --$this->locksHeld[
$path][$type];
161 if ( $this->locksHeld[
$path][$type] <= 0 ) {
162 unset( $this->locksHeld[
$path][$type] );
164 if ( $this->locksHeld[
$path] === [] ) {
165 unset( $this->locksHeld[
$path] );
166 if ( isset( $this->handles[
$path] ) ) {
167 $handlesToClose[] = $this->handles[
$path];
168 unset( $this->handles[
$path] );
173 if ( $this->isWindows ) {
176 $status->merge( $this->closeLockHandles(
$path, $handlesToClose ) );
177 $status->merge( $this->pruneKeyLockFiles(
$path ) );
181 $status->merge( $this->pruneKeyLockFiles(
$path ) );
182 $status->merge( $this->closeLockHandles(
$path, $handlesToClose ) );
194 private function closeLockHandles(
$path, array $handlesToClose ) {
195 $status = StatusValue::newGood();
196 foreach ( $handlesToClose as $handle ) {
197 if ( !flock( $handle, LOCK_UN ) ) {
198 $status->fatal(
'lockmanager-fail-releaselock',
$path );
200 if ( !fclose( $handle ) ) {
201 $status->warning(
'lockmanager-fail-closelock',
$path );
212 private function pruneKeyLockFiles(
$path ) {
214 if ( !isset( $this->locksHeld[
$path] ) ) {
215 # No locks are held for the lock file anymore
217 $status->warning(
'lockmanager-fail-deletelock',
$path );
219 unset( $this->handles[
$path] );
231 return "{$this->lockDir}/{$this->sha1Base36Absolute( $path )}.lock";
238 while ( count( $this->locksHeld ) ) {
239 foreach ( $this->locksHeld as
$path => $locks ) {
247class_alias( FSLockManager::class,
'FSLockManager' );
Generic operation result class Has warning/error list, boolean status and arbitrary value.
static newGood( $value=null)
Factory function for good results.