1
0
mirror of https://github.com/danog/parallel.git synced 2024-12-02 17:52:14 +01:00
parallel/lib/Sync/FileMutex.php

79 lines
2.2 KiB
PHP
Raw Normal View History

2016-08-22 06:40:48 +02:00
<?php declare(strict_types = 1);
2015-08-02 04:07:33 +02:00
2016-08-23 23:47:40 +02:00
namespace Amp\Parallel\Sync;
2016-08-18 18:04:48 +02:00
use Amp\{ Coroutine, Pause };
2016-08-23 23:47:40 +02:00
use Amp\Parallel\MutexException;
2016-11-15 00:43:44 +01:00
use Interop\Async\Promise;
2015-08-02 04:07:33 +02:00
/**
* A cross-platform mutex that uses exclusive files as the lock mechanism.
2015-08-02 04:07:33 +02:00
*
* This mutex implementation is not always atomic and depends on the operating
* system's implementation of file creation operations. Use this implementation
2015-08-02 04:07:33 +02:00
* only if no other mutex types are available.
*
2015-10-17 06:32:01 +02:00
* This implementation avoids using [flock()](http://php.net/flock)
* because flock() is known to have some atomicity issues on some systems. In
* addition, flock() does not work as expected when trying to lock a file
* multiple times in the same process on Linux. Instead, exclusive file creation
* is used to create a lock file, which is atomic on most systems.
*
2015-10-17 06:32:01 +02:00
* @see http://php.net/fopen
2015-08-02 04:07:33 +02:00
*/
2016-08-18 18:04:48 +02:00
class FileMutex implements Mutex {
2016-08-22 06:40:48 +02:00
const LATENCY_TIMEOUT = 10;
2016-08-26 17:10:03 +02:00
/** @var string The full path to the lock file. */
2015-08-02 04:07:33 +02:00
private $fileName;
/**
* Creates a new mutex.
*/
2016-08-19 00:36:58 +02:00
public function __construct() {
2016-08-18 18:04:48 +02:00
$this->fileName = \tempnam(\sys_get_temp_dir(), 'mutex-') . '.lock';
2015-08-02 04:07:33 +02:00
}
/**
* {@inheritdoc}
*/
2016-11-15 00:43:44 +01:00
public function acquire(): Promise {
2016-08-18 18:04:48 +02:00
return new Coroutine($this->doAcquire());
}
/**
* @coroutine
*
* @return \Generator
*/
private function doAcquire(): \Generator {
// Try to create the lock file. If the file already exists, someone else
// has the lock, so set an asynchronous timer and try again.
2016-08-18 18:04:48 +02:00
while (($handle = @\fopen($this->fileName, 'x')) === false) {
yield new Pause(self::LATENCY_TIMEOUT);
2015-08-02 04:07:33 +02:00
}
2016-08-18 18:04:48 +02:00
2015-08-08 06:12:21 +02:00
// Return a lock object that can be used to release the lock on the mutex.
2016-08-18 18:04:48 +02:00
$lock = new Lock(function () {
2015-08-08 06:12:21 +02:00
$this->release();
});
2016-08-18 18:04:48 +02:00
\fclose($handle);
2016-01-23 07:00:56 +01:00
return $lock;
2015-08-08 06:12:21 +02:00
}
2015-08-02 04:07:33 +02:00
/**
2015-08-31 00:52:00 +02:00
* Releases the lock on the mutex.
*
* @throws MutexException If the unlock operation failed.
2015-08-02 04:07:33 +02:00
*/
2016-08-18 18:04:48 +02:00
protected function release() {
$success = @\unlink($this->fileName);
2015-08-02 04:07:33 +02:00
if (!$success) {
2015-08-08 06:12:21 +02:00
throw new MutexException('Failed to unlock the mutex file.');
2015-08-02 04:07:33 +02:00
}
}
}