From 289a723d5e6abdfa1d26a2c00ac796ed904292bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A1chym=20Tou=C5=A1ek?= Date: Sat, 2 May 2020 11:14:21 +0200 Subject: [PATCH] Add AsyncFileMutex (#43) --- .travis.yml | 4 +- composer.json | 11 ++++- src/Sync/AsyncFileMutex.php | 84 ++++++++++++++++++++++++++++++++ src/UvDriver.php | 2 +- test/Sync/AsyncFileMutexTest.php | 15 ++++++ 5 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 src/Sync/AsyncFileMutex.php create mode 100644 test/Sync/AsyncFileMutexTest.php diff --git a/.travis.yml b/.travis.yml index 0f6f1fc..cee1596 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,9 +31,9 @@ before_install: install: - if [ "$TRAVIS_PHP_VERSION" = "nightly" ]; then - composer update -n --prefer-dist --ignore-platform-reqs; + composer update -n --ignore-platform-reqs; else - composer update -n --prefer-dist; + composer update -n; fi - composer show diff --git a/composer.json b/composer.json index 53d3dbe..40eb040 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,8 @@ "php": ">=7.1", "amphp/amp": "^2.2", "amphp/byte-stream": "^1.6.1", - "amphp/parallel": "^1.2" + "amphp/parallel": "^1.2", + "amphp/sync": "^1.3" }, "require-dev": { "ext-eio": "^2", @@ -52,7 +53,13 @@ }, "autoload-dev": { "psr-4": { - "Amp\\File\\Test\\": "test" + "Amp\\File\\Test\\": "test", + "Amp\\Sync\\Test\\": "vendor/amphp/sync/test" + } + }, + "config": { + "preferred-install": { + "amphp/sync": "source" } }, "extra": { diff --git a/src/Sync/AsyncFileMutex.php b/src/Sync/AsyncFileMutex.php new file mode 100644 index 0000000..7d81234 --- /dev/null +++ b/src/Sync/AsyncFileMutex.php @@ -0,0 +1,84 @@ +fileName = $fileName; + } + + /** + * {@inheritdoc} + */ + public function acquire(): Promise + { + 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. + while (true) { + try { + $file = yield open($this->fileName, 'x'); + + break; + } catch (FilesystemException $exception) { + yield new Delayed(self::LATENCY_TIMEOUT); + } + } + + // Return a lock object that can be used to release the lock on the mutex. + $lock = new Lock(0, function (): void { + $this->release(); + }); + + yield $file->close(); + + return $lock; + } + + /** + * Releases the lock on the mutex. + */ + private function release(): void + { + unlink($this->fileName)->onResolve( + function (?\Throwable $exception): void { + if ($exception !== null) { + throw new SyncException( + 'Failed to unlock the mutex file: ' . $this->fileName, + 0, + $exception + ); + } + } + ); + } +} diff --git a/src/UvDriver.php b/src/UvDriver.php index 887898e..1fccfbe 100644 --- a/src/UvDriver.php +++ b/src/UvDriver.php @@ -57,7 +57,7 @@ final class UvDriver implements Driver $openArr = [$mode, $path, $deferred]; \uv_fs_open($this->loop, $path, $flags, $chmod, function ($fh) use ($openArr): void { - if ($fh) { + if (\is_resource($fh)) { $this->onOpenHandle($fh, $openArr); } else { [, $path, $deferred] = $openArr; diff --git a/test/Sync/AsyncFileMutexTest.php b/test/Sync/AsyncFileMutexTest.php new file mode 100644 index 0000000..f41f135 --- /dev/null +++ b/test/Sync/AsyncFileMutexTest.php @@ -0,0 +1,15 @@ +