1
0
mirror of https://github.com/danog/file.git synced 2024-11-26 11:54:54 +01:00

Use ClosedException as mandated by the ByteStream interfaces

This commit also changes end() to wait for the close() to finish before returning.
This commit is contained in:
Niklas Keller 2017-06-21 11:01:19 +02:00
parent 40fd1383e7
commit a13bb6f721
6 changed files with 88 additions and 35 deletions

View File

@ -2,9 +2,11 @@
namespace Amp\File;
use Amp\ByteStream\ClosedException;
use Amp\Failure;
use Amp\Promise;
use Amp\Success;
use function Amp\call;
class BlockingHandle implements Handle {
private $fh;
@ -33,7 +35,7 @@ class BlockingHandle implements Handle {
*/
public function read(int $length = self::DEFAULT_READ_LENGTH): Promise {
if ($this->fh === null) {
throw new FilesystemException("The file has been closed");
throw new ClosedException("The file has been closed");
}
$data = \fread($this->fh, $length);
@ -52,7 +54,7 @@ class BlockingHandle implements Handle {
*/
public function write(string $data): Promise {
if ($this->fh === null) {
throw new FilesystemException("The file has been closed");
throw new ClosedException("The file has been closed");
}
$len = \fwrite($this->fh, $data);
@ -70,9 +72,14 @@ class BlockingHandle implements Handle {
* {@inheritdoc}
*/
public function end(string $data = ""): Promise {
$promise = $this->write($data);
$promise->onResolve([$this, "close"]);
return $promise;
return call(function () use ($data) {
$promise = $this->write($data);
// ignore any errors
yield Promise\any([$this->close()]);
return $promise;
});
}
/**

View File

@ -2,6 +2,7 @@
namespace Amp\File;
use Amp\ByteStream\ClosedException;
use Amp\Deferred;
use Amp\Promise;
use Amp\Success;
@ -61,7 +62,7 @@ class EioHandle implements Handle {
$this->isActive = false;
if ($result === -1) {
$deferred->fail(new FilesystemException(
$deferred->fail(new ClosedException(
sprintf('Reading from file failed: %s.', \eio_get_last_error($req))
));
} else {
@ -79,7 +80,7 @@ class EioHandle implements Handle {
}
if (!$this->writable) {
throw new \Error("The file is no longer writable");
throw new ClosedException("The file is no longer writable");
}
$this->isActive = true;
@ -122,15 +123,20 @@ class EioHandle implements Handle {
* {@inheritdoc}
*/
public function end(string $data = ""): Promise {
$promise = $this->write($data);
$this->writable = false;
$promise->onResolve([$this, "close"]);
return $promise;
return call(function () use ($data) {
$promise = $this->write($data);
$this->writable = false;
// ignore any errors
yield Promise\any([$this->close()]);
return $promise;
});
}
private function onWrite(Deferred $deferred, $result, $req) {
if ($this->queue->isEmpty()) {
$deferred->fail(new FilesystemException('No pending write, the file may have been closed'));
$deferred->fail(new ClosedException('No pending write, the file may have been closed'));
}
$this->queue->shift();
@ -139,7 +145,7 @@ class EioHandle implements Handle {
}
if ($result === -1) {
$deferred->fail(new FilesystemException(
$deferred->fail(new ClosedException(
sprintf('Writing to the file failed: %s', \eio_get_last_error($req))
));
} else {

View File

@ -2,7 +2,9 @@
namespace Amp\File;
class FilesystemException extends \Exception {
use Amp\ByteStream\StreamException;
class FilesystemException extends StreamException {
public function __construct(string $message, \Throwable $previous = null) {
parent::__construct($message, 0, $previous);
}

View File

@ -2,12 +2,14 @@
namespace Amp\File;
use Amp\ByteStream\ClosedException;
use Amp\Coroutine;
use Amp\Parallel\Worker\TaskException;
use Amp\Parallel\Worker\Worker;
use Amp\Parallel\Worker\WorkerException;
use Amp\Promise;
use Amp\Success;
use function Amp\call;
class ParallelHandle implements Handle {
/** @var \Amp\Parallel\Worker\Worker */
@ -90,7 +92,7 @@ class ParallelHandle implements Handle {
public function read(int $length = self::DEFAULT_READ_LENGTH): Promise {
if ($this->id === null) {
throw new FilesystemException("The file has been closed");
throw new ClosedException("The file has been closed");
}
if ($this->busy) {
@ -121,7 +123,7 @@ class ParallelHandle implements Handle {
*/
public function write(string $data): Promise {
if ($this->id === null) {
throw new FilesystemException("The file has been closed");
throw new ClosedException("The file has been closed");
}
if ($this->busy && $this->pendingWrites === 0) {
@ -129,7 +131,7 @@ class ParallelHandle implements Handle {
}
if (!$this->writable) {
throw new \Error("The file is no longer writable");
throw new ClosedException("The file is no longer writable");
}
return new Coroutine($this->doWrite($data));
@ -139,10 +141,15 @@ class ParallelHandle implements Handle {
* {@inheritdoc}
*/
public function end(string $data = ""): Promise {
$promise = $this->write($data);
$this->writable = false;
$promise->onResolve([$this, "close"]);
return $promise;
return call(function () use ($data) {
$promise = $this->write($data);
$this->writable = false;
// ignore any errors
yield Promise\any([$this->close()]);
return $promise;
});
}
private function doWrite(string $data): \Generator {
@ -170,7 +177,7 @@ class ParallelHandle implements Handle {
*/
public function seek(int $offset, int $whence = SEEK_SET): Promise {
if ($this->id === null) {
throw new FilesystemException("The file has been closed");
throw new ClosedException("The file has been closed");
}
if ($this->busy) {

View File

@ -2,6 +2,7 @@
namespace Amp\File;
use Amp\ByteStream\ClosedException;
use Amp\Deferred;
use Amp\File\Internal\UvPoll;
use Amp\Loop;
@ -57,7 +58,7 @@ class UvHandle implements Handle {
$this->isActive = false;
if ($result < 0) {
$deferred->fail(new FilesystemException(\uv_strerror($result)));
$deferred->fail(new ClosedException(\uv_strerror($result)));
} else {
$length = strlen($buffer);
$this->position = $this->position + $length;
@ -79,7 +80,7 @@ class UvHandle implements Handle {
}
if (!$this->writable) {
throw new \Error("The file is no longer writable");
throw new ClosedException("The file is no longer writable");
}
$this->isActive = true;
@ -103,10 +104,15 @@ class UvHandle implements Handle {
* {@inheritdoc}
*/
public function end(string $data = ""): Promise {
$promise = $this->write($data);
$this->writable = false;
$promise->onResolve([$this, "close"]);
return $promise;
return call(function () use ($data) {
$promise = $this->write($data);
$this->writable = false;
// ignore any errors
yield Promise\any([$this->close()]);
return $promise;
});
}
private function push(string $data): Promise {
@ -117,7 +123,7 @@ class UvHandle implements Handle {
$onWrite = function ($fh, $result) use ($deferred, $length) {
if ($this->queue->isEmpty()) {
$deferred->fail(new FilesystemException('No pending write, the file may have been closed'));
$deferred->fail(new ClosedException('No pending write, the file may have been closed'));
}
$this->queue->shift();
@ -126,9 +132,7 @@ class UvHandle implements Handle {
}
if ($result < 0) {
$deferred->fail(new FilesystemException(
\uv_strerror($result)
));
$deferred->fail(new ClosedException(\uv_strerror($result)));
} else {
StatCache::clear($this->path);
$newPosition = $this->position + $length;

View File

@ -2,6 +2,7 @@
namespace Amp\File\Test;
use Amp\ByteStream\ClosedException;
use Amp\File;
use Amp\PHPUnit\TestCase;
@ -36,6 +37,33 @@ abstract class HandleTest extends TestCase {
});
}
public function testWriteAfterClose() {
$this->execute(function () {
$path = Fixture::path() . "/write";
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "c+");
$this->assertSame(0, $handle->tell());
yield $handle->write("foo");
yield $handle->close();
$this->expectException(ClosedException::class);
yield $handle->write("bar");
});
}
public function testWriteAfterEnd() {
$this->execute(function () {
$path = Fixture::path() . "/write";
/** @var \Amp\File\Handle $handle */
$handle = yield File\open($path, "c+");
$this->assertSame(0, $handle->tell());
yield $handle->end("foo");
$this->expectException(ClosedException::class);
yield $handle->write("bar");
});
}
public function testReadingToEof() {
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
@ -150,14 +178,13 @@ abstract class HandleTest extends TestCase {
});
}
/**
* @expectedException \Amp\File\FilesystemException
*/
public function testClose() {
$this->execute(function () {
/** @var \Amp\File\Handle $handle */
$handle = yield File\open(__FILE__, "r");
yield $handle->close();
$this->expectException(ClosedException::class);
yield $handle->read();
});
}