Skip to content
This repository has been archived by the owner on Oct 1, 2023. It is now read-only.

Commit

Permalink
Add CloseableHandle::closeWhenDisposed() (#159)
Browse files Browse the repository at this point in the history
Summary:
If not used and `close()` is not called, handles will be closed at some
undefined later time, e.g. end of request or when garabage collected.

No current implementation is refcount-based.

Pull Request resolved: #159

Reviewed By: DavidSnider

Differential Revision: D22418834

Pulled By: fredemmott

fbshipit-source-id: 0bcee26d7d3357cafefd796944cf0172448361b6
  • Loading branch information
fredemmott authored and facebook-github-bot committed Jul 7, 2020
1 parent f131390 commit 35a7464
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 3 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@
"hhvm/hsl": "^4.15"
},
"provide": {
"hhvm/hsl-io": "0.2.0"
"hhvm/hsl-io": "0.2.1"
}
}
7 changes: 7 additions & 0 deletions src/io/CloseableHandle.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@
interface CloseableHandle extends Handle {
/** Close the handle */
public function close(): void;

/** Close the handle when the returned disposable is disposed.
*
* Usage: `using $handle->closeWhenDisposed();`
*/
<<__ReturnDisposable>>
public function closeWhenDisposed(): \IDisposable;
}
7 changes: 6 additions & 1 deletion src/io/MemoryHandle.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace HH\Lib\IO;

use namespace HH\Lib\{Math, OS, Str};
use namespace HH\Lib\_Private\_OS;
use namespace HH\Lib\_Private\{_IO, _OS};

enum MemoryHandleWriteMode: int {
OVERWRITE = 0;
Expand Down Expand Up @@ -42,6 +42,11 @@ public function close(): void {
$this->offset = -1;
}

<<__ReturnDisposable>>
public function closeWhenDisposed(): \IDisposable {
return new _IO\CloseWhenDisposed($this);
}

public async function readAsync(
?int $max_bytes = null,
?int $_timeout_nanos = null,
Expand Down
24 changes: 24 additions & 0 deletions src/io/_Private/CloseWhenDisposed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?hh
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

namespace HH\Lib\_Private\_IO;

use namespace HH\Lib\IO;

final class CloseWhenDisposed implements \IDisposable {
public function __construct(
private IO\CloseableHandle $handle,
) {
}

public function __dispose(): void {
$this->handle->close();
}
}
7 changes: 6 additions & 1 deletion src/io/_Private/FileDescriptorHandle.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
namespace HH\Lib\_Private\_IO;

use namespace HH\Lib\{IO, OS, Str};
use namespace HH\Lib\_Private\_OS;
use namespace HH\Lib\_Private\{_IO, _OS};

abstract class FileDescriptorHandle implements IO\CloseableHandle, IO\FDHandle {
protected bool $isAwaitable = true;
Expand Down Expand Up @@ -52,4 +52,9 @@ final public function getFileDescriptor(): OS\FileDescriptor {
final public function close(): void {
OS\close($this->impl);
}

<<__ReturnDisposable>>
final public function closeWhenDisposed(): \IDisposable {
return new _IO\CloseWhenDisposed($this);
}
}
9 changes: 9 additions & 0 deletions tests/io/MemoryHandleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ final class MemoryHandleTest extends HackTest {
expect(await $h->readAllAsync())->toEqual('derp');
}

public function testCloseWhenDisposed(): void {
$h = new IO\MemoryHandle('foobar');
using ($h->closeWhenDisposed()) {
expect($h->read(3))->toEqual('foo');
}
$ex = expect(() ==> $h->read(3))->toThrow(OS\ErrnoException::class);
expect($ex->getErrno())->toEqual(OS\Errno::EBADF);
}

public async function testReadAtInvalidOffset(): Awaitable<void> {
$h = new IO\MemoryHandle('herpderp');
$h->seek(99999);
Expand Down
14 changes: 14 additions & 0 deletions tests/io/PipeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,20 @@ final class PipeTest extends HackTest {
}
}

public async function testCloseWhenDisposed(): Awaitable<void> {
list($r, $w) = IO\pipe();
using ($w->closeWhenDisposed()) {
$w->write("Hello, world\n");
}
expect(await $r->readAsync())->toEqual("Hello, world\n");
// - does not block forever
// - does not fail, just succeeds with no data
expect(await $r->readAsync())->toEqual('');

$ex = expect(() ==> $w->write('foo'))->toThrow(OS\ErrnoException::class);
expect($ex->getErrno())->toEqual(OS\Errno::EBADF);
}

public async function testReadFromClosedPipe(): Awaitable<void> {
// Intent is to:
// - make sure we throw the expected errno
Expand Down

0 comments on commit 35a7464

Please sign in to comment.